Re: [PATCH 09/16] fs: add vfs_set_fscaps()

From: Seth Forshee (DigitalOcean)
Date: Thu Nov 30 2023 - 10:38:08 EST


On Thu, Nov 30, 2023 at 10:01:55AM +0200, Amir Goldstein wrote:
> On Wed, Nov 29, 2023 at 11:50 PM Seth Forshee (DigitalOcean)
> <sforshee@xxxxxxxxxx> wrote:
> >
> > Provide a type-safe interface for setting filesystem capabilities and a
> > generic implementation suitable for most filesystems.
> >
> > Signed-off-by: Seth Forshee (DigitalOcean) <sforshee@xxxxxxxxxx>
> > ---
> > fs/xattr.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/fs.h | 2 ++
> > 2 files changed, 89 insertions(+)
> >
> > diff --git a/fs/xattr.c b/fs/xattr.c
> > index 3abaf9bef0a5..03cc824e4f87 100644
> > --- a/fs/xattr.c
> > +++ b/fs/xattr.c
> > @@ -247,6 +247,93 @@ int vfs_get_fscaps(struct mnt_idmap *idmap, struct dentry *dentry,
> > }
> > EXPORT_SYMBOL(vfs_get_fscaps);
> >
> > +static int generic_set_fscaps(struct mnt_idmap *idmap, struct dentry *dentry,
> > + const struct vfs_caps *caps, int flags)
> > +{
> > + struct inode *inode = d_inode(dentry);
> > + struct vfs_ns_cap_data nscaps;
> > + int size;
> > +
> > + size = vfs_caps_to_xattr(idmap, i_user_ns(inode), caps,
> > + &nscaps, sizeof(nscaps));
> > + if (size < 0)
> > + return size;
> > +
> > + return __vfs_setxattr_noperm(idmap, dentry, XATTR_NAME_CAPS,
> > + &nscaps, size, flags);
> > +}
> > +
> > +/**
> > + * vfs_set_fscaps - set filesystem capabilities
> > + * @idmap: idmap of the mount the inode was found from
> > + * @dentry: the dentry on which to set filesystem capabilities
> > + * @caps: the filesystem capabilities to be written
> > + * @flags: setxattr flags to use when writing the capabilities xattr
> > + *
> > + * This function writes the supplied filesystem capabilities to the dentry.
> > + *
> > + * Return: 0 on success, a negative errno on error.
> > + */
> > +int vfs_set_fscaps(struct mnt_idmap *idmap, struct dentry *dentry,
> > + const struct vfs_caps *caps, int flags)
> > +{
> > + struct inode *inode = d_inode(dentry);
> > + struct inode *delegated_inode = NULL;
> > + struct vfs_ns_cap_data nscaps;
> > + int size, error;
> > +
> > + /*
> > + * Unfortunately EVM wants to have the raw xattr value to compare to
> > + * the on-disk version, so we need to pass the raw xattr to the
> > + * security hooks. But we also want to do security checks before
> > + * breaking leases, so that means a conversion to the raw xattr here
> > + * which will usually be reduntant with the conversion we do for
> > + * writing the xattr to disk.
> > + */
> > + size = vfs_caps_to_xattr(idmap, i_user_ns(inode), caps, &nscaps,
> > + sizeof(nscaps));
> > + if (size < 0)
> > + return size;
> > +
> > +retry_deleg:
> > + inode_lock(inode);
> > +
> > + error = xattr_permission(idmap, inode, XATTR_NAME_CAPS, MAY_WRITE);
> > + if (error)
> > + goto out_inode_unlock;
> > + error = security_inode_setxattr(idmap, dentry, XATTR_NAME_CAPS, &nscaps,
> > + size, flags);
> > + if (error)
> > + goto out_inode_unlock;
> > +
> > + error = try_break_deleg(inode, &delegated_inode);
> > + if (error)
> > + goto out_inode_unlock;
> > +
> > + if (inode->i_opflags & IOP_XATTR) {
> > + if (inode->i_op->set_fscaps)
> > + error = inode->i_op->set_fscaps(idmap, dentry, caps, flags);
> > + else
> > + error = generic_set_fscaps(idmap, dentry, caps, flags);
>
> I think the non-generic case is missing fsnotify_xattr().
>
> See vfs_set_acl() for comparison.

Good catch. I'm going to have another look at some of this in light of
some of your other feedback, but I'll get it fixed one way or another in
v2.