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

fuse: fixes after adapting to new posix acl api

This cycle we ported all filesystems to the new posix acl api. While
looking at further simplifications in this area to remove the last
remnants of the generic dummy posix acl handlers we realized that we
regressed fuse daemons that don't set FUSE_POSIX_ACL but still make use
of posix acls.

With the change to a dedicated posix acl api interacting with posix acls
doesn't go through the old xattr codepaths anymore and instead only
relies the get acl and set acl inode operations.

Before this change fuse daemons that don't set FUSE_POSIX_ACL were able
to get and set posix acl albeit with two caveats. First, that posix acls
aren't cached. And second, that they aren't used for permission checking
in the vfs.

We regressed that use-case as we currently refuse to retrieve any posix
acls if they aren't enabled via FUSE_POSIX_ACL. So older fuse daemons
would see a change in behavior.

We can restore the old behavior in multiple ways. We could change the
new posix acl api and look for a dedicated xattr handler and if we find
one prefer that over the dedicated posix acl api. That would break the
consistency of the new posix acl api so we would very much prefer not to
do that.

We could introduce a new ACL_*_CACHE sentinel that would instruct the
vfs permission checking codepath to not call into the filesystem and
ignore acls.

But a more straightforward fix for v6.2 is to do the same thing that
Overlayfs does and give fuse a separate get acl method for permission
checking. Overlayfs uses this to express different needs for vfs
permission lookup and acl based retrieval via the regular system call
path as well. Let fuse do the same for now. This way fuse can continue
to refuse to retrieve posix acls for daemons that don't set
FUSE_POSXI_ACL for permission checking while allowing a fuse server to
retrieve it via the usual system calls.

In the future, we could extend the get acl inode operation to not just
pass a simple boolean to indicate rcu lookup but instead make it a flag
argument. Then in addition to passing the information that this is an
rcu lookup to the filesystem we could also introduce a flag that tells
the filesystem that this is a request from the vfs to use these acls for
permission checking. Then fuse could refuse the get acl request for
permission checking when the daemon doesn't have FUSE_POSIX_ACL set in
the same get acl method. This would also help Overlayfs and allow us to
remove the second method for it as well.

But since that change is more invasive as we need to update the get acl
inode operation for multiple filesystems we should not do this as a fix
for v6.2. Instead we will do this for the v6.3 merge window.

Fwiw, since posix acls are now always correctly translated in the new
posix acl api we could also allow them to be used for daemons without
FUSE_POSIX_ACL that are not mounted on the host. But this is behavioral
change and again if dones should be done for v6.3. For now, let's just
restore the original behavior.

A nice side-effect of this change is that for fuse daemons with and
without FUSE_POSIX_ACL the same code is used for posix acls in a
backwards compatible way. This also means we can remove the legacy xattr
handlers completely. We've also added comments to explain the expected
behavior for daemons without FUSE_POSIX_ACL into the code.

Fixes: 318e66856dde ("xattr: use posix acl api")
Signed-off-by: Seth Forshee (Digital Ocean) <sforshee@kernel.org>
Reviewed-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>

