aboutsummaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/internal.h8
-rw-r--r--fs/libfs.c41
-rw-r--r--fs/nsfs.c33
-rw-r--r--fs/pidfs.c24
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);