Commit 7c46948a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fs.fuse.acl.v6.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/idmapping

Pull fuse ACL fix from Christian Brauner:
 "The new posix acl API doesn't depend on the xattr handler
  infrastructure anymore and instead only relies on the posix acl inode
  operations. As a result daemons without FUSE_POSIX_ACL are unable to
  use posix acls like they used to.

  Fix this by copying what we did for overlayfs during the posix acl api
  conversion. Make fuse implement a dedicated ->get_inode_acl() method
  as does overlayfs. Fuse can then also uses this to express different
  needs for vfs permission checking during lookup and acl based
  retrieval via the regular system call path.

  This allows fuse to continue to refuse retrieving posix acls for
  daemons that don't set FUSE_POSXI_ACL for permission checking while
  also allowing a fuse server to retrieve it via the usual system calls"

* tag 'fs.fuse.acl.v6.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/idmapping:
  fuse: fixes after adapting to new posix acl api
parents 948ef7bb facd6105
...@@ -11,9 +11,10 @@ ...@@ -11,9 +11,10 @@
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h> #include <linux/posix_acl_xattr.h>
struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu) static struct posix_acl *__fuse_get_acl(struct fuse_conn *fc,
struct user_namespace *mnt_userns,
struct inode *inode, int type, bool rcu)
{ {
struct fuse_conn *fc = get_fuse_conn(inode);
int size; int size;
const char *name; const char *name;
void *value = NULL; void *value = NULL;
...@@ -25,7 +26,7 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu) ...@@ -25,7 +26,7 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
if (fuse_is_bad(inode)) if (fuse_is_bad(inode))
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
if (!fc->posix_acl || fc->no_getxattr) if (fc->no_getxattr)
return NULL; return NULL;
if (type == ACL_TYPE_ACCESS) if (type == ACL_TYPE_ACCESS)
...@@ -53,6 +54,46 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu) ...@@ -53,6 +54,46 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
return acl; return acl;
} }
static inline bool fuse_no_acl(const struct fuse_conn *fc,
const struct inode *inode)
{
/*
* Refuse interacting with POSIX ACLs for daemons that
* don't support FUSE_POSIX_ACL and are not mounted on
* the host to retain backwards compatibility.
*/
return !fc->posix_acl && (i_user_ns(inode) != &init_user_ns);
}
struct posix_acl *fuse_get_acl(struct user_namespace *mnt_userns,
struct dentry *dentry, int type)
{
struct inode *inode = d_inode(dentry);
struct fuse_conn *fc = get_fuse_conn(inode);
if (fuse_no_acl(fc, inode))
return ERR_PTR(-EOPNOTSUPP);
return __fuse_get_acl(fc, mnt_userns, inode, type, false);
}
struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu)
{
struct fuse_conn *fc = get_fuse_conn(inode);
/*
* FUSE daemons before FUSE_POSIX_ACL was introduced could get and set
* POSIX ACLs without them being used for permission checking by the
* vfs. Retain that behavior for backwards compatibility as there are
* filesystems that do all permission checking for acls in the daemon
* and not in the kernel.
*/
if (!fc->posix_acl)
return NULL;
return __fuse_get_acl(fc, &init_user_ns, inode, type, rcu);
}
int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
struct posix_acl *acl, int type) struct posix_acl *acl, int type)
{ {
...@@ -64,7 +105,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -64,7 +105,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
if (fuse_is_bad(inode)) if (fuse_is_bad(inode))
return -EIO; return -EIO;
if (!fc->posix_acl || fc->no_setxattr) if (fc->no_setxattr || fuse_no_acl(fc, inode))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (type == ACL_TYPE_ACCESS) if (type == ACL_TYPE_ACCESS)
...@@ -99,7 +140,13 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -99,7 +140,13 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
return ret; return ret;
} }
if (!vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) && /*
* Fuse daemons without FUSE_POSIX_ACL never changed the passed
* through POSIX ACLs. Such daemons don't expect setgid bits to
* be stripped.
*/
if (fc->posix_acl &&
!vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) &&
!capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID))
extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID; extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID;
...@@ -108,8 +155,15 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -108,8 +155,15 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
} else { } else {
ret = fuse_removexattr(inode, name); ret = fuse_removexattr(inode, name);
} }
forget_all_cached_acls(inode);
fuse_invalidate_attr(inode); if (fc->posix_acl) {
/*
* Fuse daemons without FUSE_POSIX_ACL never cached POSIX ACLs
* and didn't invalidate attributes. Retain that behavior.
*/
forget_all_cached_acls(inode);
fuse_invalidate_attr(inode);
}
return ret; return ret;
} }
...@@ -1942,7 +1942,8 @@ static const struct inode_operations fuse_dir_inode_operations = { ...@@ -1942,7 +1942,8 @@ static const struct inode_operations fuse_dir_inode_operations = {
.permission = fuse_permission, .permission = fuse_permission,
.getattr = fuse_getattr, .getattr = fuse_getattr,
.listxattr = fuse_listxattr, .listxattr = fuse_listxattr,
.get_inode_acl = fuse_get_acl, .get_inode_acl = fuse_get_inode_acl,
.get_acl = fuse_get_acl,
.set_acl = fuse_set_acl, .set_acl = fuse_set_acl,
.fileattr_get = fuse_fileattr_get, .fileattr_get = fuse_fileattr_get,
.fileattr_set = fuse_fileattr_set, .fileattr_set = fuse_fileattr_set,
...@@ -1964,7 +1965,8 @@ static const struct inode_operations fuse_common_inode_operations = { ...@@ -1964,7 +1965,8 @@ static const struct inode_operations fuse_common_inode_operations = {
.permission = fuse_permission, .permission = fuse_permission,
.getattr = fuse_getattr, .getattr = fuse_getattr,
.listxattr = fuse_listxattr, .listxattr = fuse_listxattr,
.get_inode_acl = fuse_get_acl, .get_inode_acl = fuse_get_inode_acl,
.get_acl = fuse_get_acl,
.set_acl = fuse_set_acl, .set_acl = fuse_set_acl,
.fileattr_get = fuse_fileattr_get, .fileattr_get = fuse_fileattr_get,
.fileattr_set = fuse_fileattr_set, .fileattr_set = fuse_fileattr_set,
......
...@@ -1264,11 +1264,11 @@ ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value, ...@@ -1264,11 +1264,11 @@ ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size); ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
int fuse_removexattr(struct inode *inode, const char *name); int fuse_removexattr(struct inode *inode, const char *name);
extern const struct xattr_handler *fuse_xattr_handlers[]; extern const struct xattr_handler *fuse_xattr_handlers[];
extern const struct xattr_handler *fuse_acl_xattr_handlers[];
extern const struct xattr_handler *fuse_no_acl_xattr_handlers[];
struct posix_acl; struct posix_acl;
struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu); struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu);
struct posix_acl *fuse_get_acl(struct user_namespace *mnt_userns,
struct dentry *dentry, int type);
int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry, int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
struct posix_acl *acl, int type); struct posix_acl *acl, int type);
......
...@@ -311,7 +311,8 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, ...@@ -311,7 +311,8 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
fuse_dax_dontcache(inode, attr->flags); fuse_dax_dontcache(inode, attr->flags);
} }
static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
struct fuse_conn *fc)
{ {
inode->i_mode = attr->mode & S_IFMT; inode->i_mode = attr->mode & S_IFMT;
inode->i_size = attr->size; inode->i_size = attr->size;
...@@ -333,6 +334,12 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) ...@@ -333,6 +334,12 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
new_decode_dev(attr->rdev)); new_decode_dev(attr->rdev));
} else } else
BUG(); BUG();
/*
* Ensure that we don't cache acls for daemons without FUSE_POSIX_ACL
* so they see the exact same behavior as before.
*/
if (!fc->posix_acl)
inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
} }
static int fuse_inode_eq(struct inode *inode, void *_nodeidp) static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
...@@ -372,7 +379,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, ...@@ -372,7 +379,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
if (!inode) if (!inode)
return NULL; return NULL;
fuse_init_inode(inode, attr); fuse_init_inode(inode, attr, fc);
get_fuse_inode(inode)->nodeid = nodeid; get_fuse_inode(inode)->nodeid = nodeid;
inode->i_flags |= S_AUTOMOUNT; inode->i_flags |= S_AUTOMOUNT;
goto done; goto done;
...@@ -388,7 +395,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, ...@@ -388,7 +395,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
if (!fc->writeback_cache || !S_ISREG(attr->mode)) if (!fc->writeback_cache || !S_ISREG(attr->mode))
inode->i_flags |= S_NOCMTIME; inode->i_flags |= S_NOCMTIME;
inode->i_generation = generation; inode->i_generation = generation;
fuse_init_inode(inode, attr); fuse_init_inode(inode, attr, fc);
unlock_new_inode(inode); unlock_new_inode(inode);
} else if (fuse_stale_inode(inode, generation, attr)) { } else if (fuse_stale_inode(inode, generation, attr)) {
/* nodeid was reused, any I/O on the old inode should fail */ /* nodeid was reused, any I/O on the old inode should fail */
...@@ -1174,7 +1181,6 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args, ...@@ -1174,7 +1181,6 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
if ((flags & FUSE_POSIX_ACL)) { if ((flags & FUSE_POSIX_ACL)) {
fc->default_permissions = 1; fc->default_permissions = 1;
fc->posix_acl = 1; fc->posix_acl = 1;
fm->sb->s_xattr = fuse_acl_xattr_handlers;
} }
if (flags & FUSE_CACHE_SYMLINKS) if (flags & FUSE_CACHE_SYMLINKS)
fc->cache_symlinks = 1; fc->cache_symlinks = 1;
...@@ -1420,13 +1426,6 @@ static void fuse_sb_defaults(struct super_block *sb) ...@@ -1420,13 +1426,6 @@ static void fuse_sb_defaults(struct super_block *sb)
if (sb->s_user_ns != &init_user_ns) if (sb->s_user_ns != &init_user_ns)
sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER; sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION); sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
/*
* If we are not in the initial user namespace posix
* acls must be translated.
*/
if (sb->s_user_ns != &init_user_ns)
sb->s_xattr = fuse_no_acl_xattr_handlers;
} }
static int fuse_fill_super_submount(struct super_block *sb, static int fuse_fill_super_submount(struct super_block *sb,
......
...@@ -203,27 +203,6 @@ static int fuse_xattr_set(const struct xattr_handler *handler, ...@@ -203,27 +203,6 @@ static int fuse_xattr_set(const struct xattr_handler *handler,
return fuse_setxattr(inode, name, value, size, flags, 0); return fuse_setxattr(inode, name, value, size, flags, 0);
} }
static bool no_xattr_list(struct dentry *dentry)
{
return false;
}
static int no_xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *value, size_t size)
{
return -EOPNOTSUPP;
}
static int no_xattr_set(const struct xattr_handler *handler,
struct user_namespace *mnt_userns,
struct dentry *dentry, struct inode *nodee,
const char *name, const void *value,
size_t size, int flags)
{
return -EOPNOTSUPP;
}
static const struct xattr_handler fuse_xattr_handler = { static const struct xattr_handler fuse_xattr_handler = {
.prefix = "", .prefix = "",
.get = fuse_xattr_get, .get = fuse_xattr_get,
...@@ -234,33 +213,3 @@ const struct xattr_handler *fuse_xattr_handlers[] = { ...@@ -234,33 +213,3 @@ const struct xattr_handler *fuse_xattr_handlers[] = {
&fuse_xattr_handler, &fuse_xattr_handler,
NULL NULL
}; };
const struct xattr_handler *fuse_acl_xattr_handlers[] = {
&posix_acl_access_xattr_handler,
&posix_acl_default_xattr_handler,
&fuse_xattr_handler,
NULL
};
static const struct xattr_handler fuse_no_acl_access_xattr_handler = {
.name = XATTR_NAME_POSIX_ACL_ACCESS,
.flags = ACL_TYPE_ACCESS,
.list = no_xattr_list,
.get = no_xattr_get,
.set = no_xattr_set,
};
static const struct xattr_handler fuse_no_acl_default_xattr_handler = {
.name = XATTR_NAME_POSIX_ACL_DEFAULT,
.flags = ACL_TYPE_ACCESS,
.list = no_xattr_list,
.get = no_xattr_get,
.set = no_xattr_set,
};
const struct xattr_handler *fuse_no_acl_xattr_handlers[] = {
&fuse_no_acl_access_xattr_handler,
&fuse_no_acl_default_xattr_handler,
&fuse_xattr_handler,
NULL
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment