summaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorSeth Forshee <seth.forshee@canonical.com>2016-08-29 09:46:37 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2016-10-01 01:32:32 -0400
commit60bcc88ad185d512f5718f2f8dcccb483ea8fb73 (patch)
tree898d528618e9091ed7a9191ee736b57073b8a1ad /fs/fuse
parent5e940c1dd3c1f7561924954eecee956ec277a79b (diff)
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>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/Kconfig1
-rw-r--r--fs/fuse/Makefile2
-rw-r--r--fs/fuse/acl.c99
-rw-r--r--fs/fuse/dir.c16
-rw-r--r--fs/fuse/fuse_i.h14
-rw-r--r--fs/fuse/inode.c9
-rw-r--r--fs/fuse/xattr.c18
7 files changed, 152 insertions, 7 deletions
diff --git a/fs/fuse/Kconfig b/fs/fuse/Kconfig
index 1b2f6c2c3aaf..76f09ce7e5b2 100644
--- a/fs/fuse/Kconfig
+++ b/fs/fuse/Kconfig
@@ -1,5 +1,6 @@
1config FUSE_FS 1config FUSE_FS
2 tristate "FUSE (Filesystem in Userspace) support" 2 tristate "FUSE (Filesystem in Userspace) support"
3 select FS_POSIX_ACL
3 help 4 help
4 With FUSE it is possible to implement a fully functional filesystem 5 With FUSE it is possible to implement a fully functional filesystem
5 in a userspace program. 6 in a userspace program.
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index 448aa27ada00..60da84a86dab 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -5,4 +5,4 @@
5obj-$(CONFIG_FUSE_FS) += fuse.o 5obj-$(CONFIG_FUSE_FS) += fuse.o
6obj-$(CONFIG_CUSE) += cuse.o 6obj-$(CONFIG_CUSE) += cuse.o
7 7
8fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o 8fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o
diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c
new file mode 100644
index 000000000000..ec85765502f1
--- /dev/null
+++ b/fs/fuse/acl.c
@@ -0,0 +1,99 @@
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
14struct 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
50int 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}
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 7cb68b18eb3f..3076f48d86a6 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -14,6 +14,7 @@
14#include <linux/namei.h> 14#include <linux/namei.h>
15#include <linux/slab.h> 15#include <linux/slab.h>
16#include <linux/xattr.h> 16#include <linux/xattr.h>
17#include <linux/posix_acl.h>
17 18
18static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx) 19static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
19{ 20{
@@ -244,6 +245,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
244 if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) 245 if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
245 goto invalid; 246 goto invalid;
246 247
248 forget_all_cached_acls(inode);
247 fuse_change_attributes(inode, &outarg.attr, 249 fuse_change_attributes(inode, &outarg.attr,
248 entry_attr_timeout(&outarg), 250 entry_attr_timeout(&outarg),
249 attr_version); 251 attr_version);
@@ -918,6 +920,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
918 920
919 if (time_before64(fi->i_time, get_jiffies_64())) { 921 if (time_before64(fi->i_time, get_jiffies_64())) {
920 r = true; 922 r = true;
923 forget_all_cached_acls(inode);
921 err = fuse_do_getattr(inode, stat, file); 924 err = fuse_do_getattr(inode, stat, file);
922 } else { 925 } else {
923 r = false; 926 r = false;
@@ -1065,6 +1068,7 @@ static int fuse_perm_getattr(struct inode *inode, int mask)
1065 if (mask & MAY_NOT_BLOCK) 1068 if (mask & MAY_NOT_BLOCK)
1066 return -ECHILD; 1069 return -ECHILD;
1067 1070
1071 forget_all_cached_acls(inode);
1068 return fuse_do_getattr(inode, NULL, NULL); 1072 return fuse_do_getattr(inode, NULL, NULL);
1069} 1073}
1070 1074
@@ -1234,6 +1238,7 @@ retry:
1234 fi->nlookup++; 1238 fi->nlookup++;
1235 spin_unlock(&fc->lock); 1239 spin_unlock(&fc->lock);
1236 1240
1241 forget_all_cached_acls(inode);
1237 fuse_change_attributes(inode, &o->attr, 1242 fuse_change_attributes(inode, &o->attr,
1238 entry_attr_timeout(o), 1243 entry_attr_timeout(o),
1239 attr_version); 1244 attr_version);
@@ -1748,6 +1753,13 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
1748 1753
1749 ret = fuse_do_setattr(inode, attr, file); 1754 ret = fuse_do_setattr(inode, attr, file);
1750 if (!ret) { 1755 if (!ret) {
1756 /*
1757 * If filesystem supports acls it may have updated acl xattrs in
1758 * the filesystem, so forget cached acls for the inode.
1759 */
1760 if (fc->posix_acl)
1761 forget_all_cached_acls(inode);
1762
1751 /* Directory mode changed, may need to revalidate access */ 1763 /* Directory mode changed, may need to revalidate access */
1752 if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE)) 1764 if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE))
1753 fuse_invalidate_entry_cache(entry); 1765 fuse_invalidate_entry_cache(entry);
@@ -1785,6 +1797,8 @@ static const struct inode_operations fuse_dir_inode_operations = {
1785 .getxattr = generic_getxattr, 1797 .getxattr = generic_getxattr,
1786 .listxattr = fuse_listxattr, 1798 .listxattr = fuse_listxattr,
1787 .removexattr = generic_removexattr, 1799 .removexattr = generic_removexattr,
1800 .get_acl = fuse_get_acl,
1801 .set_acl = fuse_set_acl,
1788}; 1802};
1789 1803
1790static const struct file_operations fuse_dir_operations = { 1804static const struct file_operations fuse_dir_operations = {
@@ -1806,6 +1820,8 @@ static const struct inode_operations fuse_common_inode_operations = {
1806 .getxattr = generic_getxattr, 1820 .getxattr = generic_getxattr,
1807 .listxattr = fuse_listxattr, 1821 .listxattr = fuse_listxattr,
1808 .removexattr = generic_removexattr, 1822 .removexattr = generic_removexattr,
1823 .get_acl = fuse_get_acl,
1824 .set_acl = fuse_set_acl,
1809}; 1825};
1810 1826
1811static const struct inode_operations fuse_symlink_inode_operations = { 1827static const struct inode_operations fuse_symlink_inode_operations = {
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 9940a648c985..e8d96ec22533 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -23,6 +23,7 @@
23#include <linux/poll.h> 23#include <linux/poll.h>
24#include <linux/workqueue.h> 24#include <linux/workqueue.h>
25#include <linux/kref.h> 25#include <linux/kref.h>
26#include <linux/xattr.h>
26 27
27/** Max number of pages that can be used in a single read request */ 28/** Max number of pages that can be used in a single read request */
28#define FUSE_MAX_PAGES_PER_REQ 32 29#define FUSE_MAX_PAGES_PER_REQ 32
@@ -627,6 +628,9 @@ struct fuse_conn {
627 /** Is lseek not implemented by fs? */ 628 /** Is lseek not implemented by fs? */
628 unsigned no_lseek:1; 629 unsigned no_lseek:1;
629 630
631 /** Does the filesystem support posix acls? */
632 unsigned posix_acl:1;
633
630 /** The number of requests waiting for completion */ 634 /** The number of requests waiting for completion */
631 atomic_t num_waiting; 635 atomic_t num_waiting;
632 636
@@ -971,7 +975,17 @@ void fuse_set_initialized(struct fuse_conn *fc);
971void fuse_unlock_inode(struct inode *inode); 975void fuse_unlock_inode(struct inode *inode);
972void fuse_lock_inode(struct inode *inode); 976void fuse_lock_inode(struct inode *inode);
973 977
978int fuse_setxattr(struct inode *inode, const char *name, const void *value,
979 size_t size, int flags);
980ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
981 size_t size);
974ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size); 982ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
983int fuse_removexattr(struct inode *inode, const char *name);
975extern const struct xattr_handler *fuse_xattr_handlers[]; 984extern const struct xattr_handler *fuse_xattr_handlers[];
985extern const struct xattr_handler *fuse_acl_xattr_handlers[];
986
987struct posix_acl;
988struct posix_acl *fuse_get_acl(struct inode *inode, int type);
989int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
976 990
977#endif /* _FS_FUSE_I_H */ 991#endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index b965934939cb..16ac9d86c507 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -20,6 +20,7 @@
20#include <linux/random.h> 20#include <linux/random.h>
21#include <linux/sched.h> 21#include <linux/sched.h>
22#include <linux/exportfs.h> 22#include <linux/exportfs.h>
23#include <linux/posix_acl.h>
23 24
24MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); 25MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
25MODULE_DESCRIPTION("Filesystem in Userspace"); 26MODULE_DESCRIPTION("Filesystem in Userspace");
@@ -340,6 +341,7 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
340 return -ENOENT; 341 return -ENOENT;
341 342
342 fuse_invalidate_attr(inode); 343 fuse_invalidate_attr(inode);
344 forget_all_cached_acls(inode);
343 if (offset >= 0) { 345 if (offset >= 0) {
344 pg_start = offset >> PAGE_SHIFT; 346 pg_start = offset >> PAGE_SHIFT;
345 if (len <= 0) 347 if (len <= 0)
@@ -914,6 +916,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
914 fc->handle_killpriv = 1; 916 fc->handle_killpriv = 1;
915 if (arg->time_gran && arg->time_gran <= 1000000000) 917 if (arg->time_gran && arg->time_gran <= 1000000000)
916 fc->sb->s_time_gran = arg->time_gran; 918 fc->sb->s_time_gran = arg->time_gran;
919 if ((arg->flags & FUSE_POSIX_ACL)) {
920 fc->flags |= FUSE_DEFAULT_PERMISSIONS;
921 fc->posix_acl = 1;
922 fc->sb->s_xattr = fuse_acl_xattr_handlers;
923 }
917 } else { 924 } else {
918 ra_pages = fc->max_read / PAGE_SIZE; 925 ra_pages = fc->max_read / PAGE_SIZE;
919 fc->no_lock = 1; 926 fc->no_lock = 1;
@@ -943,7 +950,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
943 FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | 950 FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
944 FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | 951 FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
945 FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | 952 FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
946 FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV; 953 FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL;
947 req->in.h.opcode = FUSE_INIT; 954 req->in.h.opcode = FUSE_INIT;
948 req->in.numargs = 1; 955 req->in.numargs = 1;
949 req->in.args[0].size = sizeof(*arg); 956 req->in.args[0].size = sizeof(*arg);
diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c
index e22980f0a9e2..04b097f29d8a 100644
--- a/fs/fuse/xattr.c
+++ b/fs/fuse/xattr.c
@@ -9,9 +9,10 @@
9#include "fuse_i.h" 9#include "fuse_i.h"
10 10
11#include <linux/xattr.h> 11#include <linux/xattr.h>
12#include <linux/posix_acl_xattr.h>
12 13
13static int fuse_setxattr(struct inode *inode, const char *name, 14int fuse_setxattr(struct inode *inode, const char *name, const void *value,
14 const void *value, size_t size, int flags) 15 size_t size, int flags)
15{ 16{
16 struct fuse_conn *fc = get_fuse_conn(inode); 17 struct fuse_conn *fc = get_fuse_conn(inode);
17 FUSE_ARGS(args); 18 FUSE_ARGS(args);
@@ -45,8 +46,8 @@ static int fuse_setxattr(struct inode *inode, const char *name,
45 return err; 46 return err;
46} 47}
47 48
48static ssize_t fuse_getxattr(struct inode *inode, const char *name, 49ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
49 void *value, size_t size) 50 size_t size)
50{ 51{
51 struct fuse_conn *fc = get_fuse_conn(inode); 52 struct fuse_conn *fc = get_fuse_conn(inode);
52 FUSE_ARGS(args); 53 FUSE_ARGS(args);
@@ -147,7 +148,7 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
147 return ret; 148 return ret;
148} 149}
149 150
150static int fuse_removexattr(struct inode *inode, const char *name) 151int fuse_removexattr(struct inode *inode, const char *name)
151{ 152{
152 struct fuse_conn *fc = get_fuse_conn(inode); 153 struct fuse_conn *fc = get_fuse_conn(inode);
153 FUSE_ARGS(args); 154 FUSE_ARGS(args);
@@ -201,3 +202,10 @@ const struct xattr_handler *fuse_xattr_handlers[] = {
201 &fuse_xattr_handler, 202 &fuse_xattr_handler,
202 NULL 203 NULL
203}; 204};
205
206const struct xattr_handler *fuse_acl_xattr_handlers[] = {
207 &posix_acl_access_xattr_handler,
208 &posix_acl_default_xattr_handler,
209 &fuse_xattr_handler,
210 NULL
211};