Add struct fuse_entry2_out, which is a new extended entry reply struct that carries a backing_id and statx attributes. This will be necessary for setting fuse passthrough on inodes. Add helpers that subsequent commits will use to process fuse_entry2_out for passthrough support for lookup, revalidate, and create. fuse_statx_to_attr() is also moved to earlier in the file to avoid forward declaring. Signed-off-by: Joanne Koong --- fs/fuse/dir.c | 116 +++++++++++++++++++++++++++++--------- fs/fuse/fuse_i.h | 5 ++ include/uapi/linux/fuse.h | 14 +++++ 3 files changed, 107 insertions(+), 28 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index a338f2a06b50..b7a9d2b0476a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -355,11 +355,18 @@ static void fuse_invalidate_entry(struct dentry *entry) fuse_invalidate_entry_cache(entry); } -static void fuse_lookup_init(struct fuse_args *args, u64 nodeid, - const struct qstr *name, - struct fuse_entry_out *outarg) +static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args, + u64 nodeid, const struct qstr *name, + struct fuse_entry_out *outarg, + struct fuse_entry2_out *outarg2) { - memset(outarg, 0, sizeof(struct fuse_entry_out)); + bool use_entry2 = fuse_use_entry2(fc); + + if (use_entry2) + memset(outarg2, 0, sizeof(struct fuse_entry2_out)); + else + memset(outarg, 0, sizeof(struct fuse_entry_out)); + args->opcode = FUSE_LOOKUP; args->nodeid = nodeid; args->in_numargs = 3; @@ -369,8 +376,79 @@ static void fuse_lookup_init(struct fuse_args *args, u64 nodeid, args->in_args[2].size = 1; args->in_args[2].value = ""; args->out_numargs = 1; - args->out_args[0].size = sizeof(struct fuse_entry_out); - args->out_args[0].value = outarg; + + if (use_entry2) { + args->out_args[0].size = sizeof(struct fuse_entry2_out); + args->out_args[0].value = outarg2; + } else { + args->out_args[0].size = sizeof(struct fuse_entry_out); + args->out_args[0].value = outarg; + } +} + +static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr) +{ + memset(attr, 0, sizeof(*attr)); + attr->ino = sx->ino; + attr->size = sx->size; + attr->blocks = sx->blocks; + attr->atime = sx->atime.tv_sec; + attr->mtime = sx->mtime.tv_sec; + attr->ctime = sx->ctime.tv_sec; + attr->atimensec = sx->atime.tv_nsec; + attr->mtimensec = sx->mtime.tv_nsec; + attr->ctimensec = sx->ctime.tv_nsec; + attr->mode = sx->mode; + attr->nlink = sx->nlink; + attr->uid = sx->uid; + attr->gid = sx->gid; + attr->rdev = new_encode_dev(MKDEV(sx->rdev_major, sx->rdev_minor)); + attr->blksize = sx->blksize; +} + +static void fuse_entry2_to_entry(struct fuse_entry2_out *outarg2, + struct fuse_entry_out *outarg) +{ + memset(outarg, 0, sizeof(struct fuse_entry_out)); + outarg->nodeid = outarg2->nodeid; + outarg->generation = outarg2->generation; + outarg->entry_valid = outarg2->entry_valid; + outarg->attr_valid = outarg2->attr_valid; + outarg->entry_valid_nsec = outarg2->entry_valid_nsec; + outarg->attr_valid_nsec = outarg2->attr_valid_nsec; + fuse_statx_to_attr(&outarg2->statx, &outarg->attr); + outarg->attr.flags = outarg2->flags; +} + +static __maybe_unused int fuse_process_entry2(struct fuse_conn *fc, + struct fuse_entry2_out *outarg2, + struct fuse_entry_out *outarg, + struct fuse_statx **sxp) +{ + if (!fuse_use_entry2(fc)) + return 0; + + /* + * Convert entry2 format to entry format before error checking since the + * caller needs outarg->nodeid field even on error + */ + fuse_entry2_to_entry(outarg2, outarg); + + if (outarg2->reserved) + return -EINVAL; + + /* error */ + if (outarg2->backing_id < 0) + return outarg2->backing_id; + + /* + * If passthrough is enabled (backing_id > 0), statx attributes are not + * cached because passthrough getattr fetches them directly from the + * backing inode + */ + if (!outarg2->backing_id) + *sxp = &outarg2->statx; + return outarg2->backing_id; } /* @@ -389,6 +467,7 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name, struct fuse_conn *fc; struct fuse_inode *fi; struct fuse_entry_out outarg; + struct fuse_entry2_out outarg2; struct fuse_forget_link *forget; FUSE_ARGS(args); u64 attr_version; @@ -431,7 +510,7 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name, attr_version = fuse_get_attr_version(fc); - fuse_lookup_init(&args, get_node_id(dir), name, &outarg); + fuse_lookup_init(fc, &args, get_node_id(dir), name, &outarg, &outarg2); ret = fuse_simple_request(get_fuse_mount(inode), &args); if (ret || !outarg.nodeid) { kfree(forget); @@ -551,6 +630,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name FUSE_ARGS(args); struct fuse_forget_link *forget; u64 attr_version, evict_ctr; + struct fuse_entry2_out outarg2; int err; *inode = NULL; @@ -567,7 +647,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name attr_version = fuse_get_attr_version(fm->fc); evict_ctr = fuse_get_evict_ctr(fm->fc); - fuse_lookup_init(&args, nodeid, name, outarg); + fuse_lookup_init(fm->fc, &args, nodeid, name, outarg, &outarg2); err = fuse_simple_request(fm, &args); /* Zero nodeid is same as -ENOENT, but with valid timeout */ if (err || !outarg->nodeid) @@ -1386,26 +1466,6 @@ static void fuse_fillattr(struct mnt_idmap *idmap, struct inode *inode, stat->blksize = 1 << blkbits; } -static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr) -{ - memset(attr, 0, sizeof(*attr)); - attr->ino = sx->ino; - attr->size = sx->size; - attr->blocks = sx->blocks; - attr->atime = sx->atime.tv_sec; - attr->mtime = sx->mtime.tv_sec; - attr->ctime = sx->ctime.tv_sec; - attr->atimensec = sx->atime.tv_nsec; - attr->mtimensec = sx->mtime.tv_nsec; - attr->ctimensec = sx->ctime.tv_nsec; - attr->mode = sx->mode; - attr->nlink = sx->nlink; - attr->uid = sx->uid; - attr->gid = sx->gid; - attr->rdev = new_encode_dev(MKDEV(sx->rdev_major, sx->rdev_minor)); - attr->blksize = sx->blksize; -} - void fuse_kstat_to_attr(struct fuse_conn *fc, const struct kstat *stat, struct fuse_attr *attr) { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1f2c849ea4e3..89c9333e9702 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1368,6 +1368,11 @@ static inline bool fuse_passthrough_op(struct inode *inode, enum fuse_opcode op) int fuse_passthrough_getattr(struct inode *inode, struct kstat *stat, u32 request_mask, unsigned int flags); +static inline bool fuse_use_entry2(struct fuse_conn *fc) +{ + return fc->passthrough_ino; +} + #ifdef CONFIG_SYSCTL extern int fuse_sysctl_register(void); extern void fuse_sysctl_unregister(void); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 6404ed95c758..3963631558f9 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -244,6 +244,7 @@ * 7.46 * - add FUSE_PASSTHROUGH_INO * - add ops_mask field to struct fuse_backing_map + * - add fuse_entry2_out */ #ifndef _LINUX_FUSE_H @@ -705,6 +706,19 @@ struct fuse_entry_out { struct fuse_attr attr; }; +struct fuse_entry2_out { + uint64_t nodeid; + uint64_t generation; + uint64_t entry_valid; + uint64_t attr_valid; + uint32_t entry_valid_nsec; + uint32_t attr_valid_nsec; + int32_t backing_id; + uint32_t flags; + uint64_t reserved; + struct fuse_statx statx; +}; + struct fuse_forget_in { uint64_t nlookup; }; -- 2.52.0