+78 -74
+61 -7
fs/fuse/acl.c
··· 11 11 #include <linux/posix_acl.h> 12 12 #include <linux/posix_acl_xattr.h> 13 13 14 - struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu) 14 + static struct posix_acl *__fuse_get_acl(struct fuse_conn *fc, 15 + struct user_namespace *mnt_userns, 16 + struct inode *inode, int type, bool rcu) 15 17 { 16 - struct fuse_conn *fc = get_fuse_conn(inode); 17 18 int size; 18 19 const char *name; 19 20 void *value = NULL; ··· 26 25 if (fuse_is_bad(inode)) 27 26 return ERR_PTR(-EIO); 28 27 29 - if (!fc->posix_acl || fc->no_getxattr) 28 + if (fc->no_getxattr) 30 29 return NULL; 31 30 32 31 if (type == ACL_TYPE_ACCESS) ··· 54 53 return acl; 55 54 } 56 55 56 + static inline bool fuse_no_acl(const struct fuse_conn *fc, 57 + const struct inode *inode) 58 + { 59 + /* 60 + * Refuse interacting with POSIX ACLs for daemons that 61 + * don't support FUSE_POSIX_ACL and are not mounted on 62 + * the host to retain backwards compatibility. 63 + */ 64 + return !fc->posix_acl && (i_user_ns(inode) != &init_user_ns); 65 + } 66 + 67 + struct posix_acl *fuse_get_acl(struct user_namespace *mnt_userns, 68 + struct dentry *dentry, int type) 69 + { 70 + struct inode *inode = d_inode(dentry); 71 + struct fuse_conn *fc = get_fuse_conn(inode); 72 + 73 + if (fuse_no_acl(fc, inode)) 74 + return ERR_PTR(-EOPNOTSUPP); 75 + 76 + return __fuse_get_acl(fc, mnt_userns, inode, type, false); 77 + } 78 + 79 + struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu) 80 + { 81 + struct fuse_conn *fc = get_fuse_conn(inode); 82 + 83 + /* 84 + * FUSE daemons before FUSE_POSIX_ACL was introduced could get and set 85 + * POSIX ACLs without them being used for permission checking by the 86 + * vfs. Retain that behavior for backwards compatibility as there are 87 + * filesystems that do all permission checking for acls in the daemon 88 + * and not in the kernel. 89 + */ 90 + if (!fc->posix_acl) 91 + return NULL; 92 + 93 + return __fuse_get_acl(fc, &init_user_ns, inode, type, rcu); 94 + } 95 + 57 96 int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, 58 97 struct posix_acl *acl, int type) 59 98 { ··· 105 64 if (fuse_is_bad(inode)) 106 65 return -EIO; 107 66 108 - if (!fc->posix_acl || fc->no_setxattr) 67 + if (fc->no_setxattr || fuse_no_acl(fc, inode)) 109 68 return -EOPNOTSUPP; 110 69 111 70 if (type == ACL_TYPE_ACCESS) ··· 140 99 return ret; 141 100 } 142 101 143 - if (!vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) && 102 + /* 103 + * Fuse daemons without FUSE_POSIX_ACL never changed the passed 104 + * through POSIX ACLs. Such daemons don't expect setgid bits to 105 + * be stripped. 106 + */ 107 + if (fc->posix_acl && 108 + !vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) && 144 109 !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) 145 110 extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; 146 111 ··· 155 108 } else { 156 109 ret = fuse_removexattr(inode, name); 157 110 } 158 - forget_all_cached_acls(inode); 159 - fuse_invalidate_attr(inode); 111 + 112 + if (fc->posix_acl) { 113 + /* 114 + * Fuse daemons without FUSE_POSIX_ACL never cached POSIX ACLs 115 + * and didn't invalidate attributes. Retain that behavior. 116 + */ 117 + forget_all_cached_acls(inode); 118 + fuse_invalidate_attr(inode); 119 + } 160 120 161 121 return ret; 162 122 }
+4 -2
fs/fuse/dir.c
··· 1942 1942 .permission = fuse_permission, 1943 1943 .getattr = fuse_getattr, 1944 1944 .listxattr = fuse_listxattr, 1945 - .get_inode_acl = fuse_get_acl, 1945 + .get_inode_acl = fuse_get_inode_acl, 1946 + .get_acl = fuse_get_acl, 1946 1947 .set_acl = fuse_set_acl, 1947 1948 .fileattr_get = fuse_fileattr_get, 1948 1949 .fileattr_set = fuse_fileattr_set, ··· 1965 1964 .permission = fuse_permission, 1966 1965 .getattr = fuse_getattr, 1967 1966 .listxattr = fuse_listxattr, 1968 - .get_inode_acl = fuse_get_acl, 1967 + .get_inode_acl = fuse_get_inode_acl, 1968 + .get_acl = fuse_get_acl, 1969 1969 .set_acl = fuse_set_acl, 1970 1970 .fileattr_get = fuse_fileattr_get, 1971 1971 .fileattr_set = fuse_fileattr_set,
+3 -3
fs/fuse/fuse_i.h
··· 1264 1264 ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size); 1265 1265 int fuse_removexattr(struct inode *inode, const char *name); 1266 1266 extern const struct xattr_handler *fuse_xattr_handlers[]; 1267 - extern const struct xattr_handler *fuse_acl_xattr_handlers[]; 1268 - extern const struct xattr_handler *fuse_no_acl_xattr_handlers[]; 1269 1267 1270 1268 struct posix_acl; 1271 - struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu); 1269 + struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu); 1270 + struct posix_acl *fuse_get_acl(struct user_namespace *mnt_userns, 1271 + struct dentry *dentry, int type); 1272 1272 int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, 1273 1273 struct posix_acl *acl, int type); 1274 1274
+10 -11
fs/fuse/inode.c
··· 311 311 fuse_dax_dontcache(inode, attr->flags); 312 312 } 313 313 314 - static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) 314 + static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr, 315 + struct fuse_conn *fc) 315 316 { 316 317 inode->i_mode = attr->mode & S_IFMT; 317 318 inode->i_size = attr->size; ··· 334 333 new_decode_dev(attr->rdev)); 335 334 } else 336 335 BUG(); 336 + /* 337 + * Ensure that we don't cache acls for daemons without FUSE_POSIX_ACL 338 + * so they see the exact same behavior as before. 339 + */ 340 + if (!fc->posix_acl) 341 + inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE; 337 342 } 338 343 339 344 static int fuse_inode_eq(struct inode *inode, void *_nodeidp) ··· 379 372 if (!inode) 380 373 return NULL; 381 374 382 - fuse_init_inode(inode, attr); 375 + fuse_init_inode(inode, attr, fc); 383 376 get_fuse_inode(inode)->nodeid = nodeid; 384 377 inode->i_flags |= S_AUTOMOUNT; 385 378 goto done; ··· 395 388 if (!fc->writeback_cache || !S_ISREG(attr->mode)) 396 389 inode->i_flags |= S_NOCMTIME; 397 390 inode->i_generation = generation; 398 - fuse_init_inode(inode, attr); 391 + fuse_init_inode(inode, attr, fc); 399 392 unlock_new_inode(inode); 400 393 } else if (fuse_stale_inode(inode, generation, attr)) { 401 394 /* nodeid was reused, any I/O on the old inode should fail */ ··· 1181 1174 if ((flags & FUSE_POSIX_ACL)) { 1182 1175 fc->default_permissions = 1; 1183 1176 fc->posix_acl = 1; 1184 - fm->sb->s_xattr = fuse_acl_xattr_handlers; 1185 1177 } 1186 1178 if (flags & FUSE_CACHE_SYMLINKS) 1187 1179 fc->cache_symlinks = 1; ··· 1426 1420 if (sb->s_user_ns != &init_user_ns) 1427 1421 sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER; 1428 1422 sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION); 1429 - 1430 - /* 1431 - * If we are not in the initial user namespace posix 1432 - * acls must be translated. 1433 - */ 1434 - if (sb->s_user_ns != &init_user_ns) 1435 - sb->s_xattr = fuse_no_acl_xattr_handlers; 1436 1423 } 1437 1424 1438 1425 static int fuse_fill_super_submount(struct super_block *sb,
-51
fs/fuse/xattr.c
··· 203 203 return fuse_setxattr(inode, name, value, size, flags, 0); 204 204 } 205 205 206 - static bool no_xattr_list(struct dentry *dentry) 207 - { 208 - return false; 209 - } 210 - 211 - static int no_xattr_get(const struct xattr_handler *handler, 212 - struct dentry *dentry, struct inode *inode, 213 - const char *name, void *value, size_t size) 214 - { 215 - return -EOPNOTSUPP; 216 - } 217 - 218 - static int no_xattr_set(const struct xattr_handler *handler, 219 - struct user_namespace *mnt_userns, 220 - struct dentry *dentry, struct inode *nodee, 221 - const char *name, const void *value, 222 - size_t size, int flags) 223 - { 224 - return -EOPNOTSUPP; 225 - } 226 - 227 206 static const struct xattr_handler fuse_xattr_handler = { 228 207 .prefix = "", 229 208 .get = fuse_xattr_get, ··· 210 231 }; 211 232 212 233 const struct xattr_handler *fuse_xattr_handlers[] = { 213 - &fuse_xattr_handler, 214 - NULL 215 - }; 216 - 217 - const struct xattr_handler *fuse_acl_xattr_handlers[] = { 218 - &posix_acl_access_xattr_handler, 219 - &posix_acl_default_xattr_handler, 220 - &fuse_xattr_handler, 221 - NULL 222 - }; 223 - 224 - static const struct xattr_handler fuse_no_acl_access_xattr_handler = { 225 - .name = XATTR_NAME_POSIX_ACL_ACCESS, 226 - .flags = ACL_TYPE_ACCESS, 227 - .list = no_xattr_list, 228 - .get = no_xattr_get, 229 - .set = no_xattr_set, 230 - }; 231 - 232 - static const struct xattr_handler fuse_no_acl_default_xattr_handler = { 233 - .name = XATTR_NAME_POSIX_ACL_DEFAULT, 234 - .flags = ACL_TYPE_ACCESS, 235 - .list = no_xattr_list, 236 - .get = no_xattr_get, 237 - .set = no_xattr_set, 238 - }; 239 - 240 - const struct xattr_handler *fuse_no_acl_xattr_handlers[] = { 241 - &fuse_no_acl_access_xattr_handler, 242 - &fuse_no_acl_default_xattr_handler, 243 234 &fuse_xattr_handler, 244 235 NULL 245 236 };