Skip to contentSkip to conversation

fuse: Support posix ACLs

RFC Superseded Seth Forshee 5 participants 18 messages Jun 29, 2016 – Jul 1, 2016 2 messages not in the archive

Subsystems (2)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

RFC 1/1 fuse: Support posix ACLs

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

Are there any places where we need to invalidate cached ACLs that aren't covered in the patch?

Thanks,
Seth

fs/fuse/dir.c +155 -16
index 8466e122ee62..2f53b0491159 100644
@@ -13,6 +13,8 @@
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
 {
@@ -1061,6 +1063,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);
 }
 
@@ -1719,9 +1722,8 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
 	return fuse_update_attributes(inode, stat, NULL, NULL);
 }
 
-static int fuse_setxattr(struct dentry *unused, struct inode *inode,
-			 const char *name, const void *value,
-			 size_t size, int flags)
+static 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);
@@ -1755,8 +1757,8 @@ static int fuse_setxattr(struct dentry *unused, struct inode *inode,
 	return err;
 }
 
-static ssize_t fuse_getxattr(struct dentry *entry, struct inode *inode,
-			     const char *name, void *value, size_t size)
+static 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);
@@ -1838,9 +1840,8 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
 	return ret;
 }
 
-static int fuse_removexattr(struct dentry *entry, const char *name)
+static int fuse_removexattr(struct inode *inode, const char *name)
 {
-	struct inode *inode = d_inode(entry);
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	FUSE_ARGS(args);
 	int err;
@@ -1865,6 +1866,138 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
 	return err;
 }
 
+static int fuse_xattr_get(const struct xattr_handler *handler,
+			  struct dentry *dentry, struct inode *inode,
+			  const char *name, void *value, size_t size)
+{
+	return fuse_getxattr(inode, name, value, size);
+}
+
+static int fuse_xattr_set(const struct xattr_handler *handler,
+			  struct dentry *dentry, struct inode *inode,
+			  const char *name, const void *value, size_t size,
+			  int flags)
+{
+	if (!value)
+		return fuse_removexattr(inode, name);
+
+	return fuse_setxattr(inode, name, value, size, flags);
+}
+
+static const struct xattr_handler fuse_xattr_handler = {
+	.prefix	= "",
+	.get	= fuse_xattr_get,
+	.set	= fuse_xattr_set,
+};
+
+#ifndef CONFIG_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;
+}
+
+static const struct xattr_handler fuse_xattr_acl_access_handler = {
+	.name	= XATTR_NAME_POSIX_ACL_ACCESS,
+	.get	= fuse_xattr_acl_get,
+	.set	= fuse_xattr_acl_set,
+};
+
+static const struct xattr_handler fuse_xattr_acl_default_handler = {
+	.name	= XATTR_NAME_POSIX_ACL_DEFAULT,
+	.get	= fuse_xattr_acl_get,
+	.set	= fuse_xattr_acl_set,
+};
+#endif /* CONFIG_POSIX_ACL */
+
+const struct xattr_handler *fuse_xattr_handlers[] = {
+#ifdef CONFIG_FS_POSIX_ACL
+	&posix_acl_access_xattr_handler,
+	&posix_acl_default_xattr_handler,
+#else
+	&fuse_xattr_acl_access_handler,
+	&fuse_xattr_acl_default_handler,
+#endif
+	&fuse_xattr_handler,
+};
+
+static struct posix_acl *fuse_get_acl(struct inode *inode, int type)
+{
+	int size;
+	const char *name;
+	void *value = NULL;
+	struct posix_acl *acl;
+
+	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(inode->i_sb->s_user_ns, value, size);
+	} else if ((size == 0) || (size == -ENODATA)) {
+		acl = NULL;
+	} else {
+		acl = ERR_PTR(size);
+	}
+	kfree(value);
+
+	return acl;
+}
+
+static int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+{
+	const char *name;
+	int ret;
+
+	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 -EINVAL;
+
+	if (acl) {
+		struct user_namespace *s_user_ns = inode->i_sb->s_user_ns;
+		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(s_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;
+}
+
 static const struct inode_operations fuse_dir_inode_operations = {
 	.lookup		= fuse_lookup,
 	.mkdir		= fuse_mkdir,
@@ -1879,10 +2012,12 @@ static const struct inode_operations fuse_dir_inode_operations = {
 	.mknod		= fuse_mknod,
 	.permission	= fuse_permission,
 	.getattr	= fuse_getattr,
-	.setxattr	= fuse_setxattr,
-	.getxattr	= fuse_getxattr,
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
 	.listxattr	= fuse_listxattr,
-	.removexattr	= fuse_removexattr,
+	.removexattr	= generic_removexattr,
+	.get_acl	= fuse_get_acl,
+	.set_acl	= fuse_set_acl,
 };
 
 static const struct file_operations fuse_dir_operations = {
@@ -1900,10 +2035,12 @@ static const struct inode_operations fuse_common_inode_operations = {
 	.setattr	= fuse_setattr,
 	.permission	= fuse_permission,
 	.getattr	= fuse_getattr,
-	.setxattr	= fuse_setxattr,
-	.getxattr	= fuse_getxattr,
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
 	.listxattr	= fuse_listxattr,
-	.removexattr	= fuse_removexattr,
+	.removexattr	= generic_removexattr,
+	.get_acl	= fuse_get_acl,
+	.set_acl	= fuse_set_acl,
 };
 
 static const struct inode_operations fuse_symlink_inode_operations = {
@@ -1911,10 +2048,12 @@ static const struct inode_operations fuse_symlink_inode_operations = {
 	.get_link	= fuse_get_link,
 	.readlink	= generic_readlink,
 	.getattr	= fuse_getattr,
-	.setxattr	= fuse_setxattr,
-	.getxattr	= fuse_getxattr,
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
 	.listxattr	= fuse_listxattr,
-	.removexattr	= fuse_removexattr,
+	.removexattr	= generic_removexattr,
+	.get_acl	= fuse_get_acl,
+	.set_acl	= fuse_set_acl,
 };
 
 void fuse_init_common(struct inode *inode)
fs/fuse/fuse_i.h +3 -0
index 9f4c3c82edd6..02c08796051e 100644
@@ -23,6 +23,7 @@
 #include <linux/poll.h>
 #include <linux/workqueue.h>
 #include <linux/kref.h>
+#include <linux/xattr.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
 
@@ -693,6 +694,8 @@ extern const struct file_operations fuse_dev_operations;
 
 extern const struct dentry_operations fuse_dentry_operations;
 
+extern const struct xattr_handler *fuse_xattr_handlers[];
+
 /**
  * Inode to nodeid comparison.
  */
fs/fuse/inode.c +3 -0
index 254f1944ee98..9c1519c269bb 100644
@@ -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 <[email protected]>");
 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)
@@ -1066,6 +1068,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
 	}
 	sb->s_magic = FUSE_SUPER_MAGIC;
 	sb->s_op = &fuse_super_operations;
+	sb->s_xattr = fuse_xattr_handlers;
 	sb->s_maxbytes = MAX_LFS_FILESIZE;
 	sb->s_time_gran = 1;
 	sb->s_export_op = &fuse_export_operations;
#1 lore raw

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Regards,
Michael Theall

Seth Forshee <[email protected]> wrote on 06/29/2016 02:07:31 PM:

Quoted 30 lines

From: Seth Forshee <[email protected]>
To: [email protected], [email protected]
Cc: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/[email protected]>, Miklos Szeredi <[email protected]>
Date: 06/29/2016 02:08 PM
Subject: [fuse-devel] [RFC] fuse: Support posix ACLs

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

Are there any places where we need to invalidate cached ACLs that aren't covered in the patch?

Thanks,
Seth

fs/fuse/dir.c +0 -0
index 8466e122ee62..2f53b0491159 100644

fs/fuse/dir.c line 19

Quoted 7 lines
fs/fuse/dir.c +2 -0
@@ -13,6 +13,8 @@
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct dir_context

*ctx)

Quoted 140 lines
fs/fuse/dir.c +3 -3
 {
@@ -1061,6 +1063,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);
 }
@@ -1719,9 +1722,8 @@ static int fuse_getattr(struct vfsmount *mnt,
 struct dentry *entry,
    return fuse_update_attributes(inode, stat, NULL, NULL);
 }
 
-static int fuse_setxattr(struct dentry *unused, struct inode *inode,
-          const char *name, const void *value,
-          size_t size, int flags)
+static 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);
fs/fuse/dir.c +2 -2
@@ -1755,8 +1757,8 @@ static int fuse_setxattr(struct dentry
 *unused, struct inode *inode,
    return err;
 }
 
