Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fuse: Add posix ACL support

Add a new INIT flag, FUSE_POSIX_ACL, for negotiating ACL support with
userspace. When it is set in the INIT response, ACL support will be
enabled. ACL support also implies "default_permissions".

When ACL support is enabled, the kernel will cache and have responsibility
for enforcing ACLs. ACL xattrs will be passed to userspace, which is
responsible for updating the ACLs in the filesystem, keeping the file mode
in sync, and inheritance of default ACLs when new filesystem nodes are
created.

Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>

authored by

Seth Forshee and committed by
Miklos Szeredi
60bcc88a 5e940c1d

+155 -7
+1
fs/fuse/Kconfig
··· 1 1 config FUSE_FS 2 2 tristate "FUSE (Filesystem in Userspace) support" 3 + select FS_POSIX_ACL 3 4 help 4 5 With FUSE it is possible to implement a fully functional filesystem 5 6 in a userspace program.
+1 -1
fs/fuse/Makefile
··· 5 5 obj-$(CONFIG_FUSE_FS) += fuse.o 6 6 obj-$(CONFIG_CUSE) += cuse.o 7 7 8 - fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o 8 + fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o
+99
fs/fuse/acl.c
··· 1 + /* 2 + * FUSE: Filesystem in Userspace 3 + * Copyright (C) 2016 Canonical Ltd. <seth.forshee@canonical.com> 4 + * 5 + * This program can be distributed under the terms of the GNU GPL. 6 + * See the file COPYING. 7 + */ 8 + 9 + #include "fuse_i.h" 10 + 11 + #include <linux/posix_acl.h> 12 + #include <linux/posix_acl_xattr.h> 13 + 14 + struct posix_acl *fuse_get_acl(struct inode *inode, int type) 15 + { 16 + struct fuse_conn *fc = get_fuse_conn(inode); 17 + int size; 18 + const char *name; 19 + void *value = NULL; 20 + struct posix_acl *acl; 21 + 22 + if (!fc->posix_acl || fc->no_getxattr) 23 + return NULL; 24 + 25 + if (type == ACL_TYPE_ACCESS) 26 + name = XATTR_NAME_POSIX_ACL_ACCESS; 27 + else if (type == ACL_TYPE_DEFAULT) 28 + name = XATTR_NAME_POSIX_ACL_DEFAULT; 29 + else 30 + return ERR_PTR(-EOPNOTSUPP); 31 + 32 + value = kmalloc(PAGE_SIZE, GFP_KERNEL); 33 + if (!value) 34 + return ERR_PTR(-ENOMEM); 35 + size = fuse_getxattr(inode, name, value, PAGE_SIZE); 36 + if (size > 0) 37 + acl = posix_acl_from_xattr(&init_user_ns, value, size); 38 + else if ((size == 0) || (size == -ENODATA) || 39 + (size == -EOPNOTSUPP && fc->no_getxattr)) 40 + acl = NULL; 41 + else if (size == -ERANGE) 42 + acl = ERR_PTR(-E2BIG); 43 + else 44 + acl = ERR_PTR(size); 45 + 46 + kfree(value); 47 + return acl; 48 + } 49 + 50 + int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type) 51 + { 52 + struct fuse_conn *fc = get_fuse_conn(inode); 53 + const char *name; 54 + int ret; 55 + 56 + if (!fc->posix_acl || fc->no_setxattr) 57 + return -EOPNOTSUPP; 58 + 59 + if (type == ACL_TYPE_ACCESS) 60 + name = XATTR_NAME_POSIX_ACL_ACCESS; 61 + else if (type == ACL_TYPE_DEFAULT) 62 + name = XATTR_NAME_POSIX_ACL_DEFAULT; 63 + else 64 + return -EINVAL; 65 + 66 + if (acl) { 67 + /* 68 + * Fuse userspace is responsible for updating access 69 + * permissions in the inode, if needed. fuse_setxattr 70 + * invalidates the inode attributes, which will force 71 + * them to be refreshed the next time they are used, 72 + * and it also updates i_ctime. 73 + */ 74 + size_t size = posix_acl_xattr_size(acl->a_count); 75 + void *value; 76 + 77 + if (size > PAGE_SIZE) 78 + return -E2BIG; 79 + 80 + value = kmalloc(size, GFP_KERNEL); 81 + if (!value) 82 + return -ENOMEM; 83 + 84 + ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); 85 + if (ret < 0) { 86 + kfree(value); 87 + return ret; 88 + } 89 + 90 + ret = fuse_setxattr(inode, name, value, size, 0); 91 + kfree(value); 92 + } else { 93 + ret = fuse_removexattr(inode, name); 94 + } 95 + forget_all_cached_acls(inode); 96 + fuse_invalidate_attr(inode); 97 + 98 + return ret; 99 + }
+16
fs/fuse/dir.c
··· 14 14 #include <linux/namei.h> 15 15 #include <linux/slab.h> 16 16 #include <linux/xattr.h> 17 + #include <linux/posix_acl.h> 17 18 18 19 static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx) 19 20 { ··· 245 244 if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) 246 245 goto invalid; 247 246 247 + forget_all_cached_acls(inode); 248 248 fuse_change_attributes(inode, &outarg.attr, 249 249 entry_attr_timeout(&outarg), 250 250 attr_version); ··· 920 918 921 919 if (time_before64(fi->i_time, get_jiffies_64())) { 922 920 r = true; 921 + forget_all_cached_acls(inode); 923 922 err = fuse_do_getattr(inode, stat, file); 924 923 } else { 925 924 r = false; ··· 1068 1065 if (mask & MAY_NOT_BLOCK) 1069 1066 return -ECHILD; 1070 1067 1068 + forget_all_cached_acls(inode); 1071 1069 return fuse_do_getattr(inode, NULL, NULL); 1072 1070 } 1073 1071 ··· 1238 1234 fi->nlookup++; 1239 1235 spin_unlock(&fc->lock); 1240 1236 1237 + forget_all_cached_acls(inode); 1241 1238 fuse_change_attributes(inode, &o->attr, 1242 1239 entry_attr_timeout(o), 1243 1240 attr_version); ··· 1753 1748 1754 1749 ret = fuse_do_setattr(inode, attr, file); 1755 1750 if (!ret) { 1751 + /* 1752 + * If filesystem supports acls it may have updated acl xattrs in 1753 + * the filesystem, so forget cached acls for the inode. 1754 + */ 1755 + if (fc->posix_acl) 1756 + forget_all_cached_acls(inode); 1757 + 1756 1758 /* Directory mode changed, may need to revalidate access */ 1757 1759 if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE)) 1758 1760 fuse_invalidate_entry_cache(entry); ··· 1797 1785 .getxattr = generic_getxattr, 1798 1786 .listxattr = fuse_listxattr, 1799 1787 .removexattr = generic_removexattr, 1788 + .get_acl = fuse_get_acl, 1789 + .set_acl = fuse_set_acl, 1800 1790 }; 1801 1791 1802 1792 static const struct file_operations fuse_dir_operations = { ··· 1820 1806 .getxattr = generic_getxattr, 1821 1807 .listxattr = fuse_listxattr, 1822 1808 .removexattr = generic_removexattr, 1809 + .get_acl = fuse_get_acl, 1810 + .set_acl = fuse_set_acl, 1823 1811 }; 1824 1812 1825 1813 static const struct inode_operations fuse_symlink_inode_operations = {
+14
fs/fuse/fuse_i.h
··· 23 23 #include <linux/poll.h> 24 24 #include <linux/workqueue.h> 25 25 #include <linux/kref.h> 26 + #include <linux/xattr.h> 26 27 27 28 /** Max number of pages that can be used in a single read request */ 28 29 #define FUSE_MAX_PAGES_PER_REQ 32 ··· 628 627 /** Is lseek not implemented by fs? */ 629 628 unsigned no_lseek:1; 630 629 630 + /** Does the filesystem support posix acls? */ 631 + unsigned posix_acl:1; 632 + 631 633 /** The number of requests waiting for completion */ 632 634 atomic_t num_waiting; 633 635 ··· 975 971 void fuse_unlock_inode(struct inode *inode); 976 972 void fuse_lock_inode(struct inode *inode); 977 973 974 + int fuse_setxattr(struct inode *inode, const char *name, const void *value, 975 + size_t size, int flags); 976 + ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value, 977 + size_t size); 978 978 ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size); 979 + int fuse_removexattr(struct inode *inode, const char *name); 979 980 extern const struct xattr_handler *fuse_xattr_handlers[]; 981 + extern const struct xattr_handler *fuse_acl_xattr_handlers[]; 982 + 983 + struct posix_acl; 984 + struct posix_acl *fuse_get_acl(struct inode *inode, int type); 985 + int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type); 980 986 981 987 #endif /* _FS_FUSE_I_H */
+8 -1
fs/fuse/inode.c
··· 20 20 #include <linux/random.h> 21 21 #include <linux/sched.h> 22 22 #include <linux/exportfs.h> 23 + #include <linux/posix_acl.h> 23 24 24 25 MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); 25 26 MODULE_DESCRIPTION("Filesystem in Userspace"); ··· 341 340 return -ENOENT; 342 341 343 342 fuse_invalidate_attr(inode); 343 + forget_all_cached_acls(inode); 344 344 if (offset >= 0) { 345 345 pg_start = offset >> PAGE_SHIFT; 346 346 if (len <= 0) ··· 916 914 fc->handle_killpriv = 1; 917 915 if (arg->time_gran && arg->time_gran <= 1000000000) 918 916 fc->sb->s_time_gran = arg->time_gran; 917 + if ((arg->flags & FUSE_POSIX_ACL)) { 918 + fc->flags |= FUSE_DEFAULT_PERMISSIONS; 919 + fc->posix_acl = 1; 920 + fc->sb->s_xattr = fuse_acl_xattr_handlers; 921 + } 919 922 } else { 920 923 ra_pages = fc->max_read / PAGE_SIZE; 921 924 fc->no_lock = 1; ··· 950 943 FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | 951 944 FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | 952 945 FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | 953 - FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV; 946 + FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL; 954 947 req->in.h.opcode = FUSE_INIT; 955 948 req->in.numargs = 1; 956 949 req->in.args[0].size = sizeof(*arg);
+13 -5
fs/fuse/xattr.c
··· 9 9 #include "fuse_i.h" 10 10 11 11 #include <linux/xattr.h> 12 + #include <linux/posix_acl_xattr.h> 12 13 13 - static int fuse_setxattr(struct inode *inode, const char *name, 14 - const void *value, size_t size, int flags) 14 + int fuse_setxattr(struct inode *inode, const char *name, const void *value, 15 + size_t size, int flags) 15 16 { 16 17 struct fuse_conn *fc = get_fuse_conn(inode); 17 18 FUSE_ARGS(args); ··· 46 45 return err; 47 46 } 48 47 49 - static ssize_t fuse_getxattr(struct inode *inode, const char *name, 50 - void *value, size_t size) 48 + ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value, 49 + size_t size) 51 50 { 52 51 struct fuse_conn *fc = get_fuse_conn(inode); 53 52 FUSE_ARGS(args); ··· 148 147 return ret; 149 148 } 150 149 151 - static int fuse_removexattr(struct inode *inode, const char *name) 150 + int fuse_removexattr(struct inode *inode, const char *name) 152 151 { 153 152 struct fuse_conn *fc = get_fuse_conn(inode); 154 153 FUSE_ARGS(args); ··· 199 198 }; 200 199 201 200 const struct xattr_handler *fuse_xattr_handlers[] = { 201 + &fuse_xattr_handler, 202 + NULL 203 + }; 204 + 205 + const struct xattr_handler *fuse_acl_xattr_handlers[] = { 206 + &posix_acl_access_xattr_handler, 207 + &posix_acl_default_xattr_handler, 202 208 &fuse_xattr_handler, 203 209 NULL 204 210 };
+3
include/uapi/linux/fuse.h
··· 111 111 * 112 112 * 7.26 113 113 * - add FUSE_HANDLE_KILLPRIV 114 + * - add FUSE_POSIX_ACL 114 115 */ 115 116 116 117 #ifndef _LINUX_FUSE_H ··· 243 242 * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens 244 243 * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir 245 244 * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc 245 + * FUSE_POSIX_ACL: filesystem supports posix acls 246 246 */ 247 247 #define FUSE_ASYNC_READ (1 << 0) 248 248 #define FUSE_POSIX_LOCKS (1 << 1) ··· 265 263 #define FUSE_NO_OPEN_SUPPORT (1 << 17) 266 264 #define FUSE_PARALLEL_DIROPS (1 << 18) 267 265 #define FUSE_HANDLE_KILLPRIV (1 << 19) 266 + #define FUSE_POSIX_ACL (1 << 20) 268 267 269 268 /** 270 269 * CUSE INIT request/reply flags