linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Seth Forshee <seth.forshee@canonical.com>
To: fuse-devel@lists.sourceforge.net, linux-fsdevel@vger.kernel.org
Cc: "Miklos Szeredi" <miklos@szeredi.hu>,
	"Eric W. Biederman" <ebiederm@xmission.com>,
	"Michael j Theall" <mtheall@us.ibm.com>,
	"Jean-Pierre André" <jean-pierre.andre@wanadoo.fr>,
	"Nikolaus Rath" <Nikolaus@rath.org>,
	"Seth Forshee" <seth.forshee@canonical.com>
Subject: [RFC v3 2/2] fuse: Add posix acl support
Date: Mon,  1 Aug 2016 16:27:26 -0500	[thread overview]
Message-ID: <1470086846-19844-3-git-send-email-seth.forshee@canonical.com> (raw)
In-Reply-To: <1470086846-19844-1-git-send-email-seth.forshee@canonical.com>

Support posix acls in fuse only when CONFIG_FUSE_FS_POSIX_ACL=y
and default_permissions is used for the filesystem. When
default_permissions is not used fuse cannot meaninfully support
cals, as fuse_permission() only sends FUSE_PERMISSION from the
access, faccessat, chdir, fchdir, and chroot system calls.
Therefore whenever CONFIG_FUSE_FS_POSIX_ACL=n or
default_permissions is not used fuse will return -EOPNOTSUPP
whenever posix acl xattrs are read or written.

XXX: Default acls are currently broken for files created via
atomic_open.

Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
---
 fs/fuse/Kconfig  |  13 ++++
 fs/fuse/Makefile |   2 +-
 fs/fuse/acl.c    | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/fuse/dir.c    |  28 +++++++-
 fs/fuse/fuse_i.h |  29 ++++++++
 fs/fuse/inode.c  |   2 +
 fs/fuse/xattr.c  |  13 ++--
 7 files changed, 279 insertions(+), 8 deletions(-)
 create mode 100644 fs/fuse/acl.c

diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig
index 1b2f6c2c3aaf..5d4ebb0cc0dc 100644
--- a/fs/fuse/Kconfig
+++ b/fs/fuse/Kconfig
@@ -16,6 +16,19 @@ config FUSE_FS
 	  If you want to develop a userspace FS, or if you want to use
 	  a filesystem based on FUSE, answer Y or M.
 
+config FUSE_FS_POSIX_ACL
+        bool "Fuse POSIX Access Control Lists"
+        depends on FUSE_FS
+        select FS_POSIX_ACL
+        help
+          POSIX Access Control Lists (ACLs) support permissions for users and
+          groups beyond the owner/group/world scheme.
+
+          To learn more about Access Control Lists, visit the POSIX ACLs for
+          Linux website <http://acl.bestbits.at/>.
+
+          If you don't know what Access Control Lists are, say N
+
 config CUSE
 	tristate "Character device in Userspace support"
 	depends on FUSE_FS
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index 448aa27ada00..60da84a86dab 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -5,4 +5,4 @@
 obj-$(CONFIG_FUSE_FS) += fuse.o
 obj-$(CONFIG_CUSE) += cuse.o
 
-fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o
+fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o
diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c
new file mode 100644
index 000000000000..c0f412dfe9a7
--- /dev/null
+++ b/fs/fuse/acl.c
@@ -0,0 +1,200 @@
+/*
+  FUSE: Filesystem in Userspace
+  Copyright (C) 2016 Canonical Ltd. <seth.forshee@canonical.com>
+
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
+*/
+
+#include "fuse_i.h"
+
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
+
+#ifdef CONFIG_FUSE_FS_POSIX_ACL
+
+static int fuse_xattr_acl_get(const struct xattr_handler *handler,
+			      struct dentry *dentry, struct inode *inode,
+			      const char *name, void *value, size_t size)
+{
+	struct fuse_conn *fc = get_fuse_conn(inode);
+
+	if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
+		if (handler->flags == ACL_TYPE_ACCESS)
+			return posix_acl_access_xattr_handler.get(
+					&posix_acl_access_xattr_handler,
+					dentry, inode, name, value, size);
+		if (handler->flags == ACL_TYPE_DEFAULT)
+			return posix_acl_default_xattr_handler.get(
+					&posix_acl_default_xattr_handler,
+					dentry, inode, name, value, size);
+	}
+	return -EOPNOTSUPP;
+}
+
+static int fuse_xattr_acl_set(const struct xattr_handler *handler,
+				struct dentry *dentry, struct inode *inode,
+				const char *name, const void *value, size_t size,
+				int flags)
+{
+	struct fuse_conn *fc = get_fuse_conn(inode);
+
+	if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
+		if (handler->flags == ACL_TYPE_ACCESS)
+			return posix_acl_access_xattr_handler.set(
+					&posix_acl_access_xattr_handler,
+					dentry, inode, name, value, size,
+					flags);
+		if (handler->flags == ACL_TYPE_DEFAULT)
+			return posix_acl_default_xattr_handler.set(
+					&posix_acl_default_xattr_handler,
+					dentry, inode, name, value, size,
+					flags);
+	}
+	return -EOPNOTSUPP;
+}
+
+struct posix_acl *fuse_get_acl(struct inode *inode, int type)
+{
+	struct fuse_conn *fc = get_fuse_conn(inode);
+	int size;
+	const char *name;
+	void *value = NULL;
+	struct posix_acl *acl;
+
+	if (fc->no_getxattr)
+		return NULL;
+
+	if (type == ACL_TYPE_ACCESS)
+		name = XATTR_NAME_POSIX_ACL_ACCESS;
+	else if (type == ACL_TYPE_DEFAULT)
+		name = XATTR_NAME_POSIX_ACL_DEFAULT;
+	else
+		return ERR_PTR(-EOPNOTSUPP);
+
+	size = fuse_getxattr(inode, name, NULL, 0);
+	if (size > 0) {
+		value = kzalloc(size, GFP_KERNEL);
+		if (!value)
+			return ERR_PTR(-ENOMEM);
+		size = fuse_getxattr(inode, name, value, size);
+	}
+	if (size > 0) {
+		acl = posix_acl_from_xattr(&init_user_ns, value, size);
+	} else if ((size == 0) || (size == -ENODATA) ||
+		   (size == -EOPNOTSUPP && fc->no_getxattr)) {
+		acl = NULL;
+	} else {
+		acl = ERR_PTR(size);
+	}
+	kfree(value);
+
+	return acl;
+}
+
+int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+{
+	struct fuse_conn *fc = get_fuse_conn(inode);
+	const char *name;
+	int ret;
+
+	if (fc->no_setxattr)
+		return -EOPNOTSUPP;
+
+	if (type == ACL_TYPE_ACCESS) {
+		struct iattr attr;
+		name = XATTR_NAME_POSIX_ACL_ACCESS;
+		attr.ia_mode = inode->i_mode;
+		ret = posix_acl_equiv_mode(acl, &attr.ia_mode);
+		if (ret < 0)
+			return ret;
+		if (ret == 0)
+			acl = NULL;
+		if (inode->i_mode != attr.ia_mode) {
+			attr.ia_valid = ATTR_MODE | ATTR_CTIME;
+			attr.ia_ctime = current_fs_time(inode->i_sb);
+			ret = fuse_do_setattr(inode, &attr, NULL);
+			if (ret)
+				return ret;
+		}
+	} else if (type == ACL_TYPE_DEFAULT) {
+		name = XATTR_NAME_POSIX_ACL_DEFAULT;
+	} else {
+		return -EINVAL;
+	}
+
+	if (acl) {
+		size_t size = posix_acl_xattr_size(acl->a_count);
+		void *value = kmalloc(size, GFP_KERNEL);
+		if (!value)
+			return -ENOMEM;
+
+		ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
+		if (ret < 0) {
+			kfree(value);
+			return ret;
+		}
+
+		ret = fuse_setxattr(inode, name, value, size, 0);
+		kfree(value);
+	} else {
+		ret = fuse_removexattr(inode, name);
+	}
+	if (ret == 0)
+		set_cached_acl(inode, type, acl);
+	return ret;
+}
+
+int fuse_init_acl(struct inode *inode, struct inode *dir)
+{
+	struct posix_acl *default_acl, *acl;
+	int err;
+
+	err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
+	if (err)
+		return err;
+
+	if (default_acl) {
+		err = fuse_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
+		posix_acl_release(default_acl);
+	}
+	if (acl) {
+		if (!err)
+			err = fuse_set_acl(inode, acl, ACL_TYPE_ACCESS);
+		posix_acl_release(default_acl);
+	}
+	return err;
+}
+
+#else /* !CONFIG_FUSE_FS_POSIX_ACL */
+
+static int fuse_xattr_acl_get(const struct xattr_handler *handler,
+			      struct dentry *dentry, struct inode *inode,
+			      const char *name, void *value, size_t size)
+{
+	return -EOPNOTSUPP;
+}
+
+static int fuse_xattr_acl_set(const struct xattr_handler *handler,
+				struct dentry *dentry, struct inode *inode,
+				const char *name, const void *value, size_t size,
+				int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_FUSE_FS_POSIX_ACL */
+
+const struct xattr_handler fuse_xattr_acl_access_handler = {
+	.name	= XATTR_NAME_POSIX_ACL_ACCESS,
+	.flags	= ACL_TYPE_ACCESS,
+	.get	= fuse_xattr_acl_get,
+	.set	= fuse_xattr_acl_set,
+};
+
+const struct xattr_handler fuse_xattr_acl_default_handler = {
+	.name	= XATTR_NAME_POSIX_ACL_DEFAULT,
+	.flags	= ACL_TYPE_DEFAULT,
+	.get	= fuse_xattr_acl_get,
+	.set	= fuse_xattr_acl_set,
+};
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 9df55b8e776a..ca7d573f3121 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -14,6 +14,7 @@
 #include <linux/namei.h>
 #include <linux/slab.h>
 #include <linux/xattr.h>
+#include <linux/posix_acl.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
 {
@@ -244,6 +245,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
 		if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
 			goto invalid;
 
+		forget_all_cached_acls(inode);
 		fuse_change_attributes(inode, &outarg.attr,
 				       entry_attr_timeout(&outarg),
 				       attr_version);
@@ -554,6 +556,14 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
 	}
 	kfree(forget);
 
+	if (args->in.h.opcode != FUSE_LINK) {
+		err = fuse_init_acl(inode, dir);
+		if (err) {
+			iput(inode);
+			return err;
+		}
+	}
+
 	err = d_instantiate_no_diralias(entry, inode);
 	if (err)
 		return err;
@@ -916,6 +926,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
 
 	if (time_before64(fi->i_time, get_jiffies_64())) {
 		r = true;
+		forget_all_cached_acls(inode);
 		err = fuse_do_getattr(inode, stat, file);
 	} else {
 		r = false;
@@ -1062,6 +1073,7 @@ static int fuse_perm_getattr(struct inode *inode, int mask)
 	if (mask & MAY_NOT_BLOCK)
 		return -ECHILD;
 
+	forget_all_cached_acls(inode);
 	return fuse_do_getattr(inode, NULL, NULL);
 }
 
@@ -1231,6 +1243,7 @@ retry:
 		fi->nlookup++;
 		spin_unlock(&fc->lock);
 
+		forget_all_cached_acls(inode);
 		fuse_change_attributes(inode, &o->attr,
 				       entry_attr_timeout(o),
 				       attr_version);
@@ -1698,14 +1711,19 @@ error:
 static int fuse_setattr(struct dentry *entry, struct iattr *attr)
 {
 	struct inode *inode = d_inode(entry);
+	int ret;
 
 	if (!fuse_allow_current_process(get_fuse_conn(inode)))
 		return -EACCES;
 
 	if (attr->ia_valid & ATTR_FILE)
-		return fuse_do_setattr(inode, attr, attr->ia_file);
+		ret = fuse_do_setattr(inode, attr, attr->ia_file);
 	else
-		return fuse_do_setattr(inode, attr, NULL);
+		ret = fuse_do_setattr(inode, attr, NULL);
+
+	if (!ret && (attr->ia_valid & ATTR_MODE))
+		ret = posix_acl_chmod(inode, inode->i_mode);
+	return ret;
 }
 
 static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
@@ -1738,6 +1756,8 @@ static const struct inode_operations fuse_dir_inode_operations = {
 	.getxattr	= generic_getxattr,
 	.listxattr	= fuse_listxattr,
 	.removexattr	= generic_removexattr,
+	.get_acl	= fuse_get_acl,
+	.set_acl	= fuse_set_acl,
 };
 
 static const struct file_operations fuse_dir_operations = {
@@ -1759,6 +1779,8 @@ static const struct inode_operations fuse_common_inode_operations = {
 	.getxattr	= generic_getxattr,
 	.listxattr	= fuse_listxattr,
 	.removexattr	= generic_removexattr,
+	.get_acl	= fuse_get_acl,
+	.set_acl	= fuse_set_acl,
 };
 
 static const struct inode_operations fuse_symlink_inode_operations = {
@@ -1770,6 +1792,8 @@ static const struct inode_operations fuse_symlink_inode_operations = {
 	.getxattr	= generic_getxattr,
 	.listxattr	= fuse_listxattr,
 	.removexattr	= generic_removexattr,
+	.get_acl	= fuse_get_acl,
+	.set_acl	= fuse_set_acl,
 };
 
 void fuse_init_common(struct inode *inode)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 93a5e8191da1..c1a630bb2b21 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -25,6 +25,7 @@
 #include <linux/kref.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
+#include <linux/xattr.h>
 
 /** Max number of pages that can be used in a single read request */
 #define FUSE_MAX_PAGES_PER_REQ 32
@@ -966,7 +967,35 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
 
 void fuse_set_initialized(struct fuse_conn *fc);
 
+int fuse_setxattr(struct inode *inode, const char *name, const void *value,
+		  size_t size, int flags);
+ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
+		      size_t size);
 ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
+int fuse_removexattr(struct inode *inode, const char *name);
 extern const struct xattr_handler *fuse_xattr_handlers[];
 
+struct posix_acl;
+
+extern const struct xattr_handler fuse_xattr_acl_access_handler;
+extern const struct xattr_handler fuse_xattr_acl_default_handler;
+
+#ifdef CONFIG_FUSE_FS_POSIX_ACL
+
+struct posix_acl *fuse_get_acl(struct inode *inode, int type);
+int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
+int fuse_init_acl(struct inode *inode, struct inode *dir);
+
+#else
+
+#define fuse_get_acl NULL
+#define fuse_set_acl NULL
+
+static inline int fuse_init_acl(struct inode *inode, struct inode *dir)
+{
+	return 0;
+}
+
+#endif
+
 #endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index fee06e48157d..9c1519c269bb 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -21,6 +21,7 @@
 #include <linux/sched.h>
 #include <linux/exportfs.h>
 #include <linux/pid_namespace.h>
+#include <linux/posix_acl.h>
 
 MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
 MODULE_DESCRIPTION("Filesystem in Userspace");
@@ -339,6 +340,7 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
 		return -ENOENT;
 
 	fuse_invalidate_attr(inode);
+	forget_all_cached_acls(inode);
 	if (offset >= 0) {
 		pg_start = offset >> PAGE_SHIFT;
 		if (len <= 0)
diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c
index d30e99610217..d87d58112eb1 100644
--- a/fs/fuse/xattr.c
+++ b/fs/fuse/xattr.c
@@ -9,9 +9,10 @@
 #include "fuse_i.h"
 
 #include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 
-static int fuse_setxattr(struct inode *inode, const char *name,
-			 const void *value, size_t size, int flags)
+int fuse_setxattr(struct inode *inode, const char *name, const void *value,
+		  size_t size, int flags)
 {
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	FUSE_ARGS(args);
@@ -45,8 +46,8 @@ static int fuse_setxattr(struct inode *inode, const char *name,
 	return err;
 }
 
-static ssize_t fuse_getxattr(struct inode *inode, const char *name,
-			     void *value, size_t size)
+ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
+		      size_t size)
 {
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	FUSE_ARGS(args);
@@ -128,7 +129,7 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
 	return ret;
 }
 
-static int fuse_removexattr(struct inode *inode, const char *name)
+int fuse_removexattr(struct inode *inode, const char *name)
 {
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	FUSE_ARGS(args);
@@ -179,5 +180,7 @@ static const struct xattr_handler fuse_xattr_handler = {
 };
 
 const struct xattr_handler *fuse_xattr_handlers[] = {
+	&fuse_xattr_acl_access_handler,
+	&fuse_xattr_acl_default_handler,
 	&fuse_xattr_handler,
 };
-- 
2.7.4


  parent reply	other threads:[~2016-08-01 21:27 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-01 21:27 [RFC v3 0/2] Support for posix acls in fuse Seth Forshee
2016-08-01 21:27 ` [RFC v3 1/2] fuse: Use generic xattr ops Seth Forshee
2016-08-04 11:09   ` Miklos Szeredi
2016-08-04 14:12     ` Seth Forshee
2016-08-01 21:27 ` Seth Forshee [this message]
2016-08-04 12:11   ` [RFC v3 2/2] fuse: Add posix acl support Miklos Szeredi
     [not found]     ` <CAJfpegtzeJid8tHkz66scDcpCjNEEwtBb4m8MQqq7u+SCdj3dQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-08-04 12:40       ` Ravishankar N
2016-08-04 14:11     ` Seth Forshee
2016-08-05 23:07       ` Eric W. Biederman
2016-08-06  1:52         ` Seth Forshee
2016-08-06 21:09           ` Miklos Szeredi
2016-08-07  3:46             ` Seth Forshee
2016-08-07 12:59               ` Eric W. Biederman
     [not found]                 ` <87popkrazt.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>
2016-08-07 13:51                   ` Seth Forshee
2016-08-16 20:59     ` Seth Forshee
2016-08-17 12:01       ` Miklos Szeredi
2016-08-01 23:03 ` [RFC v3 0/2] Support for posix acls in fuse Nikolaus Rath
2016-08-02  3:39   ` Seth Forshee
2016-08-02 15:13     ` [fuse-devel] " Michael Theall
2016-08-09  0:00       ` Nikolaus Rath
2016-08-09  0:03 ` Nikolaus Rath
2016-08-09  0:27   ` Eric W. Biederman
2016-08-09 22:44     ` Nikolaus Rath
2016-08-09  7:06   ` Jean-Pierre André

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1470086846-19844-3-git-send-email-seth.forshee@canonical.com \
    --to=seth.forshee@canonical.com \
    --cc=Nikolaus@rath.org \
    --cc=ebiederm@xmission.com \
    --cc=fuse-devel@lists.sourceforge.net \
    --cc=jean-pierre.andre@wanadoo.fr \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=miklos@szeredi.hu \
    --cc=mtheall@us.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).