-static ssize_t fuse_getxattr(struct dentry *entry, struct inode *inode,
-              const char *name, void *value, size_t size)
+static 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);
fs/fuse/dir.c +1 -2
@@ -1838,9 +1840,8 @@ static ssize_t fuse_listxattr(struct dentry
 *entry, char *list, size_t size)
    return ret;
 }
 
-static int fuse_removexattr(struct dentry *entry, const char *name)
+static int fuse_removexattr(struct inode *inode, const char *name)
 {
-   struct inode *inode = d_inode(entry);
    struct fuse_conn *fc = get_fuse_conn(inode);
    FUSE_ARGS(args);
    int err;
fs/fuse/dir.c +88 -0
@@ -1865,6 +1866,138 @@ static int fuse_removexattr(struct dentry
 *entry, const char *name)
    return err;
 }
 
+static int fuse_xattr_get(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, void *value, size_t size)
+{
+   return fuse_getxattr(inode, name, value, size);
+}
+
+static int fuse_xattr_set(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, const void *value, size_t size,
+           int flags)
+{
+   if (!value)
+      return fuse_removexattr(inode, name);
+
+   return fuse_setxattr(inode, name, value, size, flags);
+}
+
+static const struct xattr_handler fuse_xattr_handler = {
+   .prefix   = "",
+   .get   = fuse_xattr_get,
+   .set   = fuse_xattr_set,
+};
+
+#ifndef CONFIG_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;
+}
+
+static const struct xattr_handler fuse_xattr_acl_access_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_ACCESS,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+
+static const struct xattr_handler fuse_xattr_acl_default_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_DEFAULT,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+#endif /* CONFIG_POSIX_ACL */
+
+const struct xattr_handler *fuse_xattr_handlers[] = {
+#ifdef CONFIG_FS_POSIX_ACL
+   &posix_acl_access_xattr_handler,
+   &posix_acl_default_xattr_handler,
+#else
+   &fuse_xattr_acl_access_handler,
+   &fuse_xattr_acl_default_handler,
+#endif
+   &fuse_xattr_handler,
+};
+
+static struct posix_acl *fuse_get_acl(struct inode *inode, int type)
+{
+   int size;
+   const char *name;
+   void *value = NULL;
+   struct posix_acl *acl;
+
+   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(inode->i_sb->s_user_ns, value, size);
+   } else if ((size == 0) || (size == -ENODATA)) {

fs/fuse/dir.c static int fuse_removexattr(struct dentry · line 1967

Quoted 10 lines
fs/fuse/dir.c +10 -0
+      acl = NULL;
+   } else {
+      acl = ERR_PTR(size);
+   }
+   kfree(value);
+
+   return acl;
+}
+
+static int fuse_set_acl(struct inode *inode, struct posix_acl *acl,int

type)

fs/fuse/fuse_i.h extern const struct file_operations

Quoted 101 lines
fs/fuse/dir.c +34 -0
+{
+   const char *name;
+   int ret;
+
+   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 -EINVAL;
+
+   if (acl) {
+      struct user_namespace *s_user_ns = inode->i_sb->s_user_ns;
+      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(s_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;
+}
+
 static const struct inode_operations fuse_dir_inode_operations = {
    .lookup      = fuse_lookup,

.mkdir = fuse_mkdir,

fs/fuse/dir.c +5 -3
@@ -1879,10 +2012,12 @@ static const struct inode_operations
 fuse_dir_inode_operations = {
    .mknod      = fuse_mknod,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct file_operations fuse_dir_operations = {

fs/fuse/dir.c +5 -3
@@ -1900,10 +2035,12 @@ static const struct inode_operations
 fuse_common_inode_operations = {
    .setattr   = fuse_setattr,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct inode_operations fuse_symlink_inode_operations = {

fs/fuse/dir.c +5 -3
@@ -1911,10 +2048,12 @@ static const struct inode_operations
 fuse_symlink_inode_operations = {
    .get_link   = fuse_get_link,
    .readlink   = generic_readlink,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

void fuse_init_common(struct inode *inode)

fs/fuse/fuse_i.h +1 -0
index 9f4c3c82edd6..02c08796051e 100644
@@ -23,6 +23,7 @@
 #include <linux/poll.h>
 #include <linux/workqueue.h>
 #include <linux/kref.h>
+#include <linux/xattr.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
 
@@ -693,6 +694,8 @@ extern const struct file_operations

fuse_dev_operations;

Quoted 37 lines
fs/fuse/fuse_i.h +2 -0
 extern const struct dentry_operations fuse_dentry_operations;
 
+extern const struct xattr_handler *fuse_xattr_handlers[];
+
 /**
  * Inode to nodeid comparison.
  */
fs/fuse/inode.c +2 -0
index 254f1944ee98..9c1519c269bb 100644
@@ -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 <[email protected]>");
 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)
fs/fuse/inode.c +1 -0
@@ -1066,6 +1068,7 @@ static int fuse_fill_super(struct super_block
 *sb, void *data, int silent)
    }
    sb->s_magic = FUSE_SUPER_MAGIC;
    sb->s_op = &fuse_super_operations;
+   sb->s_xattr = fuse_xattr_handlers;
    sb->s_maxbytes = MAX_LFS_FILESIZE;
    sb->s_time_gran = 1;
Quoted 4 lines
    sb->s_export_op = &fuse_export_operations;

------------------------------------------------------------------------------

Quoted 10 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape
--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

------------------------------------------------------------------------------
Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape

Signature · 2 lines
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/lists/listinfo/fuse-devel

A message that is not in the archive (sent off-list or not yet received): OFF8F0F486.DB2CEB73-ON86257FE1.006A1FF4-86257FE1.006A9703-8eTO7WVQ4XIsd+ienQ86orlN3bxYEBpz@public.gmane.org

#3 lore raw

More comments:

My patch does set_cached_acl() in fuse_get_acl(). I would investigate if this is necessary.

Since your patch invalidates the cached acls on getattr(), I would investigate on whether you also need to invalidate cached acls on the entries returned by readdirplus.

Quoted 4 lines

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

I wouldn't exactly call it "support". Basically by turning off default_permissions, the fuser server can do what it wants permission-wise. My filesystem turns off default_permissions in order to support ACLs, but as far as fuse is concerned, it is totally oblivious and blindly does what my server says is permitted. That being said, there still lies the problem of checking permissions along the path. The low-level interface only gives you the final inode, which in the case of hard links, the path is ambiguous. This means you can't check each path component for permissions (and the kernel certainly won't! since you turned off default_permissions). Therefore the only "correct" way to implement ACL support on the fuse server is by setting the attr/entry timeout to 0 to force the lookup every time, which defeats many purposes of using fuse in the first place. You can read all the gory details in the thread I linked containing my original patch.

Fortunately, this patch (and mine) allows you to use default_permissions *and* have acl support, meaning permission checks will be performed in kernel for each path component. The extra benefit is that it allows any backing filesystem which supports xattrs to support acls, provided your fuse server has unencumbered privileges for that filesystem.

Regards,
Michael Theall

Michael j Theall/Houston/IBM@IBMUS wrote on 06/29/2016 02:24:14 PM:

Quoted 7 lines

From: Michael j Theall/Houston/IBM@IBMUS
To: Seth Forshee <[email protected]>
Cc: [email protected], [email protected], Miklos-QbrlkLqkJHK1JOh/[email protected], Szeredi <[email protected]>
Date: 06/29/2016 02:37 PM
Subject: Re: [fuse-devel] [RFC] fuse: Support posix ACLs

Quoted 8 lines

Going by the patch I posted a couple of years ago: https://
sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to- speed on the kernel so I'm not sure if you actually need to implement

both.

Quoted 5 lines

Regards,
Michael Theall

Seth Forshee <[email protected]> wrote on 06/29/2016 02:07:31

PM:

Quoted 8 lines
Quoted 8 lines

From: Seth Forshee <[email protected]>
To: [email protected], [email protected]
Cc: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/[email protected]>, Miklos Szeredi <[email protected]>
Date: 06/29/2016 02:08 PM
Subject: [fuse-devel] [RFC] fuse: Support posix ACLs

Quoted 7 lines
Quoted 7 lines

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace

side

Quoted 5 lines
Quoted 5 lines

when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

Are there any places where we need to invalidate cached ACLs that

aren't

Quoted 47 lines
Quoted 11 lines

covered in the patch?

Thanks,
Seth

fs/fuse/dir.c +0 -0
index 8466e122ee62..2f53b0491159 100644

fs/fuse/dir.c line 19

Quoted 7 lines
fs/fuse/dir.c +2 -0
@@ -13,6 +13,8 @@
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct

dir_context *ctx)

Quoted 28 lines
fs/fuse/dir.c +3 -3
 {
@@ -1061,6 +1063,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);
 }
@@ -1719,9 +1722,8 @@ static int fuse_getattr(struct vfsmount *mnt,
 struct dentry *entry,
    return fuse_update_attributes(inode, stat, NULL, NULL);
 }
 
-static int fuse_setxattr(struct dentry *unused, struct inode *inode,
-          const char *name, const void *value,
-          size_t size, int flags)
+static 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);
fs/fuse/dir.c +0 -0
@@ -1755,8 +1757,8 @@ static int fuse_setxattr(struct dentry
 *unused, struct inode *inode,
    return err;
 }
 
Quoted 1 line

fs/fuse/dir.c static int fuse_setxattr(struct dentry · line 1759

Quoted 1 line
fs/fuse/dir.c +0 -1
-static ssize_t fuse_getxattr(struct dentry *entry, struct inode

*inode,

Quoted 222 lines
Quoted 111 lines
fs/fuse/dir.c +2 -1
-              const char *name, void *value, size_t size)
+static 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);
fs/fuse/dir.c +1 -2
@@ -1838,9 +1840,8 @@ static ssize_t fuse_listxattr(struct dentry
 *entry, char *list, size_t size)
    return ret;
 }
 
-static int fuse_removexattr(struct dentry *entry, const char *name)
+static int fuse_removexattr(struct inode *inode, const char *name)
 {
-   struct inode *inode = d_inode(entry);
    struct fuse_conn *fc = get_fuse_conn(inode);
    FUSE_ARGS(args);
    int err;
fs/fuse/dir.c +88 -0
@@ -1865,6 +1866,138 @@ static int fuse_removexattr(struct dentry
 *entry, const char *name)
    return err;
 }
 
+static int fuse_xattr_get(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, void *value, size_t size)
+{
+   return fuse_getxattr(inode, name, value, size);
+}
+
+static int fuse_xattr_set(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, const void *value, size_t size,
+           int flags)
+{
+   if (!value)
+      return fuse_removexattr(inode, name);
+
+   return fuse_setxattr(inode, name, value, size, flags);
+}
+
+static const struct xattr_handler fuse_xattr_handler = {
+   .prefix   = "",
+   .get   = fuse_xattr_get,
+   .set   = fuse_xattr_set,
+};
+
+#ifndef CONFIG_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;
+}
+
+static const struct xattr_handler fuse_xattr_acl_access_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_ACCESS,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+
+static const struct xattr_handler fuse_xattr_acl_default_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_DEFAULT,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+#endif /* CONFIG_POSIX_ACL */
+
+const struct xattr_handler *fuse_xattr_handlers[] = {
+#ifdef CONFIG_FS_POSIX_ACL
+   &posix_acl_access_xattr_handler,
+   &posix_acl_default_xattr_handler,
+#else
+   &fuse_xattr_acl_access_handler,
+   &fuse_xattr_acl_default_handler,
+#endif
+   &fuse_xattr_handler,
+};
+
+static struct posix_acl *fuse_get_acl(struct inode *inode, int type)
+{
+   int size;
+   const char *name;
+   void *value = NULL;
+   struct posix_acl *acl;
+
+   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(inode->i_sb->s_user_ns, value, size);
+   } else if ((size == 0) || (size == -ENODATA)) {

fs/fuse/dir.c static int fuse_removexattr(struct dentry · line 1967

Quoted 10 lines
fs/fuse/dir.c +10 -0
+      acl = NULL;
+   } else {
+      acl = ERR_PTR(size);
+   }
+   kfree(value);
+
+   return acl;
+}
+
+static int fuse_set_acl(struct inode *inode, struct posix_acl

*acl,int type)

Quoted 100 lines
fs/fuse/dir.c +34 -0
+{
+   const char *name;
+   int ret;
+
+   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 -EINVAL;
+
+   if (acl) {
+      struct user_namespace *s_user_ns = inode->i_sb->s_user_ns;
+      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(s_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;
+}
+
 static const struct inode_operations fuse_dir_inode_operations = {
    .lookup      = fuse_lookup,

.mkdir = fuse_mkdir,

fs/fuse/dir.c +5 -3
@@ -1879,10 +2012,12 @@ static const struct inode_operations
 fuse_dir_inode_operations = {
    .mknod      = fuse_mknod,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct file_operations fuse_dir_operations = {

fs/fuse/dir.c +5 -3
@@ -1900,10 +2035,12 @@ static const struct inode_operations
 fuse_common_inode_operations = {
    .setattr   = fuse_setattr,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct inode_operations fuse_symlink_inode_operations = {

fs/fuse/dir.c +5 -3
@@ -1911,10 +2048,12 @@ static const struct inode_operations
 fuse_symlink_inode_operations = {
    .get_link   = fuse_get_link,
    .readlink   = generic_readlink,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

void fuse_init_common(struct inode *inode)

fs/fuse/fuse_i.h +1 -0
index 9f4c3c82edd6..02c08796051e 100644
@@ -23,6 +23,7 @@
 #include <linux/poll.h>
 #include <linux/workqueue.h>
 #include <linux/kref.h>
+#include <linux/xattr.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
 
Quoted 1 line

fs/fuse/fuse_i.h extern const struct file_operations

Quoted 1 line
fs/fuse/fuse_i.h +0 -0
@@ -693,6 +694,8 @@ extern const struct file_operations

fuse_dev_operations;

Quoted 33 lines
Quoted 33 lines
fs/fuse/fuse_i.h +2 -0
 extern const struct dentry_operations fuse_dentry_operations;
 
+extern const struct xattr_handler *fuse_xattr_handlers[];
+
 /**
  * Inode to nodeid comparison.
  */
fs/fuse/inode.c +2 -0
index 254f1944ee98..9c1519c269bb 100644
@@ -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 <[email protected]>");
 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)
fs/fuse/inode.c +0 -0
@@ -1066,6 +1068,7 @@ static int fuse_fill_super(struct super_block
 *sb, void *data, int silent)
    }
    sb->s_magic = FUSE_SUPER_MAGIC;
Quoted 9 lines
Quoted 8 lines
fs/fuse/inode.c +1 -0
    sb->s_op = &fuse_super_operations;
+   sb->s_xattr = fuse_xattr_handlers;
    sb->s_maxbytes = MAX_LFS_FILESIZE;
    sb->s_time_gran = 1;
    sb->s_export_op = &fuse_export_operations;

------------------------------------------------------------------------------

Quoted 2 lines
Quoted 2 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech

luminaries

Quoted 9 lines
Quoted 8 lines

present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape
--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

------------------------------------------------------------------------------

Quoted 8 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

------------------------------------------------------------------------------
Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape

Signature · 2 lines
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/lists/listinfo/fuse-devel
#4 lore raw
Quoted 5 lines · Michael j Theall

More comments:

My patch does set_cached_acl() in fuse_get_acl(). I would investigate if this is necessary.

Hmm, on the one hand it looks like a lot of filesystems do call it, on the other hand it looks like get_acl is caching it. I'll have to take a closer look.

Quoted 3 lines

Since your patch invalidates the cached acls on getattr(), I would investigate on whether you also need to invalidate cached acls on the entries returned by readdirplus.

Possibly so. I missed the fact that fuse_direntplus_link might update the inode attributes.

Quoted 20 lines
Quoted 4 lines

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

I wouldn't exactly call it "support". Basically by turning off default_permissions, the fuser server can do what it wants permission-wise. My filesystem turns off default_permissions in order to support ACLs, but as far as fuse is concerned, it is totally oblivious and blindly does what my server says is permitted. That being said, there still lies the problem of checking permissions along the path. The low-level interface only gives you the final inode, which in the case of hard links, the path is ambiguous. This means you can't check each path component for permissions (and the kernel certainly won't! since you turned off default_permissions). Therefore the only "correct" way to implement ACL support on the fuse server is by setting the attr/entry timeout to 0 to force the lookup every time, which defeats many purposes of using fuse in the first place. You can read all the gory details in the thread I linked containing my original patch.

Quoted 5 lines

Fortunately, this patch (and mine) allows you to use default_permissions *and* have acl support, meaning permission checks will be performed in kernel for each path component. The extra benefit is that it allows any backing filesystem which supports xattrs to support acls, provided your fuse server has unencumbered privileges for that filesystem.

But if we're going through the posix acl xattr handlers then the acls will be cached. What I worry about is that somehow we could end up in a situation where the cached acls don't match those that the filesystem is using, and userspace sees one thing in response to getfacl but something different is being enforced.

Thanks,
Seth

Quoted 389 lines

Regards,
Michael Theall

Michael j Theall/Houston/IBM@IBMUS wrote on 06/29/2016 02:24:14 PM:

Quoted 7 lines

From: Michael j Theall/Houston/IBM@IBMUS
To: Seth Forshee <[email protected]>
Cc: [email protected], [email protected], [email protected], Szeredi <[email protected]>
Date: 06/29/2016 02:37 PM
Subject: Re: [fuse-devel] [RFC] fuse: Support posix ACLs

Quoted 8 lines

Going by the patch I posted a couple of years ago: https://
sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to- speed on the kernel so I'm not sure if you actually need to implement

both.

Quoted 5 lines

Regards,
Michael Theall

Seth Forshee <[email protected]> wrote on 06/29/2016 02:07:31

PM:

Quoted 8 lines
Quoted 8 lines

From: Seth Forshee <[email protected]>
To: [email protected], [email protected]
Cc: "Eric W. Biederman" <[email protected]>, Miklos Szeredi <[email protected]>
Date: 06/29/2016 02:08 PM
Subject: [fuse-devel] [RFC] fuse: Support posix ACLs

Quoted 7 lines
Quoted 7 lines

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace

side

Quoted 5 lines
Quoted 5 lines

when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

Are there any places where we need to invalidate cached ACLs that

aren't

Quoted 47 lines
Quoted 11 lines

covered in the patch?

Thanks,
Seth

fs/fuse/dir.c +0 -0
index 8466e122ee62..2f53b0491159 100644

fs/fuse/dir.c line 19

Quoted 7 lines
fs/fuse/dir.c +2 -0
@@ -13,6 +13,8 @@
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct

dir_context *ctx)

Quoted 28 lines
fs/fuse/dir.c +3 -3
 {
@@ -1061,6 +1063,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);
 }
@@ -1719,9 +1722,8 @@ static int fuse_getattr(struct vfsmount *mnt,
 struct dentry *entry,
    return fuse_update_attributes(inode, stat, NULL, NULL);
 }
 
-static int fuse_setxattr(struct dentry *unused, struct inode *inode,
-          const char *name, const void *value,
-          size_t size, int flags)
+static 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);
fs/fuse/dir.c +0 -0
@@ -1755,8 +1757,8 @@ static int fuse_setxattr(struct dentry
 *unused, struct inode *inode,
    return err;
 }
 
Quoted 1 line

fs/fuse/dir.c static int fuse_setxattr(struct dentry · line 1759

Quoted 1 line
fs/fuse/dir.c +0 -1
-static ssize_t fuse_getxattr(struct dentry *entry, struct inode

*inode,

Quoted 222 lines
Quoted 111 lines
fs/fuse/dir.c +2 -1
-              const char *name, void *value, size_t size)
+static 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);
fs/fuse/dir.c +1 -2
@@ -1838,9 +1840,8 @@ static ssize_t fuse_listxattr(struct dentry
 *entry, char *list, size_t size)
    return ret;
 }
 
-static int fuse_removexattr(struct dentry *entry, const char *name)
+static int fuse_removexattr(struct inode *inode, const char *name)
 {
-   struct inode *inode = d_inode(entry);
    struct fuse_conn *fc = get_fuse_conn(inode);
    FUSE_ARGS(args);
    int err;
fs/fuse/dir.c +88 -0
@@ -1865,6 +1866,138 @@ static int fuse_removexattr(struct dentry
 *entry, const char *name)
    return err;
 }
 
+static int fuse_xattr_get(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, void *value, size_t size)
+{
+   return fuse_getxattr(inode, name, value, size);
+}
+
+static int fuse_xattr_set(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, const void *value, size_t size,
+           int flags)
+{
+   if (!value)
+      return fuse_removexattr(inode, name);
+
+   return fuse_setxattr(inode, name, value, size, flags);
+}
+
+static const struct xattr_handler fuse_xattr_handler = {
+   .prefix   = "",
+   .get   = fuse_xattr_get,
+   .set   = fuse_xattr_set,
+};
+
+#ifndef CONFIG_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;
+}
+
+static const struct xattr_handler fuse_xattr_acl_access_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_ACCESS,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+
+static const struct xattr_handler fuse_xattr_acl_default_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_DEFAULT,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+#endif /* CONFIG_POSIX_ACL */
+
+const struct xattr_handler *fuse_xattr_handlers[] = {
+#ifdef CONFIG_FS_POSIX_ACL
+   &posix_acl_access_xattr_handler,
+   &posix_acl_default_xattr_handler,
+#else
+   &fuse_xattr_acl_access_handler,
+   &fuse_xattr_acl_default_handler,
+#endif
+   &fuse_xattr_handler,
+};
+
+static struct posix_acl *fuse_get_acl(struct inode *inode, int type)
+{
+   int size;
+   const char *name;
+   void *value = NULL;
+   struct posix_acl *acl;
+
+   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(inode->i_sb->s_user_ns, value, size);
+   } else if ((size == 0) || (size == -ENODATA)) {

fs/fuse/dir.c static int fuse_removexattr(struct dentry · line 1967

Quoted 10 lines
fs/fuse/dir.c +10 -0
+      acl = NULL;
+   } else {
+      acl = ERR_PTR(size);
+   }
+   kfree(value);
+
+   return acl;
+}
+
+static int fuse_set_acl(struct inode *inode, struct posix_acl

*acl,int type)

Quoted 100 lines
fs/fuse/dir.c +34 -0
+{
+   const char *name;
+   int ret;
+
+   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 -EINVAL;
+
+   if (acl) {
+      struct user_namespace *s_user_ns = inode->i_sb->s_user_ns;
+      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(s_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;
+}
+
 static const struct inode_operations fuse_dir_inode_operations = {
    .lookup      = fuse_lookup,

.mkdir = fuse_mkdir,

fs/fuse/dir.c +5 -3
@@ -1879,10 +2012,12 @@ static const struct inode_operations
 fuse_dir_inode_operations = {
    .mknod      = fuse_mknod,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct file_operations fuse_dir_operations = {

fs/fuse/dir.c +5 -3
@@ -1900,10 +2035,12 @@ static const struct inode_operations
 fuse_common_inode_operations = {
    .setattr   = fuse_setattr,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct inode_operations fuse_symlink_inode_operations = {

fs/fuse/dir.c +5 -3
@@ -1911,10 +2048,12 @@ static const struct inode_operations
 fuse_symlink_inode_operations = {
    .get_link   = fuse_get_link,
    .readlink   = generic_readlink,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

void fuse_init_common(struct inode *inode)

fs/fuse/fuse_i.h +1 -0
index 9f4c3c82edd6..02c08796051e 100644
@@ -23,6 +23,7 @@
 #include <linux/poll.h>
 #include <linux/workqueue.h>
 #include <linux/kref.h>
+#include <linux/xattr.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
 
Quoted 1 line

fs/fuse/fuse_i.h extern const struct file_operations

Quoted 1 line
fs/fuse/fuse_i.h +0 -0
@@ -693,6 +694,8 @@ extern const struct file_operations

fuse_dev_operations;

Quoted 33 lines
Quoted 33 lines
fs/fuse/fuse_i.h +2 -0
 extern const struct dentry_operations fuse_dentry_operations;
 
+extern const struct xattr_handler *fuse_xattr_handlers[];
+
 /**
  * Inode to nodeid comparison.
  */
fs/fuse/inode.c +2 -0
index 254f1944ee98..9c1519c269bb 100644
@@ -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 <[email protected]>");
 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)
fs/fuse/inode.c +0 -0
@@ -1066,6 +1068,7 @@ static int fuse_fill_super(struct super_block
 *sb, void *data, int silent)
    }
    sb->s_magic = FUSE_SUPER_MAGIC;
Quoted 9 lines
Quoted 8 lines
fs/fuse/inode.c +1 -0
    sb->s_op = &fuse_super_operations;
+   sb->s_xattr = fuse_xattr_handlers;
    sb->s_maxbytes = MAX_LFS_FILESIZE;
    sb->s_time_gran = 1;
    sb->s_export_op = &fuse_export_operations;

------------------------------------------------------------------------------

Quoted 2 lines
Quoted 2 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech

luminaries

Quoted 9 lines
Quoted 8 lines

present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape
--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

------------------------------------------------------------------------------

Quoted 8 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

#5 lore raw

Seth Forshee <[email protected]> wrote on 06/29/2016 04:03:18 PM:

Quoted 7 lines

From: Seth Forshee <[email protected]>
To: Michael j Theall/Houston/IBM@IBMUS
Cc: [email protected], [email protected], Szeredi <[email protected]>, Miklos-QbrlkLqkJHK1JOh/[email protected]
Date: 06/29/2016 04:03 PM
Subject: Re: [fuse-devel] [RFC] fuse: Support posix ACLs

Quoted 5 lines
Quoted 4 lines · Michael j Theall

More comments:

My patch does set_cached_acl() in fuse_get_acl(). I would investigate

if

Quoted 6 lines
Quoted 1 line

this is necessary.

Hmm, on the one hand it looks like a lot of filesystems do call it, on the other hand it looks like get_acl is caching it. I'll have to take a closer look.

Quoted 8 lines
Quoted 3 lines

Since your patch invalidates the cached acls on getattr(), I would investigate on whether you also need to invalidate cached acls on the entries returned by readdirplus.

Possibly so. I missed the fact that fuse_direntplus_link might update the inode attributes.

Quoted 1 line
Quoted 1 line

There are some indications that fuse supports ACLs on the userspace

side

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do

we

Quoted 4 lines
Quoted 4 lines
Quoted 1 line

avoid those conflicts?

I wouldn't exactly call it "support". Basically by turning off default_permissions, the fuser server can do what it wants

permission-wise.

Quoted 1 line
Quoted 1 line

My filesystem turns off default_permissions in order to support ACLs,

but

Quoted 1 line
Quoted 1 line

as far as fuse is concerned, it is totally oblivious and blindly does

what

Quoted 1 line
Quoted 1 line

my server says is permitted. That being said, there still lies the

problem

Quoted 1 line
Quoted 1 line

of checking permissions along the path. The low-level interface only

gives

Quoted 2 lines
Quoted 2 lines

you the final inode, which in the case of hard links, the path is ambiguous. This means you can't check each path component for

permissions

Quoted 1 line
Quoted 1 line

(and the kernel certainly won't! since you turned off

default_permissions).

Quoted 2 lines
Quoted 2 lines

Therefore the only "correct" way to implement ACL support on the fuse server is by setting the attr/entry timeout to 0 to force the lookup

every

Quoted 1 line
Quoted 1 line

time, which defeats many purposes of using fuse in the first place. You

can

Quoted 4 lines
Quoted 4 lines

read all the gory details in the thread I linked containing my original patch.

Fortunately, this patch (and mine) allows you to use

default_permissions

Quoted 11 lines
Quoted 4 lines

*and* have acl support, meaning permission checks will be performed in kernel for each path component. The extra benefit is that it allows any backing filesystem which supports xattrs to support acls, provided your fuse server has unencumbered privileges for that filesystem.

But if we're going through the posix acl xattr handlers then the acls will be cached. What I worry about is that somehow we could end up in a situation where the cached acls don't match those that the filesystem is using, and userspace sees one thing in response to getfacl but something different is being enforced.

This isn't any different from the unix permissions. This is the price you pay for caching any attributes. If it's a problem, the server can reduce the cache timeout, or eliminate caching by setting the timeout to 0.

Regards,
Michael Theall

Quoted 30 lines

Thanks,
Seth

Quoted 27 lines

Regards,
Michael Theall

Michael j Theall/Houston/IBM@IBMUS wrote on 06/29/2016 02:24:14 PM:

Quoted 7 lines

From: Michael j Theall/Houston/IBM@IBMUS
To: Seth Forshee <[email protected]>
Cc: [email protected], [email protected], Miklos-QbrlkLqkJHK1JOh/[email protected], Szeredi <[email protected]>
Date: 06/29/2016 02:37 PM
Subject: Re: [fuse-devel] [RFC] fuse: Support posix ACLs

Quoted 8 lines

Going by the patch I posted a couple of years ago: https://
sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to- speed on the kernel so I'm not sure if you actually need to implement

both.

Quoted 5 lines

Regards,
Michael Theall

Seth Forshee <[email protected]> wrote on 06/29/2016

02:07:31

Quoted 11 lines
Quoted 11 lines

PM:

Quoted 10 lines
Quoted 10 lines

From: Seth Forshee <[email protected]>
To: [email protected], [email protected]
Cc: "Eric W. Biederman" <ebiederm-aS9lmoZGLiVWk0Htik3J/[email protected]>, Miklos Szeredi <[email protected]>
Date: 06/29/2016 02:08 PM
Subject: [fuse-devel] [RFC] fuse: Support posix ACLs

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL

support

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

to fuse as this will allow for a cleaner implementation overall.

Below

Quoted 6 lines
Quoted 6 lines
Quoted 4 lines
Quoted 4 lines

is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace

side

Quoted 1 line
Quoted 1 line

when default_permissions is not used (though I'm not seeing how

that

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

works). Will these changes conflict with that support, and if how

do we

Quoted 33 lines
Quoted 33 lines
Quoted 3 lines
Quoted 3 lines

avoid those conflicts?

Are there any places where we need to invalidate cached ACLs that

aren't

Quoted 29 lines
Quoted 11 lines

covered in the patch?

Thanks,
Seth

fs/fuse/dir.c +0 -0
index 8466e122ee62..2f53b0491159 100644

fs/fuse/dir.c line 19

Quoted 7 lines
fs/fuse/dir.c +2 -0
@@ -13,6 +13,8 @@
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct

dir_context *ctx)

Quoted 10 lines
fs/fuse/dir.c +1 -0
 {
@@ -1061,6 +1063,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);
 }
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

fs/fuse/dir.c static int fuse_getattr(struct vfsmount *mnt, · line 1723

Quoted 6 lines
fs/fuse/dir.c +0 -1
@@ -1719,9 +1722,8 @@ static int fuse_getattr(struct vfsmount *mnt,
 struct dentry *entry,
    return fuse_update_attributes(inode, stat, NULL, NULL);
 }
 
-static int fuse_setxattr(struct dentry *unused, struct inode

*inode,

Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
fs/fuse/dir.c +2 -2
-          const char *name, const void *value,
-          size_t size, int flags)
+static 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);
fs/fuse/dir.c +0 -0
@@ -1755,8 +1757,8 @@ static int fuse_setxattr(struct dentry
 *unused, struct inode *inode,
    return err;
 }
 
Quoted 4 lines
Quoted 4 lines
Quoted 1 line

fs/fuse/dir.c static int fuse_setxattr(struct dentry · line 1759

Quoted 1 line
fs/fuse/dir.c +0 -1
-static ssize_t fuse_getxattr(struct dentry *entry, struct inode

*inode,

Quoted 2 lines
Quoted 2 lines
fs/fuse/dir.c +1 -1
-              const char *name, void *value, size_t size)
+static ssize_t fuse_getxattr(struct inode *inode, const char

*name,

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

fs/fuse/dir.c static ssize_t fuse_listxattr(struct dentry · line 1842

Quoted 10 lines
fs/fuse/dir.c +1 -0
+              void *value, size_t size)
 {
    struct fuse_conn *fc = get_fuse_conn(inode);
    FUSE_ARGS(args);
fs/fuse/dir.c +0 -1
@@ -1838,9 +1840,8 @@ static ssize_t fuse_listxattr(struct dentry
 *entry, char *list, size_t size)
    return ret;
 }
 
-static int fuse_removexattr(struct dentry *entry, const char

*name)

Quoted 44 lines
Quoted 44 lines
Quoted 44 lines
Quoted 44 lines
fs/fuse/dir.c +1 -1
+static int fuse_removexattr(struct inode *inode, const char *name)
 {
-   struct inode *inode = d_inode(entry);
    struct fuse_conn *fc = get_fuse_conn(inode);
    FUSE_ARGS(args);
    int err;
fs/fuse/dir.c +33 -0
@@ -1865,6 +1866,138 @@ static int fuse_removexattr(struct dentry
 *entry, const char *name)
    return err;
 }
 
+static int fuse_xattr_get(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, void *value, size_t size)
+{
+   return fuse_getxattr(inode, name, value, size);
+}
+
+static int fuse_xattr_set(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, const void *value, size_t size,
+           int flags)
+{
+   if (!value)
+      return fuse_removexattr(inode, name);
+
+   return fuse_setxattr(inode, name, value, size, flags);
+}
+
+static const struct xattr_handler fuse_xattr_handler = {
+   .prefix   = "",
+   .get   = fuse_xattr_get,
+   .set   = fuse_xattr_set,
+};
+
+#ifndef CONFIG_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,
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

fs/fuse/dir.c static int fuse_removexattr(struct dentry *entry, const char *name)

Quoted 8 lines
fs/fuse/dir.c +8 -0
+            struct dentry *dentry, struct inode *inode,
+            const char *name, const void *value, size_t size,
+            int flags)
+{
+   return -EOPNOTSUPP;
+}
+
+static const struct xattr_handler fuse_xattr_acl_access_handler =

{

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

fs/fuse/dir.c static int fuse_removexattr(struct dentry *entry, const char *name)

Quoted 6 lines
fs/fuse/dir.c +6 -0
+   .name   = XATTR_NAME_POSIX_ACL_ACCESS,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+
+static const struct xattr_handler fuse_xattr_acl_default_handler =

{

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
fs/fuse/dir.c +10 -0
+   .name   = XATTR_NAME_POSIX_ACL_DEFAULT,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+#endif /* CONFIG_POSIX_ACL */
+
+const struct xattr_handler *fuse_xattr_handlers[] = {
+#ifdef CONFIG_FS_POSIX_ACL
+   &posix_acl_access_xattr_handler,
+   &posix_acl_default_xattr_handler,
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

fs/fuse/dir.c static int fuse_removexattr(struct dentry *entry, const char *name)

Quoted 8 lines
fs/fuse/dir.c +8 -0
+#else
+   &fuse_xattr_acl_access_handler,
+   &fuse_xattr_acl_default_handler,
+#endif
+   &fuse_xattr_handler,
+};
+
+static struct posix_acl *fuse_get_acl(struct inode *inode, int

type)

Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
fs/fuse/dir.c +14 -0
+{
+   int size;
+   const char *name;
+   void *value = NULL;
+   struct posix_acl *acl;
+
+   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);
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

fs/fuse/dir.c static int fuse_removexattr(struct dentry *entry, const char *name)

Quoted 8 lines
fs/fuse/dir.c +8 -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(inode->i_sb->s_user_ns, value,

size);

Quoted 82 lines
Quoted 82 lines
Quoted 82 lines

fs/fuse/dir.c static int fuse_removexattr(struct dentry *entry, const char *name)

Quoted 11 lines
fs/fuse/dir.c +11 -0
+   } else if ((size == 0) || (size == -ENODATA)) {
+      acl = NULL;
+   } else {
+      acl = ERR_PTR(size);
+   }
+   kfree(value);
+
+   return acl;
+}
+
+static int fuse_set_acl(struct inode *inode, struct posix_acl

*acl,int type)

Quoted 70 lines
fs/fuse/dir.c +34 -0
+{
+   const char *name;
+   int ret;
+
+   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 -EINVAL;
+
+   if (acl) {
+      struct user_namespace *s_user_ns = inode->i_sb->s_user_ns;
+      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(s_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;
+}
+
 static const struct inode_operations fuse_dir_inode_operations = {
    .lookup      = fuse_lookup,

.mkdir = fuse_mkdir,

fs/fuse/dir.c +5 -3
@@ -1879,10 +2012,12 @@ static const struct inode_operations
 fuse_dir_inode_operations = {
    .mknod      = fuse_mknod,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct file_operations fuse_dir_operations = {

fs/fuse/dir.c +5 -3
@@ -1900,10 +2035,12 @@ static const struct inode_operations
 fuse_common_inode_operations = {
    .setattr   = fuse_setattr,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

static const struct inode_operations fuse_symlink_inode_operations

= {

Quoted 66 lines
Quoted 66 lines
Quoted 29 lines
Quoted 29 lines
fs/fuse/dir.c +5 -3
@@ -1911,10 +2048,12 @@ static const struct inode_operations
 fuse_symlink_inode_operations = {
    .get_link   = fuse_get_link,
    .readlink   = generic_readlink,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

void fuse_init_common(struct inode *inode)

fs/fuse/fuse_i.h +1 -0
index 9f4c3c82edd6..02c08796051e 100644
@@ -23,6 +23,7 @@
 #include <linux/poll.h>
 #include <linux/workqueue.h>
 #include <linux/kref.h>
+#include <linux/xattr.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
 
Quoted 1 line

fs/fuse/fuse_i.h extern const struct file_operations

Quoted 1 line
fs/fuse/fuse_i.h +0 -0
@@ -693,6 +694,8 @@ extern const struct file_operations

fuse_dev_operations;

Quoted 35 lines
Quoted 35 lines
fs/fuse/fuse_i.h +2 -0
 extern const struct dentry_operations fuse_dentry_operations;
 
+extern const struct xattr_handler *fuse_xattr_handlers[];
+
 /**
  * Inode to nodeid comparison.
  */
fs/fuse/inode.c +2 -0
index 254f1944ee98..9c1519c269bb 100644
@@ -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 <[email protected]>");
 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)
fs/fuse/inode.c +1 -0
@@ -1066,6 +1068,7 @@ static int fuse_fill_super(struct super_block
 *sb, void *data, int silent)
    }
    sb->s_magic = FUSE_SUPER_MAGIC;
    sb->s_op = &fuse_super_operations;
+   sb->s_xattr = fuse_xattr_handlers;
Quoted 9 lines
Quoted 8 lines
Quoted 7 lines
Quoted 6 lines
fs/fuse/inode.c +0 -0
    sb->s_maxbytes = MAX_LFS_FILESIZE;
    sb->s_time_gran = 1;
    sb->s_export_op = &fuse_export_operations;

------------------------------------------------------------------------------

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in

San

Quoted 3 lines
Quoted 3 lines
Quoted 1 line
Quoted 1 line

Francisco, CA to explore cutting-edge tech and listen to tech

luminaries

Quoted 1 line
Quoted 1 line

present their vision of the future. This family event has something

for

Quoted 10 lines
Quoted 9 lines
Quoted 8 lines
Quoted 7 lines

everyone, including kids. Get more information and register today.
http://sdm.link/attshape
--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

------------------------------------------------------------------------------

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in

San

Quoted 1 line
Quoted 1 line
Quoted 1 line

Francisco, CA to explore cutting-edge tech and listen to tech

luminaries

Quoted 1 line
Quoted 1 line
Quoted 1 line

present their vision of the future. This family event has something

for

Quoted 6 lines
Quoted 5 lines
Quoted 5 lines

everyone, including kids. Get more information and register today.
http://sdm.link/attshape--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

------------------------------------------------------------------------------
Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape

Signature · 2 lines
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/lists/listinfo/fuse-devel
#6 lore raw
In reply to Michael j Theall
Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

The fact that MS_POSIXACL is set indicates that fuse supports posix acls. Have posix acls never worked then?

Eric

A message that is not in the archive (sent off-list or not yet received): [email protected]

#8 lore raw

Hi,

MS_POSIXACL is only set so that fuse sends doesn't mask the mode sent with open/mkdir/mknod. This is necessary to properly implement POSIX ACLs, along with the umask value which is also sent with these requests.

Regards,
Michael Theall

ebiederm-aS9lmoZGLiVWk0Htik3J/[email protected] (Eric W. Biederman) wrote on 06/29/2016 03:18:24 PM:

Quoted 20 lines

From: ebiederm-aS9lmoZGLiVWk0Htik3J/[email protected] (Eric W. Biederman)
To: Michael j Theall/Houston/IBM@IBMUS
Cc: Seth Forshee <[email protected]>, fuse- [email protected], [email protected], Miklos Szeredi <[email protected]>
Date: 06/29/2016 03:30 PM
Subject: Re: [fuse-devel] [RFC] fuse: Support posix ACLs

Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Quoted 9 lines

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

The fact that MS_POSIXACL is set indicates that fuse supports posix acls. Have posix acls never worked then?

Eric

------------------------------------------------------------------------------
Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape

Signature · 2 lines
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/lists/listinfo/fuse-devel
#9 lore raw
In reply to Eric W. Biederman
Quoted 12 lines · [email protected] (Eric W. Biederman) wrote:
Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Quoted 3 lines

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

Using a former implementation of ACLs within fuse at the kernel level, I got the result below.
File systems expect consistency.

# Using the low level interface of fuse, with use of ACLs

# intended to be checked in the kernel, but not related to
# access control
rm -rf trydir
mkdir trydir
echo file > trydir/file
ls -l trydir/file
setfacl -m 'u::7,g::5,o::5' trydir/file
ls -l trydir/file
sleep 1
ls -l trydir/file

-rw-r--r-- 1 root root 5 2009-09-12 12:02 trydir/file -rw-r--r-- 1 root root 5 2009-09-12 12:02 trydir/file -rwxr-xr-x 1 root root 5 2009-09-12 12:02 trydir/file

Jean-Pierre

#10 lore raw
In reply to Eric W. Biederman
Quoted 12 lines · Eric W. Biederman
Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Quoted 3 lines

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

My first blush opinion is that the kernel should take care of this, not the filesystems. Then a fuse filesystem which supports xattrs gets acl support for free. Otherwise if a filesystem supports xattrs but not acls internally, we have no way of knowing that in the kernel and they get out of sync.

However if some filesystems are already doing this internally then we have redundancy. Presumably this would be harmless aside from the wasted effort.

Seth

#11 lore raw
Quoted 23 lines · Seth Forshee
Quoted 12 lines · Eric W. Biederman
Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Quoted 3 lines

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

My first blush opinion is that the kernel should take care of this, not the filesystems. Then a fuse filesystem which supports xattrs gets acl support for free. Otherwise if a filesystem supports xattrs but not acls internally, we have no way of knowing that in the kernel and they get out of sync.

Quoted 3 lines

However if some filesystems are already doing this internally then we have redundancy. Presumably this would be harmless aside from the wasted effort.

Which means that in set_acl we need to something like:

	if (type == ACL_TYPE_ACCESS) {
        	struct iattr attr;
                attr.ia_valid = ATTR_MODE;
                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 (attr.ia_mode != inode->i_mode) {
			ret = fuse_do_setattr(inode, &attr, NULL);
	                if (ret < 0)
        	        	return ret;
                }
        }

In fuse_setattr should wind up looking something like:

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)
		ret = fuse_do_setattr(inode, attr, attr->ia_file);
	else
		ret = fuse_do_setattr(inode, attr, NULL);

	if (ret == 0 && attr->ia_valid & ATTR_MODE)
        	ret = posix_acl_chmod(inode, inode->i_mode);
	return ret;
}

That should be enough to keep everything in sync with the existing fuse protocol. And then fuse filesystems won't have to care in general about the contents of acls (unless they choose to care).

Eric

#12 lore raw
Quoted 67 lines · Eric W. Biederman
Quoted 23 lines · Seth Forshee
Quoted 12 lines · Eric W. Biederman
Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Quoted 3 lines

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

My first blush opinion is that the kernel should take care of this, not the filesystems. Then a fuse filesystem which supports xattrs gets acl support for free. Otherwise if a filesystem supports xattrs but not acls internally, we have no way of knowing that in the kernel and they get out of sync.

Quoted 3 lines

However if some filesystems are already doing this internally then we have redundancy. Presumably this would be harmless aside from the wasted effort.

Which means that in set_acl we need to something like:

	if (type == ACL_TYPE_ACCESS) {
        	struct iattr attr;
                attr.ia_valid = ATTR_MODE;
                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 (attr.ia_mode != inode->i_mode) {
			ret = fuse_do_setattr(inode, &attr, NULL);
	                if (ret < 0)
        	        	return ret;
                }
        }

In fuse_setattr should wind up looking something like:

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)
		ret = fuse_do_setattr(inode, attr, attr->ia_file);
	else
		ret = fuse_do_setattr(inode, attr, NULL);

	if (ret == 0 && attr->ia_valid & ATTR_MODE)
        	ret = posix_acl_chmod(inode, inode->i_mode);
	return ret;
}
Quoted 3 lines

That should be enough to keep everything in sync with the existing fuse protocol. And then fuse filesystems won't have to care in general about the contents of acls (unless they choose to care).

Yes, I've already written pretty much the same code and am attempting to test it. The problem I'm having is finding a good filesystem to test with. fusexmp works but is probably unfair as the underlying filesystem is handling the acls and updating the mode. I haven't found any filesystem yet that fully supports xattrs but doesn't do something special with the posix acl xattrs.

Can anyone suggest a good filesystem for me to test with?

Thanks,
Seth

#13 lore raw
Quoted 8 lines · Seth Forshee

Yes, I've already written pretty much the same code and am attempting to test it. The problem I'm having is finding a good filesystem to test with. fusexmp works but is probably unfair as the underlying filesystem is handling the acls and updating the mode. I haven't found any filesystem yet that fully supports xattrs but doesn't do something special with the posix acl xattrs.

Can anyone suggest a good filesystem for me to test with?

S3QL should do it with a small patch - at the moment it deliberately returns an error when the kernel tries to acces system.posix_acl*. This is done specifically so that the kernel doesn't think that ACLs are supported when they actually aren't (because of the missing synchronization with the permission bits).

Download from https://bitbucket.org/nikratio/s3ql/ and comment out the check in src/s3ql/fs.py:getxattr():

# http://code.google.com/p/s3ql/issues/detail?id=385
elif name in (b'system.posix_acl_access',
b'system.posix_acl_default'):
raise FUSEError(ACL_ERRNO)

Best,
-Nikolaus

Signature · 4 lines
GPG encrypted emails preferred. Key id: 0xD113FCAC3C4E599F
Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F

             »Time flies like an arrow, fruit flies like a Banana.«
#14 lore raw
In reply to Eric W. Biederman
Quoted 12 lines · On Jun 29 2016, [email protected] (Eric W. Biederman) wrote:
Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Quoted 3 lines

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

A FUSE file system should be able to support ACLs without requiring the file system process to do more than support extended attributes. I believe this means that the kernel should keep i_mode and the ACLs in sync -- it would be a rather bug prone and redundant for each FUSE file system to implement its own parser for format in which the ACLs are stored in xattrs.

Best,
-Nikolaus

Signature · 4 lines
GPG encrypted emails preferred. Key id: 0xD113FCAC3C4E599F
Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F

             »Time flies like an arrow, fruit flies like a Banana.«
#15 lore raw
Quoted 17 lines · Nikolaus Rath
Quoted 12 lines · On Jun 29 2016, [email protected] (Eric W. Biederman) wrote:
Quoted 9 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated. The other major difference is that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

Quoted 3 lines

That makes an interesting question. Is it desirable to keep inode->i_mode in sync with the posix acls in fuse or should a filesystem that supports posix acls worry about that?

Quoted 6 lines

A FUSE file system should be able to support ACLs without requiring the file system process to do more than support extended attributes. I believe this means that the kernel should keep i_mode and the ACLs in sync -- it would be a rather bug prone and redundant for each FUSE file system to implement its own parser for format in which the ACLs are stored in xattrs.

The most recent patch I posted keeps them in sync.

Thanks,
Seth

#16 lore raw
In reply to Michael j Theall
Quoted 6 lines · Michael j Theall

Going by the patch I posted a couple of years ago:
https://sourceforge.net/p/fuse/mailman/message/33033653/

The only hole I see in your patch is that in setattr() you are not updating the cached acl if the ATTR_MODE is updated.

Okay, thanks.

Quoted 4 lines

The other major difference is
that my version uses the get_acl/set_acl inode operations but you use that plus the xattr handlers. I'm not up-to-speed on the kernel so I'm not sure if you actually need to implement both.

The xattr handlers use the get_acl/set_acl callbacks. Without using the xattr handlers acl xattr reads won't be pulled from the acl cache, for one thing.

Thanks,
Seth

Quoted 350 lines

Regards,
Michael Theall

Seth Forshee <[email protected]> wrote on 06/29/2016 02:07:31 PM:

Quoted 30 lines

From: Seth Forshee <[email protected]>
To: [email protected], [email protected]
Cc: "Eric W. Biederman" <[email protected]>, Miklos Szeredi <[email protected]>
Date: 06/29/2016 02:08 PM
Subject: [fuse-devel] [RFC] fuse: Support posix ACLs

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

Are there any places where we need to invalidate cached ACLs that aren't covered in the patch?

Thanks,
Seth

fs/fuse/dir.c +0 -0
index 8466e122ee62..2f53b0491159 100644

fs/fuse/dir.c line 19

Quoted 7 lines
fs/fuse/dir.c +2 -0
@@ -13,6 +13,8 @@
 #include <linux/sched.h>
 #include <linux/namei.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 
 static bool fuse_use_readdirplus(struct inode *dir, struct dir_context

*ctx)

Quoted 140 lines
fs/fuse/dir.c +3 -3
 {
@@ -1061,6 +1063,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);
 }
@@ -1719,9 +1722,8 @@ static int fuse_getattr(struct vfsmount *mnt,
 struct dentry *entry,
    return fuse_update_attributes(inode, stat, NULL, NULL);
 }
 
-static int fuse_setxattr(struct dentry *unused, struct inode *inode,
-          const char *name, const void *value,
-          size_t size, int flags)
+static 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);
fs/fuse/dir.c +2 -2
@@ -1755,8 +1757,8 @@ static int fuse_setxattr(struct dentry
 *unused, struct inode *inode,
    return err;
 }
 
-static ssize_t fuse_getxattr(struct dentry *entry, struct inode *inode,
-              const char *name, void *value, size_t size)
+static 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);
fs/fuse/dir.c +1 -2
@@ -1838,9 +1840,8 @@ static ssize_t fuse_listxattr(struct dentry
 *entry, char *list, size_t size)
    return ret;
 }
 
-static int fuse_removexattr(struct dentry *entry, const char *name)
+static int fuse_removexattr(struct inode *inode, const char *name)
 {
-   struct inode *inode = d_inode(entry);
    struct fuse_conn *fc = get_fuse_conn(inode);
    FUSE_ARGS(args);
    int err;
fs/fuse/dir.c +88 -0
@@ -1865,6 +1866,138 @@ static int fuse_removexattr(struct dentry
 *entry, const char *name)
    return err;
 }
 
+static int fuse_xattr_get(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, void *value, size_t size)
+{
+   return fuse_getxattr(inode, name, value, size);
+}
+
+static int fuse_xattr_set(const struct xattr_handler *handler,
+           struct dentry *dentry, struct inode *inode,
+           const char *name, const void *value, size_t size,
+           int flags)
+{
+   if (!value)
+      return fuse_removexattr(inode, name);
+
+   return fuse_setxattr(inode, name, value, size, flags);
+}
+
+static const struct xattr_handler fuse_xattr_handler = {
+   .prefix   = "",
+   .get   = fuse_xattr_get,
+   .set   = fuse_xattr_set,
+};
+
+#ifndef CONFIG_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;
+}
+
+static const struct xattr_handler fuse_xattr_acl_access_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_ACCESS,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+
+static const struct xattr_handler fuse_xattr_acl_default_handler = {
+   .name   = XATTR_NAME_POSIX_ACL_DEFAULT,
+   .get   = fuse_xattr_acl_get,
+   .set   = fuse_xattr_acl_set,
+};
+#endif /* CONFIG_POSIX_ACL */
+
+const struct xattr_handler *fuse_xattr_handlers[] = {
+#ifdef CONFIG_FS_POSIX_ACL
+   &posix_acl_access_xattr_handler,
+   &posix_acl_default_xattr_handler,
+#else
+   &fuse_xattr_acl_access_handler,
+   &fuse_xattr_acl_default_handler,
+#endif
+   &fuse_xattr_handler,
+};
+
+static struct posix_acl *fuse_get_acl(struct inode *inode, int type)
+{
+   int size;
+   const char *name;
+   void *value = NULL;
+   struct posix_acl *acl;
+
+   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(inode->i_sb->s_user_ns, value, size);
+   } else if ((size == 0) || (size == -ENODATA)) {

fs/fuse/dir.c static int fuse_removexattr(struct dentry · line 1967

Quoted 10 lines
fs/fuse/dir.c +10 -0
+      acl = NULL;
+   } else {
+      acl = ERR_PTR(size);
+   }
+   kfree(value);
+
+   return acl;
+}
+
+static int fuse_set_acl(struct inode *inode, struct posix_acl *acl,int

type)

fs/fuse/fuse_i.h extern const struct file_operations

Quoted 101 lines
fs/fuse/dir.c +34 -0
+{
+   const char *name;
+   int ret;
+
+   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 -EINVAL;
+
+   if (acl) {
+      struct user_namespace *s_user_ns = inode->i_sb->s_user_ns;
+      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(s_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;
+}
+
 static const struct inode_operations fuse_dir_inode_operations = {
    .lookup      = fuse_lookup,

.mkdir = fuse_mkdir,

fs/fuse/dir.c +5 -3
@@ -1879,10 +2012,12 @@ static const struct inode_operations
 fuse_dir_inode_operations = {
    .mknod      = fuse_mknod,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct file_operations fuse_dir_operations = {

fs/fuse/dir.c +5 -3
@@ -1900,10 +2035,12 @@ static const struct inode_operations
 fuse_common_inode_operations = {
    .setattr   = fuse_setattr,
    .permission   = fuse_permission,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

static const struct inode_operations fuse_symlink_inode_operations = {

fs/fuse/dir.c +5 -3
@@ -1911,10 +2048,12 @@ static const struct inode_operations
 fuse_symlink_inode_operations = {
    .get_link   = fuse_get_link,
    .readlink   = generic_readlink,
    .getattr   = fuse_getattr,
-   .setxattr   = fuse_setxattr,
-   .getxattr   = fuse_getxattr,
+   .setxattr   = generic_setxattr,
+   .getxattr   = generic_getxattr,
    .listxattr   = fuse_listxattr,
-   .removexattr   = fuse_removexattr,
+   .removexattr   = generic_removexattr,
+   .get_acl   = fuse_get_acl,
+   .set_acl   = fuse_set_acl,
 };
 

void fuse_init_common(struct inode *inode)

fs/fuse/fuse_i.h +1 -0
index 9f4c3c82edd6..02c08796051e 100644
@@ -23,6 +23,7 @@
 #include <linux/poll.h>
 #include <linux/workqueue.h>
 #include <linux/kref.h>
+#include <linux/xattr.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
 
@@ -693,6 +694,8 @@ extern const struct file_operations

fuse_dev_operations;

Quoted 37 lines
fs/fuse/fuse_i.h +2 -0
 extern const struct dentry_operations fuse_dentry_operations;
 
+extern const struct xattr_handler *fuse_xattr_handlers[];
+
 /**
  * Inode to nodeid comparison.
  */
fs/fuse/inode.c +2 -0
index 254f1944ee98..9c1519c269bb 100644
@@ -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 <[email protected]>");
 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)
fs/fuse/inode.c +1 -0
@@ -1066,6 +1068,7 @@ static int fuse_fill_super(struct super_block
 *sb, void *data, int silent)
    }
    sb->s_magic = FUSE_SUPER_MAGIC;
    sb->s_op = &fuse_super_operations;
+   sb->s_xattr = fuse_xattr_handlers;
    sb->s_maxbytes = MAX_LFS_FILESIZE;
    sb->s_time_gran = 1;
Quoted 4 lines
    sb->s_export_op = &fuse_export_operations;

------------------------------------------------------------------------------

Quoted 10 lines

Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today.
http://sdm.link/attshape
--
fuse-devel mailing list
To unsubscribe or subscribe, visit https://lists.sourceforge.net/
lists/listinfo/fuse-devel

#17 lore raw
In reply to Seth Forshee
Quoted 3 lines · Seth Forshee

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below

My best wishes go with you.

Quoted 7 lines

is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

ntfs-3g has both variants implemented. When supporting ACLs within the userspace, it does not set default_permissions, and it uses null cache timeouts.

When expecting ACLs supported at the kernel level, it sets default_permissions and it uses non_null cache timeouts.

It sets FUSE_CAP_DONT_MASK in both cases.

I would expect default_permissions to make a clear divide between those conditions, avoiding any conflicts.

Jean-Pierre

#18 lore raw
In reply to Seth Forshee
Quoted 11 lines · Seth Forshee

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

I think as long as the kernel interprets ACLs only if default_permission is used, you should be fine.

Best,
-Nikolaus

Signature · 4 lines
GPG encrypted emails preferred. Key id: 0xD113FCAC3C4E599F
Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F

             »Time flies like an arrow, fruit flies like a Banana.«
#19 lore raw
Quoted 7 lines · Nikolaus Rath
Quoted 6 lines · Seth Forshee

Eric and I are working towards adding support for fuse mounts in non-init user namespaces. Towards that end we'd like to add ACL support to fuse as this will allow for a cleaner implementation overall. Below is an initial patch to support this. I'd like to get some general feedback on this patch and ask a couple of specific questions.

Quoted 7 lines
Quoted 5 lines

There are some indications that fuse supports ACLs on the userspace side when default_permissions is not used (though I'm not seeing how that works). Will these changes conflict with that support, and if how do we avoid those conflicts?

I think as long as the kernel interprets ACLs only if default_permission is used, you should be fine.

With !default_permission fuse never calls generic_permission so the kernel won't enforce the acls regardless. For the purpose of user namespace mounts it's still useful if the kernel intercepts them so that the posix acl layer can do the uid/gid translation before passing it to the filesystem. The xattrs still get sent on to the filesystem, however cached acls if present would be used to satisfy reads of the acl xatts.

Thanks,
Seth