[RFC PATCH 05/68] vfs: Provide a mount_pseudo-replacement for the new mount API

From: David Howells
Date: Wed Mar 27 2019 - 19:41:16 EST


Provide a function, vfs_init_pseudo_fs_context(), that provides a common
infrastructure for converting pseudo-filesystems that can never be
mountable.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
cc: linux-fsdevel@xxxxxxxxxxxxxxx
---

fs/libfs.c | 93 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/fs_context.h | 7 +++
2 files changed, 100 insertions(+)

diff --git a/fs/libfs.c b/fs/libfs.c
index 0fb590d79f30..ceaf69c885c2 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -16,6 +16,7 @@
#include <linux/exportfs.h>
#include <linux/writeback.h>
#include <linux/buffer_head.h> /* sync_mapping_buffers */
+#include <linux/fs_context.h>

#include <linux/uaccess.h>

@@ -233,6 +234,98 @@ static const struct super_operations simple_super_operations = {
.statfs = simple_statfs,
};

+struct pseudo_fs_context {
+ struct qstr d_name;
+ const struct super_operations *ops;
+ const struct xattr_handler **xattr;
+ const struct dentry_operations *dops;
+ unsigned long magic;
+};
+
+static int pseudo_fs_get_tree(struct fs_context *fc)
+{
+ struct pseudo_fs_context *ctx = fc->fs_private;
+ struct super_block *s;
+ struct dentry *dentry;
+ struct inode *root;
+
+ s = sget_userns(fc->fs_type, NULL, set_anon_super, SB_KERNMOUNT|SB_NOUSER,
+ &init_user_ns, NULL);
+ if (IS_ERR(s))
+ return PTR_ERR(s);
+
+ s->s_maxbytes = MAX_LFS_FILESIZE;
+ s->s_blocksize = PAGE_SIZE;
+ s->s_blocksize_bits = PAGE_SHIFT;
+ s->s_magic = ctx->magic;
+ s->s_op = ctx->ops ?: &simple_super_operations;
+ s->s_xattr = ctx->xattr;
+ s->s_time_gran = 1;
+ root = new_inode(s);
+ if (!root)
+ goto Enomem;
+ /*
+ * since this is the first inode, make it number 1. New inodes created
+ * after this must take care not to collide with it (by passing
+ * max_reserved of 1 to iunique).
+ */
+ root->i_ino = 1;
+ root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
+ root->i_atime = root->i_mtime = root->i_ctime = current_time(root);
+ dentry = __d_alloc(s, &ctx->d_name);
+ if (!dentry) {
+ iput(root);
+ goto Enomem;
+ }
+ d_instantiate(dentry, root);
+ s->s_root = dentry;
+ s->s_d_op = ctx->dops;
+ s->s_flags |= SB_ACTIVE;
+ fc->root = dget(s->s_root);
+ return 0;
+
+Enomem:
+ deactivate_locked_super(s);
+ return -ENOMEM;
+}
+
+static void pseudo_fs_free(struct fs_context *fc)
+{
+ kfree(fc->fs_private);
+}
+
+static const struct fs_context_operations pseudo_fs_context_ops = {
+ .free = pseudo_fs_free,
+ .get_tree = pseudo_fs_get_tree,
+};
+
+/*
+ * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
+ * will never be mountable)
+ */
+int vfs_init_pseudo_fs_context(struct fs_context *fc,
+ const char *name,
+ const struct super_operations *ops,
+ const struct xattr_handler **xattr,
+ const struct dentry_operations *dops,
+ unsigned long magic)
+{
+ struct pseudo_fs_context *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->d_name = (struct qstr)QSTR_INIT(name, strlen(name));
+ ctx->ops = ops;
+ ctx->xattr = xattr;
+ ctx->dops = dops;
+ ctx->magic = magic;
+ fc->fs_private = ctx;
+ fc->ops = &pseudo_fs_context_ops;
+ return 0;
+}
+EXPORT_SYMBOL(vfs_init_pseudo_fs_context);
+
/*
* Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
* will never be mountable)
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 98b44ad89378..a779022d06f5 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -121,6 +121,13 @@ extern int generic_parse_monolithic(struct fs_context *fc, void *data);
extern int vfs_get_tree(struct fs_context *fc);
extern void put_fs_context(struct fs_context *fc);

+extern int vfs_init_pseudo_fs_context(struct fs_context *fc,
+ const char *name,
+ const struct super_operations *ops,
+ const struct xattr_handler **xattr,
+ const struct dentry_operations *dops,
+ unsigned long magic);
+
/*
* sget() wrapper to be called from the ->get_tree() op.
*/