diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-07 11:50:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-07 11:50:57 -0400 |
commit | da315f6e03988a7127680bbc26e1028991b899b8 (patch) | |
tree | fc4a41dbb22ac0a7a2f683dc5c5c6789957ce5e8 /fs/fuse | |
parent | 1c8c5a9d38f607c0b6fd12c91cbe1a4418762a21 (diff) | |
parent | 543b8f8662fe6d21f19958b666ab0051af9db21a (diff) |
Merge tag 'fuse-update-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse updates from Miklos Szeredi:
"The most interesting part of this update is user namespace support,
mostly done by Eric Biederman. This enables safe unprivileged fuse
mounts within a user namespace.
There are also a couple of fixes for bugs found by syzbot and
miscellaneous fixes and cleanups"
* tag 'fuse-update-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
fuse: don't keep dead fuse_conn at fuse_fill_super().
fuse: fix control dir setup and teardown
fuse: fix congested state leak on aborted connections
fuse: Allow fully unprivileged mounts
fuse: Ensure posix acls are translated outside of init_user_ns
fuse: add writeback documentation
fuse: honor AT_STATX_FORCE_SYNC
fuse: honor AT_STATX_DONT_SYNC
fuse: Restrict allow_other to the superblock's namespace or a descendant
fuse: Support fuse filesystems outside of init_user_ns
fuse: Fail all requests with invalid uids or gids
fuse: Remove the buggy retranslation of pids in fuse_dev_do_read
fuse: return -ECONNABORTED on /dev/fuse read after abort
fuse: atomic_o_trunc should truncate pagecache
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/acl.c | 4 | ||||
-rw-r--r-- | fs/fuse/control.c | 15 | ||||
-rw-r--r-- | fs/fuse/cuse.c | 11 | ||||
-rw-r--r-- | fs/fuse/dev.c | 43 | ||||
-rw-r--r-- | fs/fuse/dir.c | 45 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 15 | ||||
-rw-r--r-- | fs/fuse/inode.c | 50 | ||||
-rw-r--r-- | fs/fuse/xattr.c | 43 |
8 files changed, 164 insertions, 62 deletions
diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c index ec85765502f1..5a48cee6d7d3 100644 --- a/fs/fuse/acl.c +++ b/fs/fuse/acl.c | |||
@@ -34,7 +34,7 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type) | |||
34 | return ERR_PTR(-ENOMEM); | 34 | return ERR_PTR(-ENOMEM); |
35 | size = fuse_getxattr(inode, name, value, PAGE_SIZE); | 35 | size = fuse_getxattr(inode, name, value, PAGE_SIZE); |
36 | if (size > 0) | 36 | if (size > 0) |
37 | acl = posix_acl_from_xattr(&init_user_ns, value, size); | 37 | acl = posix_acl_from_xattr(fc->user_ns, value, size); |
38 | else if ((size == 0) || (size == -ENODATA) || | 38 | else if ((size == 0) || (size == -ENODATA) || |
39 | (size == -EOPNOTSUPP && fc->no_getxattr)) | 39 | (size == -EOPNOTSUPP && fc->no_getxattr)) |
40 | acl = NULL; | 40 | acl = NULL; |
@@ -81,7 +81,7 @@ int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type) | |||
81 | if (!value) | 81 | if (!value) |
82 | return -ENOMEM; | 82 | return -ENOMEM; |
83 | 83 | ||
84 | ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); | 84 | ret = posix_acl_to_xattr(fc->user_ns, acl, value, size); |
85 | if (ret < 0) { | 85 | if (ret < 0) { |
86 | kfree(value); | 86 | kfree(value); |
87 | return ret; | 87 | return ret; |
diff --git a/fs/fuse/control.c b/fs/fuse/control.c index b9ea99c5b5b3..0b694655d988 100644 --- a/fs/fuse/control.c +++ b/fs/fuse/control.c | |||
@@ -35,7 +35,7 @@ static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, | |||
35 | { | 35 | { |
36 | struct fuse_conn *fc = fuse_ctl_file_conn_get(file); | 36 | struct fuse_conn *fc = fuse_ctl_file_conn_get(file); |
37 | if (fc) { | 37 | if (fc) { |
38 | fuse_abort_conn(fc); | 38 | fuse_abort_conn(fc, true); |
39 | fuse_conn_put(fc); | 39 | fuse_conn_put(fc); |
40 | } | 40 | } |
41 | return count; | 41 | return count; |
@@ -211,10 +211,11 @@ static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, | |||
211 | if (!dentry) | 211 | if (!dentry) |
212 | return NULL; | 212 | return NULL; |
213 | 213 | ||
214 | fc->ctl_dentry[fc->ctl_ndents++] = dentry; | ||
215 | inode = new_inode(fuse_control_sb); | 214 | inode = new_inode(fuse_control_sb); |
216 | if (!inode) | 215 | if (!inode) { |
216 | dput(dentry); | ||
217 | return NULL; | 217 | return NULL; |
218 | } | ||
218 | 219 | ||
219 | inode->i_ino = get_next_ino(); | 220 | inode->i_ino = get_next_ino(); |
220 | inode->i_mode = mode; | 221 | inode->i_mode = mode; |
@@ -228,6 +229,9 @@ static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, | |||
228 | set_nlink(inode, nlink); | 229 | set_nlink(inode, nlink); |
229 | inode->i_private = fc; | 230 | inode->i_private = fc; |
230 | d_add(dentry, inode); | 231 | d_add(dentry, inode); |
232 | |||
233 | fc->ctl_dentry[fc->ctl_ndents++] = dentry; | ||
234 | |||
231 | return dentry; | 235 | return dentry; |
232 | } | 236 | } |
233 | 237 | ||
@@ -284,7 +288,10 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc) | |||
284 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { | 288 | for (i = fc->ctl_ndents - 1; i >= 0; i--) { |
285 | struct dentry *dentry = fc->ctl_dentry[i]; | 289 | struct dentry *dentry = fc->ctl_dentry[i]; |
286 | d_inode(dentry)->i_private = NULL; | 290 | d_inode(dentry)->i_private = NULL; |
287 | d_drop(dentry); | 291 | if (!i) { |
292 | /* Get rid of submounts: */ | ||
293 | d_invalidate(dentry); | ||
294 | } | ||
288 | dput(dentry); | 295 | dput(dentry); |
289 | } | 296 | } |
290 | drop_nlink(d_inode(fuse_control_sb->s_root)); | 297 | drop_nlink(d_inode(fuse_control_sb->s_root)); |
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index e9e97803442a..8f68181256c0 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include <linux/stat.h> | 48 | #include <linux/stat.h> |
49 | #include <linux/module.h> | 49 | #include <linux/module.h> |
50 | #include <linux/uio.h> | 50 | #include <linux/uio.h> |
51 | #include <linux/user_namespace.h> | ||
51 | 52 | ||
52 | #include "fuse_i.h" | 53 | #include "fuse_i.h" |
53 | 54 | ||
@@ -406,7 +407,7 @@ err_unlock: | |||
406 | err_region: | 407 | err_region: |
407 | unregister_chrdev_region(devt, 1); | 408 | unregister_chrdev_region(devt, 1); |
408 | err: | 409 | err: |
409 | fuse_abort_conn(fc); | 410 | fuse_abort_conn(fc, false); |
410 | goto out; | 411 | goto out; |
411 | } | 412 | } |
412 | 413 | ||
@@ -498,7 +499,11 @@ static int cuse_channel_open(struct inode *inode, struct file *file) | |||
498 | if (!cc) | 499 | if (!cc) |
499 | return -ENOMEM; | 500 | return -ENOMEM; |
500 | 501 | ||
501 | fuse_conn_init(&cc->fc); | 502 | /* |
503 | * Limit the cuse channel to requests that can | ||
504 | * be represented in file->f_cred->user_ns. | ||
505 | */ | ||
506 | fuse_conn_init(&cc->fc, file->f_cred->user_ns); | ||
502 | 507 | ||
503 | fud = fuse_dev_alloc(&cc->fc); | 508 | fud = fuse_dev_alloc(&cc->fc); |
504 | if (!fud) { | 509 | if (!fud) { |
@@ -581,7 +586,7 @@ static ssize_t cuse_class_abort_store(struct device *dev, | |||
581 | { | 586 | { |
582 | struct cuse_conn *cc = dev_get_drvdata(dev); | 587 | struct cuse_conn *cc = dev_get_drvdata(dev); |
583 | 588 | ||
584 | fuse_abort_conn(&cc->fc); | 589 | fuse_abort_conn(&cc->fc, false); |
585 | return count; | 590 | return count; |
586 | } | 591 | } |
587 | static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store); | 592 | static DEVICE_ATTR(abort, 0200, NULL, cuse_class_abort_store); |
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 5d06384c2cae..e03ca14f40e9 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -112,13 +112,6 @@ static void __fuse_put_request(struct fuse_req *req) | |||
112 | refcount_dec(&req->count); | 112 | refcount_dec(&req->count); |
113 | } | 113 | } |
114 | 114 | ||
115 | static void fuse_req_init_context(struct fuse_conn *fc, struct fuse_req *req) | ||
116 | { | ||
117 | req->in.h.uid = from_kuid_munged(&init_user_ns, current_fsuid()); | ||
118 | req->in.h.gid = from_kgid_munged(&init_user_ns, current_fsgid()); | ||
119 | req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns); | ||
120 | } | ||
121 | |||
122 | void fuse_set_initialized(struct fuse_conn *fc) | 115 | void fuse_set_initialized(struct fuse_conn *fc) |
123 | { | 116 | { |
124 | /* Make sure stores before this are seen on another CPU */ | 117 | /* Make sure stores before this are seen on another CPU */ |
@@ -163,11 +156,19 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, | |||
163 | goto out; | 156 | goto out; |
164 | } | 157 | } |
165 | 158 | ||
166 | fuse_req_init_context(fc, req); | 159 | req->in.h.uid = from_kuid(fc->user_ns, current_fsuid()); |
160 | req->in.h.gid = from_kgid(fc->user_ns, current_fsgid()); | ||
161 | req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns); | ||
162 | |||
167 | __set_bit(FR_WAITING, &req->flags); | 163 | __set_bit(FR_WAITING, &req->flags); |
168 | if (for_background) | 164 | if (for_background) |
169 | __set_bit(FR_BACKGROUND, &req->flags); | 165 | __set_bit(FR_BACKGROUND, &req->flags); |
170 | 166 | ||
167 | if (unlikely(req->in.h.uid == ((uid_t)-1) || | ||
168 | req->in.h.gid == ((gid_t)-1))) { | ||
169 | fuse_put_request(fc, req); | ||
170 | return ERR_PTR(-EOVERFLOW); | ||
171 | } | ||
171 | return req; | 172 | return req; |
172 | 173 | ||
173 | out: | 174 | out: |
@@ -256,7 +257,10 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc, | |||
256 | if (!req) | 257 | if (!req) |
257 | req = get_reserved_req(fc, file); | 258 | req = get_reserved_req(fc, file); |
258 | 259 | ||
259 | fuse_req_init_context(fc, req); | 260 | req->in.h.uid = from_kuid_munged(fc->user_ns, current_fsuid()); |
261 | req->in.h.gid = from_kgid_munged(fc->user_ns, current_fsgid()); | ||
262 | req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns); | ||
263 | |||
260 | __set_bit(FR_WAITING, &req->flags); | 264 | __set_bit(FR_WAITING, &req->flags); |
261 | __clear_bit(FR_BACKGROUND, &req->flags); | 265 | __clear_bit(FR_BACKGROUND, &req->flags); |
262 | return req; | 266 | return req; |
@@ -381,8 +385,7 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
381 | if (!fc->blocked && waitqueue_active(&fc->blocked_waitq)) | 385 | if (!fc->blocked && waitqueue_active(&fc->blocked_waitq)) |
382 | wake_up(&fc->blocked_waitq); | 386 | wake_up(&fc->blocked_waitq); |
383 | 387 | ||
384 | if (fc->num_background == fc->congestion_threshold && | 388 | if (fc->num_background == fc->congestion_threshold && fc->sb) { |
385 | fc->connected && fc->sb) { | ||
386 | clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC); | 389 | clear_bdi_congested(fc->sb->s_bdi, BLK_RW_SYNC); |
387 | clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC); | 390 | clear_bdi_congested(fc->sb->s_bdi, BLK_RW_ASYNC); |
388 | } | 391 | } |
@@ -1234,9 +1237,10 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, | |||
1234 | if (err) | 1237 | if (err) |
1235 | goto err_unlock; | 1238 | goto err_unlock; |
1236 | 1239 | ||
1237 | err = -ENODEV; | 1240 | if (!fiq->connected) { |
1238 | if (!fiq->connected) | 1241 | err = (fc->aborted && fc->abort_err) ? -ECONNABORTED : -ENODEV; |
1239 | goto err_unlock; | 1242 | goto err_unlock; |
1243 | } | ||
1240 | 1244 | ||
1241 | if (!list_empty(&fiq->interrupts)) { | 1245 | if (!list_empty(&fiq->interrupts)) { |
1242 | req = list_entry(fiq->interrupts.next, struct fuse_req, | 1246 | req = list_entry(fiq->interrupts.next, struct fuse_req, |
@@ -1260,12 +1264,6 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, | |||
1260 | in = &req->in; | 1264 | in = &req->in; |
1261 | reqsize = in->h.len; | 1265 | reqsize = in->h.len; |
1262 | 1266 | ||
1263 | if (task_active_pid_ns(current) != fc->pid_ns) { | ||
1264 | rcu_read_lock(); | ||
1265 | in->h.pid = pid_vnr(find_pid_ns(in->h.pid, fc->pid_ns)); | ||
1266 | rcu_read_unlock(); | ||
1267 | } | ||
1268 | |||
1269 | /* If request is too large, reply with an error and restart the read */ | 1267 | /* If request is too large, reply with an error and restart the read */ |
1270 | if (nbytes < reqsize) { | 1268 | if (nbytes < reqsize) { |
1271 | req->out.h.error = -EIO; | 1269 | req->out.h.error = -EIO; |
@@ -1287,7 +1285,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, | |||
1287 | spin_lock(&fpq->lock); | 1285 | spin_lock(&fpq->lock); |
1288 | clear_bit(FR_LOCKED, &req->flags); | 1286 | clear_bit(FR_LOCKED, &req->flags); |
1289 | if (!fpq->connected) { | 1287 | if (!fpq->connected) { |
1290 | err = -ENODEV; | 1288 | err = (fc->aborted && fc->abort_err) ? -ECONNABORTED : -ENODEV; |
1291 | goto out_end; | 1289 | goto out_end; |
1292 | } | 1290 | } |
1293 | if (err) { | 1291 | if (err) { |
@@ -2076,7 +2074,7 @@ static void end_polls(struct fuse_conn *fc) | |||
2076 | * is OK, the request will in that case be removed from the list before we touch | 2074 | * is OK, the request will in that case be removed from the list before we touch |
2077 | * it. | 2075 | * it. |
2078 | */ | 2076 | */ |
2079 | void fuse_abort_conn(struct fuse_conn *fc) | 2077 | void fuse_abort_conn(struct fuse_conn *fc, bool is_abort) |
2080 | { | 2078 | { |
2081 | struct fuse_iqueue *fiq = &fc->iq; | 2079 | struct fuse_iqueue *fiq = &fc->iq; |
2082 | 2080 | ||
@@ -2089,6 +2087,7 @@ void fuse_abort_conn(struct fuse_conn *fc) | |||
2089 | 2087 | ||
2090 | fc->connected = 0; | 2088 | fc->connected = 0; |
2091 | fc->blocked = 0; | 2089 | fc->blocked = 0; |
2090 | fc->aborted = is_abort; | ||
2092 | fuse_set_initialized(fc); | 2091 | fuse_set_initialized(fc); |
2093 | list_for_each_entry(fud, &fc->devices, entry) { | 2092 | list_for_each_entry(fud, &fc->devices, entry) { |
2094 | struct fuse_pqueue *fpq = &fud->pq; | 2093 | struct fuse_pqueue *fpq = &fud->pq; |
@@ -2151,7 +2150,7 @@ int fuse_dev_release(struct inode *inode, struct file *file) | |||
2151 | /* Are we the last open device? */ | 2150 | /* Are we the last open device? */ |
2152 | if (atomic_dec_and_test(&fc->dev_count)) { | 2151 | if (atomic_dec_and_test(&fc->dev_count)) { |
2153 | WARN_ON(fc->iq.fasync != NULL); | 2152 | WARN_ON(fc->iq.fasync != NULL); |
2154 | fuse_abort_conn(fc); | 2153 | fuse_abort_conn(fc, false); |
2155 | } | 2154 | } |
2156 | fuse_dev_free(fud); | 2155 | fuse_dev_free(fud); |
2157 | } | 2156 | } |
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 24967382a7b1..56231b31f806 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -858,8 +858,8 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, | |||
858 | stat->ino = attr->ino; | 858 | stat->ino = attr->ino; |
859 | stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); | 859 | stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); |
860 | stat->nlink = attr->nlink; | 860 | stat->nlink = attr->nlink; |
861 | stat->uid = make_kuid(&init_user_ns, attr->uid); | 861 | stat->uid = make_kuid(fc->user_ns, attr->uid); |
862 | stat->gid = make_kgid(&init_user_ns, attr->gid); | 862 | stat->gid = make_kgid(fc->user_ns, attr->gid); |
863 | stat->rdev = inode->i_rdev; | 863 | stat->rdev = inode->i_rdev; |
864 | stat->atime.tv_sec = attr->atime; | 864 | stat->atime.tv_sec = attr->atime; |
865 | stat->atime.tv_nsec = attr->atimensec; | 865 | stat->atime.tv_nsec = attr->atimensec; |
@@ -924,12 +924,20 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, | |||
924 | } | 924 | } |
925 | 925 | ||
926 | static int fuse_update_get_attr(struct inode *inode, struct file *file, | 926 | static int fuse_update_get_attr(struct inode *inode, struct file *file, |
927 | struct kstat *stat) | 927 | struct kstat *stat, unsigned int flags) |
928 | { | 928 | { |
929 | struct fuse_inode *fi = get_fuse_inode(inode); | 929 | struct fuse_inode *fi = get_fuse_inode(inode); |
930 | int err = 0; | 930 | int err = 0; |
931 | bool sync; | ||
931 | 932 | ||
932 | if (time_before64(fi->i_time, get_jiffies_64())) { | 933 | if (flags & AT_STATX_FORCE_SYNC) |
934 | sync = true; | ||
935 | else if (flags & AT_STATX_DONT_SYNC) | ||
936 | sync = false; | ||
937 | else | ||
938 | sync = time_before64(fi->i_time, get_jiffies_64()); | ||
939 | |||
940 | if (sync) { | ||
933 | forget_all_cached_acls(inode); | 941 | forget_all_cached_acls(inode); |
934 | err = fuse_do_getattr(inode, stat, file); | 942 | err = fuse_do_getattr(inode, stat, file); |
935 | } else if (stat) { | 943 | } else if (stat) { |
@@ -943,7 +951,7 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, | |||
943 | 951 | ||
944 | int fuse_update_attributes(struct inode *inode, struct file *file) | 952 | int fuse_update_attributes(struct inode *inode, struct file *file) |
945 | { | 953 | { |
946 | return fuse_update_get_attr(inode, file, NULL); | 954 | return fuse_update_get_attr(inode, file, NULL, 0); |
947 | } | 955 | } |
948 | 956 | ||
949 | int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, | 957 | int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, |
@@ -1030,7 +1038,7 @@ int fuse_allow_current_process(struct fuse_conn *fc) | |||
1030 | const struct cred *cred; | 1038 | const struct cred *cred; |
1031 | 1039 | ||
1032 | if (fc->allow_other) | 1040 | if (fc->allow_other) |
1033 | return 1; | 1041 | return current_in_userns(fc->user_ns); |
1034 | 1042 | ||
1035 | cred = current_cred(); | 1043 | cred = current_cred(); |
1036 | if (uid_eq(cred->euid, fc->user_id) && | 1044 | if (uid_eq(cred->euid, fc->user_id) && |
@@ -1475,17 +1483,17 @@ static bool update_mtime(unsigned ivalid, bool trust_local_mtime) | |||
1475 | return true; | 1483 | return true; |
1476 | } | 1484 | } |
1477 | 1485 | ||
1478 | static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, | 1486 | static void iattr_to_fattr(struct fuse_conn *fc, struct iattr *iattr, |
1479 | bool trust_local_cmtime) | 1487 | struct fuse_setattr_in *arg, bool trust_local_cmtime) |
1480 | { | 1488 | { |
1481 | unsigned ivalid = iattr->ia_valid; | 1489 | unsigned ivalid = iattr->ia_valid; |
1482 | 1490 | ||
1483 | if (ivalid & ATTR_MODE) | 1491 | if (ivalid & ATTR_MODE) |
1484 | arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode; | 1492 | arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode; |
1485 | if (ivalid & ATTR_UID) | 1493 | if (ivalid & ATTR_UID) |
1486 | arg->valid |= FATTR_UID, arg->uid = from_kuid(&init_user_ns, iattr->ia_uid); | 1494 | arg->valid |= FATTR_UID, arg->uid = from_kuid(fc->user_ns, iattr->ia_uid); |
1487 | if (ivalid & ATTR_GID) | 1495 | if (ivalid & ATTR_GID) |
1488 | arg->valid |= FATTR_GID, arg->gid = from_kgid(&init_user_ns, iattr->ia_gid); | 1496 | arg->valid |= FATTR_GID, arg->gid = from_kgid(fc->user_ns, iattr->ia_gid); |
1489 | if (ivalid & ATTR_SIZE) | 1497 | if (ivalid & ATTR_SIZE) |
1490 | arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; | 1498 | arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size; |
1491 | if (ivalid & ATTR_ATIME) { | 1499 | if (ivalid & ATTR_ATIME) { |
@@ -1629,8 +1637,19 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, | |||
1629 | return err; | 1637 | return err; |
1630 | 1638 | ||
1631 | if (attr->ia_valid & ATTR_OPEN) { | 1639 | if (attr->ia_valid & ATTR_OPEN) { |
1632 | if (fc->atomic_o_trunc) | 1640 | /* This is coming from open(..., ... | O_TRUNC); */ |
1641 | WARN_ON(!(attr->ia_valid & ATTR_SIZE)); | ||
1642 | WARN_ON(attr->ia_size != 0); | ||
1643 | if (fc->atomic_o_trunc) { | ||
1644 | /* | ||
1645 | * No need to send request to userspace, since actual | ||
1646 | * truncation has already been done by OPEN. But still | ||
1647 | * need to truncate page cache. | ||
1648 | */ | ||
1649 | i_size_write(inode, 0); | ||
1650 | truncate_pagecache(inode, 0); | ||
1633 | return 0; | 1651 | return 0; |
1652 | } | ||
1634 | file = NULL; | 1653 | file = NULL; |
1635 | } | 1654 | } |
1636 | 1655 | ||
@@ -1646,7 +1665,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, | |||
1646 | 1665 | ||
1647 | memset(&inarg, 0, sizeof(inarg)); | 1666 | memset(&inarg, 0, sizeof(inarg)); |
1648 | memset(&outarg, 0, sizeof(outarg)); | 1667 | memset(&outarg, 0, sizeof(outarg)); |
1649 | iattr_to_fattr(attr, &inarg, trust_local_cmtime); | 1668 | iattr_to_fattr(fc, attr, &inarg, trust_local_cmtime); |
1650 | if (file) { | 1669 | if (file) { |
1651 | struct fuse_file *ff = file->private_data; | 1670 | struct fuse_file *ff = file->private_data; |
1652 | inarg.valid |= FATTR_FH; | 1671 | inarg.valid |= FATTR_FH; |
@@ -1783,7 +1802,7 @@ static int fuse_getattr(const struct path *path, struct kstat *stat, | |||
1783 | if (!fuse_allow_current_process(fc)) | 1802 | if (!fuse_allow_current_process(fc)) |
1784 | return -EACCES; | 1803 | return -EACCES; |
1785 | 1804 | ||
1786 | return fuse_update_get_attr(inode, NULL, stat); | 1805 | return fuse_update_get_attr(inode, NULL, stat, flags); |
1787 | } | 1806 | } |
1788 | 1807 | ||
1789 | static const struct inode_operations fuse_dir_inode_operations = { | 1808 | static const struct inode_operations fuse_dir_inode_operations = { |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index c4c093bbf456..5256ad333b05 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/xattr.h> | 26 | #include <linux/xattr.h> |
27 | #include <linux/pid_namespace.h> | 27 | #include <linux/pid_namespace.h> |
28 | #include <linux/refcount.h> | 28 | #include <linux/refcount.h> |
29 | #include <linux/user_namespace.h> | ||
29 | 30 | ||
30 | /** Max number of pages that can be used in a single read request */ | 31 | /** Max number of pages that can be used in a single read request */ |
31 | #define FUSE_MAX_PAGES_PER_REQ 32 | 32 | #define FUSE_MAX_PAGES_PER_REQ 32 |
@@ -466,6 +467,9 @@ struct fuse_conn { | |||
466 | /** The pid namespace for this mount */ | 467 | /** The pid namespace for this mount */ |
467 | struct pid_namespace *pid_ns; | 468 | struct pid_namespace *pid_ns; |
468 | 469 | ||
470 | /** The user namespace for this mount */ | ||
471 | struct user_namespace *user_ns; | ||
472 | |||
469 | /** Maximum read size */ | 473 | /** Maximum read size */ |
470 | unsigned max_read; | 474 | unsigned max_read; |
471 | 475 | ||
@@ -515,6 +519,9 @@ struct fuse_conn { | |||
515 | abort and device release */ | 519 | abort and device release */ |
516 | unsigned connected; | 520 | unsigned connected; |
517 | 521 | ||
522 | /** Connection aborted via sysfs */ | ||
523 | bool aborted; | ||
524 | |||
518 | /** Connection failed (version mismatch). Cannot race with | 525 | /** Connection failed (version mismatch). Cannot race with |
519 | setting other bitfields since it is only set once in INIT | 526 | setting other bitfields since it is only set once in INIT |
520 | reply, before any other request, and never cleared */ | 527 | reply, before any other request, and never cleared */ |
@@ -526,6 +533,9 @@ struct fuse_conn { | |||
526 | /** Do readpages asynchronously? Only set in INIT */ | 533 | /** Do readpages asynchronously? Only set in INIT */ |
527 | unsigned async_read:1; | 534 | unsigned async_read:1; |
528 | 535 | ||
536 | /** Return an unique read error after abort. Only set in INIT */ | ||
537 | unsigned abort_err:1; | ||
538 | |||
529 | /** Do not send separate SETATTR request before open(O_TRUNC) */ | 539 | /** Do not send separate SETATTR request before open(O_TRUNC) */ |
530 | unsigned atomic_o_trunc:1; | 540 | unsigned atomic_o_trunc:1; |
531 | 541 | ||
@@ -851,7 +861,7 @@ void fuse_request_send_background_locked(struct fuse_conn *fc, | |||
851 | struct fuse_req *req); | 861 | struct fuse_req *req); |
852 | 862 | ||
853 | /* Abort all requests */ | 863 | /* Abort all requests */ |
854 | void fuse_abort_conn(struct fuse_conn *fc); | 864 | void fuse_abort_conn(struct fuse_conn *fc, bool is_abort); |
855 | 865 | ||
856 | /** | 866 | /** |
857 | * Invalidate inode attributes | 867 | * Invalidate inode attributes |
@@ -870,7 +880,7 @@ struct fuse_conn *fuse_conn_get(struct fuse_conn *fc); | |||
870 | /** | 880 | /** |
871 | * Initialize fuse_conn | 881 | * Initialize fuse_conn |
872 | */ | 882 | */ |
873 | void fuse_conn_init(struct fuse_conn *fc); | 883 | void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns); |
874 | 884 | ||
875 | /** | 885 | /** |
876 | * Release reference to fuse_conn | 886 | * Release reference to fuse_conn |
@@ -975,6 +985,7 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size); | |||
975 | int fuse_removexattr(struct inode *inode, const char *name); | 985 | int fuse_removexattr(struct inode *inode, const char *name); |
976 | extern const struct xattr_handler *fuse_xattr_handlers[]; | 986 | extern const struct xattr_handler *fuse_xattr_handlers[]; |
977 | extern const struct xattr_handler *fuse_acl_xattr_handlers[]; | 987 | extern const struct xattr_handler *fuse_acl_xattr_handlers[]; |
988 | extern const struct xattr_handler *fuse_no_acl_xattr_handlers[]; | ||
978 | 989 | ||
979 | struct posix_acl; | 990 | struct posix_acl; |
980 | struct posix_acl *fuse_get_acl(struct inode *inode, int type); | 991 | struct posix_acl *fuse_get_acl(struct inode *inode, int type); |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index ef309958e060..ffcaf98044b9 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -171,8 +171,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, | |||
171 | inode->i_ino = fuse_squash_ino(attr->ino); | 171 | inode->i_ino = fuse_squash_ino(attr->ino); |
172 | inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); | 172 | inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); |
173 | set_nlink(inode, attr->nlink); | 173 | set_nlink(inode, attr->nlink); |
174 | inode->i_uid = make_kuid(&init_user_ns, attr->uid); | 174 | inode->i_uid = make_kuid(fc->user_ns, attr->uid); |
175 | inode->i_gid = make_kgid(&init_user_ns, attr->gid); | 175 | inode->i_gid = make_kgid(fc->user_ns, attr->gid); |
176 | inode->i_blocks = attr->blocks; | 176 | inode->i_blocks = attr->blocks; |
177 | inode->i_atime.tv_sec = attr->atime; | 177 | inode->i_atime.tv_sec = attr->atime; |
178 | inode->i_atime.tv_nsec = attr->atimensec; | 178 | inode->i_atime.tv_nsec = attr->atimensec; |
@@ -371,7 +371,7 @@ void fuse_unlock_inode(struct inode *inode) | |||
371 | 371 | ||
372 | static void fuse_umount_begin(struct super_block *sb) | 372 | static void fuse_umount_begin(struct super_block *sb) |
373 | { | 373 | { |
374 | fuse_abort_conn(get_fuse_conn_super(sb)); | 374 | fuse_abort_conn(get_fuse_conn_super(sb), false); |
375 | } | 375 | } |
376 | 376 | ||
377 | static void fuse_send_destroy(struct fuse_conn *fc) | 377 | static void fuse_send_destroy(struct fuse_conn *fc) |
@@ -393,7 +393,7 @@ static void fuse_put_super(struct super_block *sb) | |||
393 | 393 | ||
394 | fuse_send_destroy(fc); | 394 | fuse_send_destroy(fc); |
395 | 395 | ||
396 | fuse_abort_conn(fc); | 396 | fuse_abort_conn(fc, false); |
397 | mutex_lock(&fuse_mutex); | 397 | mutex_lock(&fuse_mutex); |
398 | list_del(&fc->entry); | 398 | list_del(&fc->entry); |
399 | fuse_ctl_remove_conn(fc); | 399 | fuse_ctl_remove_conn(fc); |
@@ -477,7 +477,8 @@ static int fuse_match_uint(substring_t *s, unsigned int *res) | |||
477 | return err; | 477 | return err; |
478 | } | 478 | } |
479 | 479 | ||
480 | static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) | 480 | static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev, |
481 | struct user_namespace *user_ns) | ||
481 | { | 482 | { |
482 | char *p; | 483 | char *p; |
483 | memset(d, 0, sizeof(struct fuse_mount_data)); | 484 | memset(d, 0, sizeof(struct fuse_mount_data)); |
@@ -513,7 +514,7 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) | |||
513 | case OPT_USER_ID: | 514 | case OPT_USER_ID: |
514 | if (fuse_match_uint(&args[0], &uv)) | 515 | if (fuse_match_uint(&args[0], &uv)) |
515 | return 0; | 516 | return 0; |
516 | d->user_id = make_kuid(current_user_ns(), uv); | 517 | d->user_id = make_kuid(user_ns, uv); |
517 | if (!uid_valid(d->user_id)) | 518 | if (!uid_valid(d->user_id)) |
518 | return 0; | 519 | return 0; |
519 | d->user_id_present = 1; | 520 | d->user_id_present = 1; |
@@ -522,7 +523,7 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev) | |||
522 | case OPT_GROUP_ID: | 523 | case OPT_GROUP_ID: |
523 | if (fuse_match_uint(&args[0], &uv)) | 524 | if (fuse_match_uint(&args[0], &uv)) |
524 | return 0; | 525 | return 0; |
525 | d->group_id = make_kgid(current_user_ns(), uv); | 526 | d->group_id = make_kgid(user_ns, uv); |
526 | if (!gid_valid(d->group_id)) | 527 | if (!gid_valid(d->group_id)) |
527 | return 0; | 528 | return 0; |
528 | d->group_id_present = 1; | 529 | d->group_id_present = 1; |
@@ -565,8 +566,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root) | |||
565 | struct super_block *sb = root->d_sb; | 566 | struct super_block *sb = root->d_sb; |
566 | struct fuse_conn *fc = get_fuse_conn_super(sb); | 567 | struct fuse_conn *fc = get_fuse_conn_super(sb); |
567 | 568 | ||
568 | seq_printf(m, ",user_id=%u", from_kuid_munged(&init_user_ns, fc->user_id)); | 569 | seq_printf(m, ",user_id=%u", from_kuid_munged(fc->user_ns, fc->user_id)); |
569 | seq_printf(m, ",group_id=%u", from_kgid_munged(&init_user_ns, fc->group_id)); | 570 | seq_printf(m, ",group_id=%u", from_kgid_munged(fc->user_ns, fc->group_id)); |
570 | if (fc->default_permissions) | 571 | if (fc->default_permissions) |
571 | seq_puts(m, ",default_permissions"); | 572 | seq_puts(m, ",default_permissions"); |
572 | if (fc->allow_other) | 573 | if (fc->allow_other) |
@@ -597,7 +598,7 @@ static void fuse_pqueue_init(struct fuse_pqueue *fpq) | |||
597 | fpq->connected = 1; | 598 | fpq->connected = 1; |
598 | } | 599 | } |
599 | 600 | ||
600 | void fuse_conn_init(struct fuse_conn *fc) | 601 | void fuse_conn_init(struct fuse_conn *fc, struct user_namespace *user_ns) |
601 | { | 602 | { |
602 | memset(fc, 0, sizeof(*fc)); | 603 | memset(fc, 0, sizeof(*fc)); |
603 | spin_lock_init(&fc->lock); | 604 | spin_lock_init(&fc->lock); |
@@ -621,6 +622,7 @@ void fuse_conn_init(struct fuse_conn *fc) | |||
621 | fc->attr_version = 1; | 622 | fc->attr_version = 1; |
622 | get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); | 623 | get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); |
623 | fc->pid_ns = get_pid_ns(task_active_pid_ns(current)); | 624 | fc->pid_ns = get_pid_ns(task_active_pid_ns(current)); |
625 | fc->user_ns = get_user_ns(user_ns); | ||
624 | } | 626 | } |
625 | EXPORT_SYMBOL_GPL(fuse_conn_init); | 627 | EXPORT_SYMBOL_GPL(fuse_conn_init); |
626 | 628 | ||
@@ -630,6 +632,7 @@ void fuse_conn_put(struct fuse_conn *fc) | |||
630 | if (fc->destroy_req) | 632 | if (fc->destroy_req) |
631 | fuse_request_free(fc->destroy_req); | 633 | fuse_request_free(fc->destroy_req); |
632 | put_pid_ns(fc->pid_ns); | 634 | put_pid_ns(fc->pid_ns); |
635 | put_user_ns(fc->user_ns); | ||
633 | fc->release(fc); | 636 | fc->release(fc); |
634 | } | 637 | } |
635 | } | 638 | } |
@@ -918,6 +921,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | |||
918 | fc->posix_acl = 1; | 921 | fc->posix_acl = 1; |
919 | fc->sb->s_xattr = fuse_acl_xattr_handlers; | 922 | fc->sb->s_xattr = fuse_acl_xattr_handlers; |
920 | } | 923 | } |
924 | if (arg->flags & FUSE_ABORT_ERROR) | ||
925 | fc->abort_err = 1; | ||
921 | } else { | 926 | } else { |
922 | ra_pages = fc->max_read / PAGE_SIZE; | 927 | ra_pages = fc->max_read / PAGE_SIZE; |
923 | fc->no_lock = 1; | 928 | fc->no_lock = 1; |
@@ -948,7 +953,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) | |||
948 | FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | | 953 | FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA | |
949 | FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | | 954 | FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO | |
950 | FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | | 955 | FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT | |
951 | FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL; | 956 | FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL | |
957 | FUSE_ABORT_ERROR; | ||
952 | req->in.h.opcode = FUSE_INIT; | 958 | req->in.h.opcode = FUSE_INIT; |
953 | req->in.numargs = 1; | 959 | req->in.numargs = 1; |
954 | req->in.args[0].size = sizeof(*arg); | 960 | req->in.args[0].size = sizeof(*arg); |
@@ -1061,7 +1067,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
1061 | 1067 | ||
1062 | sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION); | 1068 | sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION); |
1063 | 1069 | ||
1064 | if (!parse_fuse_opt(data, &d, is_bdev)) | 1070 | if (!parse_fuse_opt(data, &d, is_bdev, sb->s_user_ns)) |
1065 | goto err; | 1071 | goto err; |
1066 | 1072 | ||
1067 | if (is_bdev) { | 1073 | if (is_bdev) { |
@@ -1089,16 +1095,27 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
1089 | if (!file) | 1095 | if (!file) |
1090 | goto err; | 1096 | goto err; |
1091 | 1097 | ||
1092 | if ((file->f_op != &fuse_dev_operations) || | 1098 | /* |
1093 | (file->f_cred->user_ns != &init_user_ns)) | 1099 | * Require mount to happen from the same user namespace which |
1100 | * opened /dev/fuse to prevent potential attacks. | ||
1101 | */ | ||
1102 | if (file->f_op != &fuse_dev_operations || | ||
1103 | file->f_cred->user_ns != sb->s_user_ns) | ||
1094 | goto err_fput; | 1104 | goto err_fput; |
1095 | 1105 | ||
1106 | /* | ||
1107 | * If we are not in the initial user namespace posix | ||
1108 | * acls must be translated. | ||
1109 | */ | ||
1110 | if (sb->s_user_ns != &init_user_ns) | ||
1111 | sb->s_xattr = fuse_no_acl_xattr_handlers; | ||
1112 | |||
1096 | fc = kmalloc(sizeof(*fc), GFP_KERNEL); | 1113 | fc = kmalloc(sizeof(*fc), GFP_KERNEL); |
1097 | err = -ENOMEM; | 1114 | err = -ENOMEM; |
1098 | if (!fc) | 1115 | if (!fc) |
1099 | goto err_fput; | 1116 | goto err_fput; |
1100 | 1117 | ||
1101 | fuse_conn_init(fc); | 1118 | fuse_conn_init(fc, sb->s_user_ns); |
1102 | fc->release = fuse_free_conn; | 1119 | fc->release = fuse_free_conn; |
1103 | 1120 | ||
1104 | fud = fuse_dev_alloc(fc); | 1121 | fud = fuse_dev_alloc(fc); |
@@ -1179,6 +1196,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
1179 | fuse_dev_free(fud); | 1196 | fuse_dev_free(fud); |
1180 | err_put_conn: | 1197 | err_put_conn: |
1181 | fuse_conn_put(fc); | 1198 | fuse_conn_put(fc); |
1199 | sb->s_fs_info = NULL; | ||
1182 | err_fput: | 1200 | err_fput: |
1183 | fput(file); | 1201 | fput(file); |
1184 | err: | 1202 | err: |
@@ -1208,7 +1226,7 @@ static void fuse_kill_sb_anon(struct super_block *sb) | |||
1208 | static struct file_system_type fuse_fs_type = { | 1226 | static struct file_system_type fuse_fs_type = { |
1209 | .owner = THIS_MODULE, | 1227 | .owner = THIS_MODULE, |
1210 | .name = "fuse", | 1228 | .name = "fuse", |
1211 | .fs_flags = FS_HAS_SUBTYPE, | 1229 | .fs_flags = FS_HAS_SUBTYPE | FS_USERNS_MOUNT, |
1212 | .mount = fuse_mount, | 1230 | .mount = fuse_mount, |
1213 | .kill_sb = fuse_kill_sb_anon, | 1231 | .kill_sb = fuse_kill_sb_anon, |
1214 | }; | 1232 | }; |
diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c index 3caac46b08b0..433717640f78 100644 --- a/fs/fuse/xattr.c +++ b/fs/fuse/xattr.c | |||
@@ -192,6 +192,26 @@ static int fuse_xattr_set(const struct xattr_handler *handler, | |||
192 | return fuse_setxattr(inode, name, value, size, flags); | 192 | return fuse_setxattr(inode, name, value, size, flags); |
193 | } | 193 | } |
194 | 194 | ||
195 | static bool no_xattr_list(struct dentry *dentry) | ||
196 | { | ||
197 | return false; | ||
198 | } | ||
199 | |||
200 | static int no_xattr_get(const struct xattr_handler *handler, | ||
201 | struct dentry *dentry, struct inode *inode, | ||
202 | const char *name, void *value, size_t size) | ||
203 | { | ||
204 | return -EOPNOTSUPP; | ||
205 | } | ||
206 | |||
207 | static int no_xattr_set(const struct xattr_handler *handler, | ||
208 | struct dentry *dentry, struct inode *nodee, | ||
209 | const char *name, const void *value, | ||
210 | size_t size, int flags) | ||
211 | { | ||
212 | return -EOPNOTSUPP; | ||
213 | } | ||
214 | |||
195 | static const struct xattr_handler fuse_xattr_handler = { | 215 | static const struct xattr_handler fuse_xattr_handler = { |
196 | .prefix = "", | 216 | .prefix = "", |
197 | .get = fuse_xattr_get, | 217 | .get = fuse_xattr_get, |
@@ -209,3 +229,26 @@ const struct xattr_handler *fuse_acl_xattr_handlers[] = { | |||
209 | &fuse_xattr_handler, | 229 | &fuse_xattr_handler, |
210 | NULL | 230 | NULL |
211 | }; | 231 | }; |
232 | |||
233 | static const struct xattr_handler fuse_no_acl_access_xattr_handler = { | ||
234 | .name = XATTR_NAME_POSIX_ACL_ACCESS, | ||
235 | .flags = ACL_TYPE_ACCESS, | ||
236 | .list = no_xattr_list, | ||
237 | .get = no_xattr_get, | ||
238 | .set = no_xattr_set, | ||
239 | }; | ||
240 | |||
241 | static const struct xattr_handler fuse_no_acl_default_xattr_handler = { | ||
242 | .name = XATTR_NAME_POSIX_ACL_DEFAULT, | ||
243 | .flags = ACL_TYPE_ACCESS, | ||
244 | .list = no_xattr_list, | ||
245 | .get = no_xattr_get, | ||
246 | .set = no_xattr_set, | ||
247 | }; | ||
248 | |||
249 | const struct xattr_handler *fuse_no_acl_xattr_handlers[] = { | ||
250 | &fuse_no_acl_access_xattr_handler, | ||
251 | &fuse_no_acl_default_xattr_handler, | ||
252 | &fuse_xattr_handler, | ||
253 | NULL | ||
254 | }; | ||