Re: [PATCH v2 1/2] fuse: Add support for FUSE_SETXATTR_V2

From: Luis Henriques
Date: Mon Mar 29 2021 - 16:28:59 EST


Vivek Goyal <vgoyal@xxxxxxxxxx> writes:

> On Mon, Mar 29, 2021 at 03:54:03PM +0100, Luis Henriques wrote:
>> On Thu, Mar 25, 2021 at 11:18:22AM -0400, Vivek Goyal wrote:
>> > Fuse client needs to send additional information to file server when
>> > it calls SETXATTR(system.posix_acl_access). Right now there is no extra
>> > space in fuse_setxattr_in. So introduce a v2 of the structure which has
>> > more space in it and can be used to send extra flags.
>> >
>> > "struct fuse_setxattr_in_v2" is only used if file server opts-in for it using
>> > flag FUSE_SETXATTR_V2 during feature negotiations.
>> >
>> > Signed-off-by: Vivek Goyal <vgoyal@xxxxxxxxxx>
>> > ---
>> > fs/fuse/acl.c | 2 +-
>> > fs/fuse/fuse_i.h | 5 ++++-
>> > fs/fuse/inode.c | 4 +++-
>> > fs/fuse/xattr.c | 21 +++++++++++++++------
>> > include/uapi/linux/fuse.h | 10 ++++++++++
>> > 5 files changed, 33 insertions(+), 9 deletions(-)
>> >
>> > diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c
>> > index e9c0f916349d..d31260a139d4 100644
>> > --- a/fs/fuse/acl.c
>> > +++ b/fs/fuse/acl.c
>> > @@ -94,7 +94,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
>> > return ret;
>> > }
>> >
>> > - ret = fuse_setxattr(inode, name, value, size, 0);
>> > + ret = fuse_setxattr(inode, name, value, size, 0, 0);
>> > kfree(value);
>> > } else {
>> > ret = fuse_removexattr(inode, name);
>> > diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
>> > index 63d97a15ffde..d00bf0b9a38c 100644
>> > --- a/fs/fuse/fuse_i.h
>> > +++ b/fs/fuse/fuse_i.h
>> > @@ -668,6 +668,9 @@ struct fuse_conn {
>> > /** Is setxattr not implemented by fs? */
>> > unsigned no_setxattr:1;
>> >
>> > + /** Does file server support setxattr_v2 */
>> > + unsigned setxattr_v2:1;
>> > +
>> > /** Is getxattr not implemented by fs? */
>> > unsigned no_getxattr:1;
>> >
>> > @@ -1170,7 +1173,7 @@ void fuse_unlock_inode(struct inode *inode, bool locked);
>> > bool fuse_lock_inode(struct inode *inode);
>> >
>> > int fuse_setxattr(struct inode *inode, const char *name, const void *value,
>> > - size_t size, int flags);
>> > + size_t size, int flags, unsigned extra_flags);
>> > ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
>> > size_t size);
>> > ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
>> > diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
>> > index b0e18b470e91..1c726df13f80 100644
>> > --- a/fs/fuse/inode.c
>> > +++ b/fs/fuse/inode.c
>> > @@ -1052,6 +1052,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
>> > fc->handle_killpriv_v2 = 1;
>> > fm->sb->s_flags |= SB_NOSEC;
>> > }
>> > + if (arg->flags & FUSE_SETXATTR_V2)
>> > + fc->setxattr_v2 = 1;
>> > } else {
>> > ra_pages = fc->max_read / PAGE_SIZE;
>> > fc->no_lock = 1;
>> > @@ -1095,7 +1097,7 @@ void fuse_send_init(struct fuse_mount *fm)
>> > FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
>> > FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
>> > FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
>> > - FUSE_HANDLE_KILLPRIV_V2;
>> > + FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_V2;
>> > #ifdef CONFIG_FUSE_DAX
>> > if (fm->fc->dax)
>> > ia->in.flags |= FUSE_MAP_ALIGNMENT;
>> > diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c
>> > index 1a7d7ace54e1..f2aae72653dc 100644
>> > --- a/fs/fuse/xattr.c
>> > +++ b/fs/fuse/xattr.c
>> > @@ -12,24 +12,33 @@
>> > #include <linux/posix_acl_xattr.h>
>> >
>> > int fuse_setxattr(struct inode *inode, const char *name, const void *value,
>> > - size_t size, int flags)
>> > + size_t size, int flags, unsigned extra_flags)
>> > {
>> > struct fuse_mount *fm = get_fuse_mount(inode);
>> > FUSE_ARGS(args);
>> > struct fuse_setxattr_in inarg;
>> > + struct fuse_setxattr_in_v2 inarg_v2;
>> > + bool setxattr_v2 = fm->fc->setxattr_v2;
>> > int err;
>> >
>> > if (fm->fc->no_setxattr)
>> > return -EOPNOTSUPP;
>> >
>> > memset(&inarg, 0, sizeof(inarg));
>> > - inarg.size = size;
>> > - inarg.flags = flags;
>> > + memset(&inarg_v2, 0, sizeof(inarg_v2));
>> > + if (setxattr_v2) {
>> > + inarg_v2.size = size;
>> > + inarg_v2.flags = flags;
>> > + inarg_v2.setxattr_flags = extra_flags;
>> > + } else {
>> > + inarg.size = size;
>> > + inarg.flags = flags;
>> > + }
>> > args.opcode = FUSE_SETXATTR;
>> > args.nodeid = get_node_id(inode);
>> > args.in_numargs = 3;
>> > - args.in_args[0].size = sizeof(inarg);
>> > - args.in_args[0].value = &inarg;
>> > + args.in_args[0].size = setxattr_v2 ? sizeof(inarg_v2) : sizeof(inarg);
>> > + args.in_args[0].value = setxattr_v2 ? &inarg_v2 : (void *)&inarg;
>>
>> And yet another minor:
>>
>> It's a bit awkward to have to cast '&inarg' to 'void *' just because
>> you're using the ternary operator. Why not use an 'if' statement instead
>> for initializing .size and .value?
>
> Yes, I had to use (void *), otherwise compiler was complaining about
> returning different types of pointers. Interesting that compiler
> expects to return same type of pointer.

IIRC, K&R (which I unfortunately don't have at hand right now) says that
the types of both expressions need to match, so probably a different
compiler would show the same warning.

Cheers,
--
Luis

> I think I am fine with this as well as adding explicit if statement. I
> guess just a matter of taste.
>
> Miklos, what do you think? If you also prefer if statement instead,
> I will make changes and post again.
>
> Vivek