From e9c5263ce16d96311c118111ac779f004be8b473 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 1 Mar 2024 10:26:03 +0100 Subject: [PATCH] libfs: improve path_from_stashed() Right now we pass a bunch of info that is fs specific which doesn't make a lot of sense and it bleeds fs sepcific details into the generic helper. nsfs and pidfs have slightly different needs when initializing inodes. Add simple operations that are stashed in sb->s_fs_info that both can implement. This also allows us to get rid of cleaning up references in the caller. All in all path_from_stashed() becomes way simpler. Signed-off-by: Christian Brauner --- fs/internal.h | 8 +++++--- fs/libfs.c | 41 ++++++++++++++++++----------------------- fs/nsfs.c | 33 ++++++++++++++++++++++----------- fs/pidfs.c | 24 +++++++++++++++++++++--- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index b0c843c3fa3c..7d3edcdf59cc 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -310,8 +310,10 @@ ssize_t __kernel_write_iter(struct file *file, struct iov_iter *from, loff_t *po struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns); struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap); void mnt_idmap_put(struct mnt_idmap *idmap); +struct stashed_operations { + void (*put_data)(void *data); + void (*init_inode)(struct inode *inode, void *data); +}; int path_from_stashed(struct dentry **stashed, unsigned long ino, - struct vfsmount *mnt, const struct file_operations *fops, - const struct inode_operations *iops, void *data, - struct path *path); + struct vfsmount *mnt, void *data, struct path *path); void stashed_dentry_prune(struct dentry *dentry); diff --git a/fs/libfs.c b/fs/libfs.c index 472f21bd0325..65322e11bcda 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1991,12 +1991,11 @@ static inline struct dentry *get_stashed_dentry(struct dentry *stashed) static struct dentry *prepare_anon_dentry(struct dentry **stashed, unsigned long ino, struct super_block *sb, - const struct file_operations *fops, - const struct inode_operations *iops, void *data) { struct dentry *dentry; struct inode *inode; + const struct stashed_operations *sops = sb->s_fs_info; dentry = d_alloc_anon(sb); if (!dentry) @@ -2010,15 +2009,13 @@ static struct dentry *prepare_anon_dentry(struct dentry **stashed, inode->i_ino = ino; inode->i_flags |= S_IMMUTABLE; - if (is_pidfs_sb(sb)) - inode->i_flags |= S_PRIVATE; - inode->i_mode = S_IFREG | S_IRUGO; - if (iops) - inode->i_op = iops; - if (fops) - inode->i_fop = fops; - inode->i_private = data; + inode->i_mode = S_IFREG; simple_inode_init_ts(inode); + sops->init_inode(inode, data); + + /* Notice when this is changed. */ + WARN_ON_ONCE(!S_ISREG(inode->i_mode)); + WARN_ON_ONCE(!IS_IMMUTABLE(inode)); /* Store address of location where dentry's supposed to be stashed. */ dentry->d_fsdata = stashed; @@ -2055,8 +2052,6 @@ static struct dentry *stash_dentry(struct dentry **stashed, * @stashed: where to retrieve or stash dentry * @ino: inode number to use * @mnt: mnt of the filesystems to use - * @iops: inode operations to use - * @fops: file operations to use * @data: data to store in inode->i_private * @path: path to create * @@ -2068,38 +2063,38 @@ static struct dentry *stash_dentry(struct dentry **stashed, * * Special-purpose helper for nsfs and pidfs. * - * Return: If 0 or an error is returned the caller can be sure that @data must - * be cleaned up. If 1 is returned @data is owned by the filesystem. + * Return: On success zero and on failure a negative error is returned. */ int path_from_stashed(struct dentry **stashed, unsigned long ino, - struct vfsmount *mnt, const struct file_operations *fops, - const struct inode_operations *iops, void *data, - struct path *path) + struct vfsmount *mnt, void *data, struct path *path) { struct dentry *dentry; - int ret = 0; + const struct stashed_operations *sops = mnt->mnt_sb->s_fs_info; /* See if dentry can be reused. */ path->dentry = get_stashed_dentry(*stashed); - if (path->dentry) + if (path->dentry) { + sops->put_data(data); goto out_path; + } /* Allocate a new dentry. */ - dentry = prepare_anon_dentry(stashed, ino, mnt->mnt_sb, fops, iops, data); - if (IS_ERR(dentry)) + dentry = prepare_anon_dentry(stashed, ino, mnt->mnt_sb, data); + if (IS_ERR(dentry)) { + sops->put_data(data); return PTR_ERR(dentry); + } /* Added a new dentry. @data is now owned by the filesystem. */ path->dentry = stash_dentry(stashed, dentry); if (path->dentry != dentry) dput(dentry); - ret = 1; out_path: WARN_ON_ONCE(path->dentry->d_fsdata != stashed); WARN_ON_ONCE(d_inode(path->dentry)->i_private != data); path->mnt = mntget(mnt); - return ret; + return 0; } void stashed_dentry_prune(struct dentry *dentry) diff --git a/fs/nsfs.c b/fs/nsfs.c index 2ce229af34e9..7aaafb5cb9fc 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -50,19 +50,13 @@ static void nsfs_evict(struct inode *inode) int ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb, void *private_data) { - int ret; struct ns_common *ns; ns = ns_get_cb(private_data); if (!ns) return -ENOENT; - ret = path_from_stashed(&ns->stashed, ns->inum, nsfs_mnt, - &ns_file_operations, NULL, ns, path); - if (ret <= 0) - ns->ops->put(ns); - if (ret < 0) - return ret; - return 0; + + return path_from_stashed(&ns->stashed, ns->inum, nsfs_mnt, ns, path); } struct ns_get_path_task_args { @@ -108,9 +102,7 @@ int open_related_ns(struct ns_common *ns, } err = path_from_stashed(&relative->stashed, relative->inum, nsfs_mnt, - &ns_file_operations, NULL, relative, &path); - if (err <= 0) - relative->ops->put(relative); + relative, &path); if (err < 0) { put_unused_fd(fd); return err; @@ -207,6 +199,24 @@ static const struct super_operations nsfs_ops = { .show_path = nsfs_show_path, }; +static void nsfs_init_inode(struct inode *inode, void *data) +{ + inode->i_private = data; + inode->i_mode |= S_IRUGO; + inode->i_fop = &ns_file_operations; +} + +static void nsfs_put_data(void *data) +{ + struct ns_common *ns = data; + ns->ops->put(ns); +} + +static const struct stashed_operations nsfs_stashed_ops = { + .init_inode = nsfs_init_inode, + .put_data = nsfs_put_data, +}; + static int nsfs_init_fs_context(struct fs_context *fc) { struct pseudo_fs_context *ctx = init_pseudo(fc, NSFS_MAGIC); @@ -214,6 +224,7 @@ static int nsfs_init_fs_context(struct fs_context *fc) return -ENOMEM; ctx->ops = &nsfs_ops; ctx->dops = &ns_dentry_operations; + fc->s_fs_info = (void *)&nsfs_stashed_ops; return 0; } diff --git a/fs/pidfs.c b/fs/pidfs.c index d38b7a184994..8fd71a00be9c 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -193,6 +193,26 @@ static const struct dentry_operations pidfs_dentry_operations = { .d_prune = stashed_dentry_prune, }; +static void pidfs_init_inode(struct inode *inode, void *data) +{ + inode->i_private = data; + inode->i_flags |= S_PRIVATE; + inode->i_mode |= S_IRWXU; + inode->i_op = &pidfs_inode_operations; + inode->i_fop = &pidfs_file_operations; +} + +static void pidfs_put_data(void *data) +{ + struct pid *pid = data; + put_pid(pid); +} + +static const struct stashed_operations pidfs_stashed_ops = { + .init_inode = pidfs_init_inode, + .put_data = pidfs_put_data, +}; + static int pidfs_init_fs_context(struct fs_context *fc) { struct pseudo_fs_context *ctx; @@ -203,6 +223,7 @@ static int pidfs_init_fs_context(struct fs_context *fc) ctx->ops = &pidfs_sops; ctx->dops = &pidfs_dentry_operations; + fc->s_fs_info = (void *)&pidfs_stashed_ops; return 0; } @@ -225,10 +246,7 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags) * for pseudo filesystems. */ ret = path_from_stashed(&pid->stashed, pid->ino, pidfs_mnt, - &pidfs_file_operations, &pidfs_inode_operations, get_pid(pid), &path); - if (ret <= 0) - put_pid(pid); if (ret < 0) return ERR_PTR(ret);