libfs: add path_from_stashed()

Add a helper for both nsfs and pidfs to reuse an already stashed dentry
or to add and stash a new dentry.

Link: https://lore.kernel.org/r/20240218-neufahrzeuge-brauhaus-fb0eb6459771@brauner
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2024-02-18 14:50:13 +01:00
parent cb12fd8e0d
commit 07fd7c3298
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
2 changed files with 97 additions and 0 deletions

View File

@ -310,3 +310,6 @@ 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);
int path_from_stashed(struct dentry **stashed, unsigned long ino,
struct vfsmount *mnt, const struct file_operations *fops,
void *data, struct path *path);

View File

@ -1973,3 +1973,97 @@ struct timespec64 simple_inode_init_ts(struct inode *inode)
return ts;
}
EXPORT_SYMBOL(simple_inode_init_ts);
static inline struct dentry *get_stashed_dentry(struct dentry *stashed)
{
struct dentry *dentry;
guard(rcu)();
dentry = READ_ONCE(stashed);
if (!dentry)
return NULL;
if (!lockref_get_not_dead(&dentry->d_lockref))
return NULL;
return dentry;
}
static struct dentry *stash_dentry(struct dentry **stashed, unsigned long ino,
struct super_block *sb,
const struct file_operations *fops,
void *data)
{
struct dentry *dentry;
struct inode *inode;
dentry = d_alloc_anon(sb);
if (!dentry)
return ERR_PTR(-ENOMEM);
inode = new_inode_pseudo(sb);
if (!inode) {
dput(dentry);
return ERR_PTR(-ENOMEM);
}
inode->i_ino = ino;
inode->i_flags |= S_IMMUTABLE;
inode->i_mode = S_IFREG | S_IRUGO;
inode->i_fop = fops;
inode->i_private = data;
simple_inode_init_ts(inode);
/* @data is now owned by the fs */
d_instantiate(dentry, inode);
if (cmpxchg(stashed, NULL, dentry)) {
d_delete(dentry); /* make sure ->d_prune() does nothing */
dput(dentry);
cpu_relax();
return ERR_PTR(-EAGAIN);
}
return dentry;
}
/**
* path_from_stashed - create path from stashed or new dentry
* @stashed: where to retrieve or stash dentry
* @ino: inode number to use
* @mnt: mnt of the filesystems to use
* @fops: file operations to use
* @data: data to store in inode->i_private
* @path: path to create
*
* The function tries to retrieve a stashed dentry from @stashed. If the dentry
* is still valid then it will be reused. If the dentry isn't able the function
* will allocate a new dentry and inode. It will then try to update @stashed
* with the newly added dentry. If it fails -EAGAIN is returned and the caller
* my retry.
*
* 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 or -EAGAIN is returned @data is owned by the
* filesystem.
*/
int path_from_stashed(struct dentry **stashed, unsigned long ino,
struct vfsmount *mnt, const struct file_operations *fops,
void *data, struct path *path)
{
struct dentry *dentry;
int ret = 0;
dentry = get_stashed_dentry(*stashed);
if (dentry)
goto out_path;
dentry = stash_dentry(stashed, ino, mnt->mnt_sb, fops, data);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
ret = 1;
out_path:
path->dentry = dentry;
path->mnt = mntget(mnt);
return ret;
}