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. */