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 <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2024-03-01 10:26:03 +01:00
parent 2558e3b231
commit e9c5263ce1
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
4 changed files with 66 additions and 40 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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);