fuse: support idmapped getattr inode op
authorAlexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Tue, 3 Sep 2024 15:16:17 +0000 (17:16 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 4 Sep 2024 14:49:28 +0000 (16:49 +0200)
We have to:
- pass an idmapping to the generic_fillattr()
to properly handle UIG/GID mapping for the userspace.
- pass -/- to fuse_fillattr() (analog of generic_fillattr() in fuse).

Difference between these two is that generic_fillattr() takes all the
stat() data from the inode directly, while fuse_fillattr() codepath takes a
fresh data just from the userspace reply on the FUSE_GETATTR request.

In some cases we can just pass &nop_mnt_idmap, because idmapping won't be
used in these codepaths. For example, when 3rd argument of
fuse_do_getattr() is NULL then idmap argument is not used.

Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dir.c

index 9da8fee..2bc3ae4 100644 (file)
@@ -1135,18 +1135,22 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
        return err;
 }
 
-static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
-                         struct kstat *stat)
+static void fuse_fillattr(struct mnt_idmap *idmap, struct inode *inode,
+                         struct fuse_attr *attr, struct kstat *stat)
 {
        unsigned int blkbits;
        struct fuse_conn *fc = get_fuse_conn(inode);
+       vfsuid_t vfsuid = make_vfsuid(idmap, fc->user_ns,
+                                     make_kuid(fc->user_ns, attr->uid));
+       vfsgid_t vfsgid = make_vfsgid(idmap, fc->user_ns,
+                                     make_kgid(fc->user_ns, attr->gid));
 
        stat->dev = inode->i_sb->s_dev;
        stat->ino = attr->ino;
        stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
        stat->nlink = attr->nlink;
-       stat->uid = make_kuid(fc->user_ns, attr->uid);
-       stat->gid = make_kgid(fc->user_ns, attr->gid);
+       stat->uid = vfsuid_into_kuid(vfsuid);
+       stat->gid = vfsgid_into_kgid(vfsgid);
        stat->rdev = inode->i_rdev;
        stat->atime.tv_sec = attr->atime;
        stat->atime.tv_nsec = attr->atimensec;
@@ -1185,8 +1189,8 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
        attr->blksize = sx->blksize;
 }
 
-static int fuse_do_statx(struct inode *inode, struct file *file,
-                        struct kstat *stat)
+static int fuse_do_statx(struct mnt_idmap *idmap, struct inode *inode,
+                        struct file *file, struct kstat *stat)
 {
        int err;
        struct fuse_attr attr;
@@ -1239,15 +1243,15 @@ static int fuse_do_statx(struct inode *inode, struct file *file,
                stat->result_mask = sx->mask & (STATX_BASIC_STATS | STATX_BTIME);
                stat->btime.tv_sec = sx->btime.tv_sec;
                stat->btime.tv_nsec = min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1);
-               fuse_fillattr(inode, &attr, stat);
+               fuse_fillattr(idmap, inode, &attr, stat);
                stat->result_mask |= STATX_TYPE;
        }
 
        return 0;
 }
 
-static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
-                          struct file *file)
+static int fuse_do_getattr(struct mnt_idmap *idmap, struct inode *inode,
+                          struct kstat *stat, struct file *file)
 {
        int err;
        struct fuse_getattr_in inarg;
@@ -1286,15 +1290,15 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
                                               ATTR_TIMEOUT(&outarg),
                                               attr_version);
                        if (stat)
-                               fuse_fillattr(inode, &outarg.attr, stat);
+                               fuse_fillattr(idmap, inode, &outarg.attr, stat);
                }
        }
        return err;
 }
 
-static int fuse_update_get_attr(struct inode *inode, struct file *file,
-                               struct kstat *stat, u32 request_mask,
-                               unsigned int flags)
+static int fuse_update_get_attr(struct mnt_idmap *idmap, struct inode *inode,
+                               struct file *file, struct kstat *stat,
+                               u32 request_mask, unsigned int flags)
 {
        struct fuse_inode *fi = get_fuse_inode(inode);
        struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1325,17 +1329,17 @@ retry:
                forget_all_cached_acls(inode);
                /* Try statx if BTIME is requested */
                if (!fc->no_statx && (request_mask & ~STATX_BASIC_STATS)) {
-                       err = fuse_do_statx(inode, file, stat);
+                       err = fuse_do_statx(idmap, inode, file, stat);
                        if (err == -ENOSYS) {
                                fc->no_statx = 1;
                                err = 0;
                                goto retry;
                        }
                } else {
-                       err = fuse_do_getattr(inode, stat, file);
+                       err = fuse_do_getattr(idmap, inode, stat, file);
                }
        } else if (stat) {
-               generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
+               generic_fillattr(idmap, request_mask, inode, stat);
                stat->mode = fi->orig_i_mode;
                stat->ino = fi->orig_ino;
                if (test_bit(FUSE_I_BTIME, &fi->state)) {
@@ -1349,7 +1353,7 @@ retry:
 
 int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask)
 {
-       return fuse_update_get_attr(inode, file, NULL, mask, 0);
+       return fuse_update_get_attr(&nop_mnt_idmap, inode, file, NULL, mask, 0);
 }
 
 int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
@@ -1493,7 +1497,7 @@ static int fuse_perm_getattr(struct inode *inode, int mask)
                return -ECHILD;
 
        forget_all_cached_acls(inode);
-       return fuse_do_getattr(inode, NULL, NULL);
+       return fuse_do_getattr(&nop_mnt_idmap, inode, NULL, NULL);
 }
 
 /*
@@ -2072,7 +2076,7 @@ static int fuse_setattr(struct mnt_idmap *idmap, struct dentry *entry,
                         * ia_mode calculation may have used stale i_mode.
                         * Refresh and recalculate.
                         */
-                       ret = fuse_do_getattr(inode, NULL, file);
+                       ret = fuse_do_getattr(idmap, inode, NULL, file);
                        if (ret)
                                return ret;
 
@@ -2129,7 +2133,7 @@ static int fuse_getattr(struct mnt_idmap *idmap,
                return -EACCES;
        }
 
-       return fuse_update_get_attr(inode, NULL, stat, request_mask, flags);
+       return fuse_update_get_attr(idmap, inode, NULL, stat, request_mask, flags);
 }
 
 static const struct inode_operations fuse_dir_inode_operations = {