From 43ce4c1feadbc84c772518a2d1974f6ba1b15089 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 21 Mar 2019 09:22:36 +0000 Subject: [PATCH 01/15] vfs: Add a single-or-reconfig keying to vfs_get_super() Add an additional keying mode to vfs_get_super() to indicate that only a single superblock should exist in the system, and that, if it does, further mounts should invoke reconfiguration upon it. This allows mount_single() to be replaced. [Fix by Eric Biggers folded in] Signed-off-by: David Howells Signed-off-by: Al Viro --- fs/super.c | 35 ++++++++++++++++++++++++++++------- include/linux/fs_context.h | 4 ++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/fs/super.c b/fs/super.c index da223b4cfbca..beaf076d9733 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1160,9 +1160,11 @@ int vfs_get_super(struct fs_context *fc, { int (*test)(struct super_block *, struct fs_context *); struct super_block *sb; + int err; switch (keying) { case vfs_get_single_super: + case vfs_get_single_reconf_super: test = test_single_super; break; case vfs_get_keyed_super: @@ -1180,18 +1182,29 @@ int vfs_get_super(struct fs_context *fc, return PTR_ERR(sb); if (!sb->s_root) { - int err = fill_super(sb, fc); - if (err) { - deactivate_locked_super(sb); - return err; - } + err = fill_super(sb, fc); + if (err) + goto error; sb->s_flags |= SB_ACTIVE; + fc->root = dget(sb->s_root); + } else { + fc->root = dget(sb->s_root); + if (keying == vfs_get_single_reconf_super) { + err = reconfigure_super(fc); + if (err < 0) { + dput(fc->root); + fc->root = NULL; + goto error; + } + } } - BUG_ON(fc->root); - fc->root = dget(sb->s_root); return 0; + +error: + deactivate_locked_super(sb); + return err; } EXPORT_SYMBOL(vfs_get_super); @@ -1211,6 +1224,14 @@ int get_tree_single(struct fs_context *fc, } EXPORT_SYMBOL(get_tree_single); +int get_tree_single_reconf(struct fs_context *fc, + int (*fill_super)(struct super_block *sb, + struct fs_context *fc)) +{ + return vfs_get_super(fc, vfs_get_single_reconf_super, fill_super); +} +EXPORT_SYMBOL(get_tree_single_reconf); + int get_tree_keyed(struct fs_context *fc, int (*fill_super)(struct super_block *sb, struct fs_context *fc), diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h index 84a5eaa09f19..0424df7f6e6b 100644 --- a/include/linux/fs_context.h +++ b/include/linux/fs_context.h @@ -141,6 +141,7 @@ extern void put_fs_context(struct fs_context *fc); */ enum vfs_get_super_keying { vfs_get_single_super, /* Only one such superblock may exist */ + vfs_get_single_reconf_super, /* As above, but reconfigure if it exists */ vfs_get_keyed_super, /* Superblocks with different s_fs_info keys may exist */ vfs_get_independent_super, /* Multiple independent superblocks may exist */ }; @@ -155,6 +156,9 @@ extern int get_tree_nodev(struct fs_context *fc, extern int get_tree_single(struct fs_context *fc, int (*fill_super)(struct super_block *sb, struct fs_context *fc)); +extern int get_tree_single_reconf(struct fs_context *fc, + int (*fill_super)(struct super_block *sb, + struct fs_context *fc)); extern int get_tree_keyed(struct fs_context *fc, int (*fill_super)(struct super_block *sb, struct fs_context *fc), From b941759985841f4be41002eacb93b2f32f835351 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:31 +0000 Subject: [PATCH 02/15] vfs: Convert romfs to use the new mount API Convert the romfs filesystem to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Signed-off-by: David Howells cc: linux-mtd@lists.infradead.org cc: linux-block@vger.kernel.org Signed-off-by: Al Viro --- fs/romfs/super.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/fs/romfs/super.c b/fs/romfs/super.c index 7d580f7c3f1d..8d198b9ecf8a 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -65,7 +65,7 @@ #include #include #include -#include +#include #include #include #include @@ -423,10 +423,10 @@ static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf) /* * remounting must involve read-only */ -static int romfs_remount(struct super_block *sb, int *flags, char *data) +static int romfs_reconfigure(struct fs_context *fc) { - sync_filesystem(sb); - *flags |= SB_RDONLY; + sync_filesystem(fc->root->d_sb); + fc->sb_flags |= SB_RDONLY; return 0; } @@ -434,7 +434,6 @@ static const struct super_operations romfs_super_ops = { .alloc_inode = romfs_alloc_inode, .free_inode = romfs_free_inode, .statfs = romfs_statfs, - .remount_fs = romfs_remount, }; /* @@ -457,7 +456,7 @@ static __u32 romfs_checksum(const void *data, int size) /* * fill in the superblock */ -static int romfs_fill_super(struct super_block *sb, void *data, int silent) +static int romfs_fill_super(struct super_block *sb, struct fs_context *fc) { struct romfs_super_block *rsb; struct inode *root; @@ -504,8 +503,8 @@ static int romfs_fill_super(struct super_block *sb, void *data, int silent) if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 || img_size < ROMFH_SIZE) { - if (!silent) - pr_warn("VFS: Can't find a romfs filesystem on dev %s.\n", + if (!(fc->sb_flags & SB_SILENT)) + errorf(fc, "VFS: Can't find a romfs filesystem on dev %s.\n", sb->s_id); goto error_rsb_inval; } @@ -518,7 +517,7 @@ static int romfs_fill_super(struct super_block *sb, void *data, int silent) storage = sb->s_mtd ? "MTD" : "the block layer"; len = strnlen(rsb->name, ROMFS_MAXFN); - if (!silent) + if (!(fc->sb_flags & SB_SILENT)) pr_notice("Mounting image '%*.*s' through %s\n", (unsigned) len, (unsigned) len, rsb->name, storage); @@ -548,23 +547,34 @@ error_rsb: /* * get a superblock for mounting */ -static struct dentry *romfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int romfs_get_tree(struct fs_context *fc) { - struct dentry *ret = ERR_PTR(-EINVAL); + int ret = -EINVAL; #ifdef CONFIG_ROMFS_ON_MTD - ret = mount_mtd(fs_type, flags, dev_name, data, romfs_fill_super); + ret = get_tree_mtd(fc, romfs_fill_super); #endif #ifdef CONFIG_ROMFS_ON_BLOCK - if (ret == ERR_PTR(-EINVAL)) - ret = mount_bdev(fs_type, flags, dev_name, data, - romfs_fill_super); + if (ret == -EINVAL) + ret = get_tree_bdev(fc, romfs_fill_super); #endif return ret; } +static const struct fs_context_operations romfs_context_ops = { + .get_tree = romfs_get_tree, + .reconfigure = romfs_reconfigure, +}; + +/* + * Set up the filesystem mount context. + */ +static int romfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &romfs_context_ops; + return 0; +} + /* * destroy a romfs superblock in the appropriate manner */ @@ -587,7 +597,7 @@ static void romfs_kill_sb(struct super_block *sb) static struct file_system_type romfs_fs_type = { .owner = THIS_MODULE, .name = "romfs", - .mount = romfs_mount, + .init_fs_context = romfs_init_fs_context, .kill_sb = romfs_kill_sb, .fs_flags = FS_REQUIRES_DEV, }; From 74f78fc5ef43d2bb676f212ffa4ad693dffc4141 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:32 +0000 Subject: [PATCH 03/15] vfs: Convert cramfs to use the new mount API Convert the cramfs filesystem to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Signed-off-by: David Howells Tested-by: Nicolas Pitre Acked-by: Nicolas Pitre cc: linux-mtd@lists.infradead.org cc: linux-block@vger.kernel.org Signed-off-by: Al Viro --- fs/cramfs/inode.c | 69 ++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 9352487bd0fc..e1cf433ac982 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -506,18 +507,19 @@ static void cramfs_kill_sb(struct super_block *sb) kfree(sbi); } -static int cramfs_remount(struct super_block *sb, int *flags, char *data) +static int cramfs_reconfigure(struct fs_context *fc) { - sync_filesystem(sb); - *flags |= SB_RDONLY; + sync_filesystem(fc->root->d_sb); + fc->sb_flags |= SB_RDONLY; return 0; } -static int cramfs_read_super(struct super_block *sb, - struct cramfs_super *super, int silent) +static int cramfs_read_super(struct super_block *sb, struct fs_context *fc, + struct cramfs_super *super) { struct cramfs_sb_info *sbi = CRAMFS_SB(sb); unsigned long root_offset; + bool silent = fc->sb_flags & SB_SILENT; /* We don't know the real size yet */ sbi->size = PAGE_SIZE; @@ -532,7 +534,7 @@ static int cramfs_read_super(struct super_block *sb, /* check for wrong endianness */ if (super->magic == CRAMFS_MAGIC_WEND) { if (!silent) - pr_err("wrong endianness\n"); + errorf(fc, "cramfs: wrong endianness"); return -EINVAL; } @@ -544,22 +546,22 @@ static int cramfs_read_super(struct super_block *sb, mutex_unlock(&read_mutex); if (super->magic != CRAMFS_MAGIC) { if (super->magic == CRAMFS_MAGIC_WEND && !silent) - pr_err("wrong endianness\n"); + errorf(fc, "cramfs: wrong endianness"); else if (!silent) - pr_err("wrong magic\n"); + errorf(fc, "cramfs: wrong magic"); return -EINVAL; } } /* get feature flags first */ if (super->flags & ~CRAMFS_SUPPORTED_FLAGS) { - pr_err("unsupported filesystem features\n"); + errorf(fc, "cramfs: unsupported filesystem features"); return -EINVAL; } /* Check that the root inode is in a sane state */ if (!S_ISDIR(super->root.mode)) { - pr_err("root is not a directory\n"); + errorf(fc, "cramfs: root is not a directory"); return -EINVAL; } /* correct strange, hard-coded permissions of mkcramfs */ @@ -578,12 +580,12 @@ static int cramfs_read_super(struct super_block *sb, sbi->magic = super->magic; sbi->flags = super->flags; if (root_offset == 0) - pr_info("empty filesystem"); + infof(fc, "cramfs: empty filesystem"); else if (!(super->flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) && ((root_offset != sizeof(struct cramfs_super)) && (root_offset != 512 + sizeof(struct cramfs_super)))) { - pr_err("bad root offset %lu\n", root_offset); + errorf(fc, "cramfs: bad root offset %lu", root_offset); return -EINVAL; } @@ -607,8 +609,7 @@ static int cramfs_finalize_super(struct super_block *sb, return 0; } -static int cramfs_blkdev_fill_super(struct super_block *sb, void *data, - int silent) +static int cramfs_blkdev_fill_super(struct super_block *sb, struct fs_context *fc) { struct cramfs_sb_info *sbi; struct cramfs_super super; @@ -623,14 +624,13 @@ static int cramfs_blkdev_fill_super(struct super_block *sb, void *data, for (i = 0; i < READ_BUFFERS; i++) buffer_blocknr[i] = -1; - err = cramfs_read_super(sb, &super, silent); + err = cramfs_read_super(sb, fc, &super); if (err) return err; return cramfs_finalize_super(sb, &super.root); } -static int cramfs_mtd_fill_super(struct super_block *sb, void *data, - int silent) +static int cramfs_mtd_fill_super(struct super_block *sb, struct fs_context *fc) { struct cramfs_sb_info *sbi; struct cramfs_super super; @@ -652,7 +652,7 @@ static int cramfs_mtd_fill_super(struct super_block *sb, void *data, pr_info("checking physical address %pap for linear cramfs image\n", &sbi->linear_phys_addr); - err = cramfs_read_super(sb, &super, silent); + err = cramfs_read_super(sb, fc, &super); if (err) return err; @@ -947,32 +947,41 @@ static const struct inode_operations cramfs_dir_inode_operations = { }; static const struct super_operations cramfs_ops = { - .remount_fs = cramfs_remount, .statfs = cramfs_statfs, }; -static struct dentry *cramfs_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) +static int cramfs_get_tree(struct fs_context *fc) { - struct dentry *ret = ERR_PTR(-ENOPROTOOPT); + int ret = -ENOPROTOOPT; if (IS_ENABLED(CONFIG_CRAMFS_MTD)) { - ret = mount_mtd(fs_type, flags, dev_name, data, - cramfs_mtd_fill_super); - if (!IS_ERR(ret)) + ret = get_tree_mtd(fc, cramfs_mtd_fill_super); + if (ret < 0) return ret; } - if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) { - ret = mount_bdev(fs_type, flags, dev_name, data, - cramfs_blkdev_fill_super); - } + if (IS_ENABLED(CONFIG_CRAMFS_BLOCKDEV)) + ret = get_tree_bdev(fc, cramfs_blkdev_fill_super); return ret; } +static const struct fs_context_operations cramfs_context_ops = { + .get_tree = cramfs_get_tree, + .reconfigure = cramfs_reconfigure, +}; + +/* + * Set up the filesystem mount context. + */ +static int cramfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &cramfs_context_ops; + return 0; +} + static struct file_system_type cramfs_fs_type = { .owner = THIS_MODULE, .name = "cramfs", - .mount = cramfs_mount, + .init_fs_context = cramfs_init_fs_context, .kill_sb = cramfs_kill_sb, .fs_flags = FS_REQUIRES_DEV, }; From ec10a24f10c8fbb6fd45701c795b6173e7dc80cc Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:32 +0000 Subject: [PATCH 04/15] vfs: Convert jffs2 to use the new mount API Convert the jffs2 filesystem to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Signed-off-by: David Howells cc: David Woodhouse cc: linux-mtd@lists.infradead.org Signed-off-by: Al Viro --- fs/jffs2/fs.c | 21 +++--- fs/jffs2/os-linux.h | 4 +- fs/jffs2/super.c | 172 +++++++++++++++++++++----------------------- 3 files changed, 96 insertions(+), 101 deletions(-) diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 8a20ddd25f2d..25736676a456 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -184,7 +185,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) { truncate_setsize(inode, iattr->ia_size); inode->i_blocks = (inode->i_size + 511) >> 9; - } + } return 0; } @@ -391,7 +392,7 @@ void jffs2_dirty_inode(struct inode *inode, int flags) jffs2_do_setattr(inode, &iattr); } -int jffs2_do_remount_fs(struct super_block *sb, int *flags, char *data) +int jffs2_do_remount_fs(struct super_block *sb, struct fs_context *fc) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); @@ -409,10 +410,10 @@ int jffs2_do_remount_fs(struct super_block *sb, int *flags, char *data) mutex_unlock(&c->alloc_sem); } - if (!(*flags & SB_RDONLY)) + if (!(fc->sb_flags & SB_RDONLY)) jffs2_start_garbage_collect_thread(c); - *flags |= SB_NOATIME; + fc->sb_flags |= SB_NOATIME; return 0; } @@ -509,7 +510,7 @@ static int calculate_inocache_hashsize(uint32_t flash_size) return hashsize; } -int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) +int jffs2_do_fill_super(struct super_block *sb, struct fs_context *fc) { struct jffs2_sb_info *c; struct inode *root_i; @@ -524,11 +525,11 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) #ifndef CONFIG_JFFS2_FS_WRITEBUFFER if (c->mtd->type == MTD_NANDFLASH) { - pr_err("Cannot operate on NAND flash unless jffs2 NAND support is compiled in\n"); + errorf(fc, "Cannot operate on NAND flash unless jffs2 NAND support is compiled in"); return -EINVAL; } if (c->mtd->type == MTD_DATAFLASH) { - pr_err("Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in\n"); + errorf(fc, "Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in"); return -EINVAL; } #endif @@ -542,12 +543,12 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) */ if ((c->sector_size * blocks) != c->flash_size) { c->flash_size = c->sector_size * blocks; - pr_info("Flash size not aligned to erasesize, reducing to %dKiB\n", - c->flash_size / 1024); + infof(fc, "Flash size not aligned to erasesize, reducing to %dKiB", + c->flash_size / 1024); } if (c->flash_size < 5*c->sector_size) { - pr_err("Too few erase blocks (%d)\n", + errorf(fc, "Too few erase blocks (%d)", c->flash_size / c->sector_size); return -EINVAL; } diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index bd3d5f0ddc34..21071fc2975d 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -172,8 +172,8 @@ void jffs2_dirty_inode(struct inode *inode, int flags); struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_raw_inode *ri); int jffs2_statfs (struct dentry *, struct kstatfs *); -int jffs2_do_remount_fs(struct super_block *, int *, char *); -int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); +int jffs2_do_remount_fs(struct super_block *sb, struct fs_context *fc); +int jffs2_do_fill_super(struct super_block *sb, struct fs_context *fc); void jffs2_gc_release_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index af4aa6599473..cbe70637c117 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -19,7 +19,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -157,96 +158,77 @@ static const struct export_operations jffs2_export_ops = { /* * JFFS2 mount options. * + * Opt_source: The source device * Opt_override_compr: override default compressor * Opt_rp_size: size of reserved pool in KiB - * Opt_err: just end of array marker */ enum { + Opt_source, Opt_override_compr, Opt_rp_size, - Opt_err, }; -static const match_table_t tokens = { - {Opt_override_compr, "compr=%s"}, - {Opt_rp_size, "rp_size=%u"}, - {Opt_err, NULL}, +static const struct fs_parameter_spec jffs2_param_specs[] = { + fsparam_string ("source", Opt_source), + fsparam_enum ("compr", Opt_override_compr), + fsparam_u32 ("rp_size", Opt_rp_size), + {} }; -static int jffs2_parse_options(struct jffs2_sb_info *c, char *data) -{ - substring_t args[MAX_OPT_ARGS]; - char *p, *name; - unsigned int opt; - - if (!data) - return 0; - - while ((p = strsep(&data, ","))) { - int token; - - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_override_compr: - name = match_strdup(&args[0]); - - if (!name) - return -ENOMEM; - if (!strcmp(name, "none")) - c->mount_opts.compr = JFFS2_COMPR_MODE_NONE; +static const struct fs_parameter_enum jffs2_param_enums[] = { + { Opt_override_compr, "none", JFFS2_COMPR_MODE_NONE }, #ifdef CONFIG_JFFS2_LZO - else if (!strcmp(name, "lzo")) - c->mount_opts.compr = JFFS2_COMPR_MODE_FORCELZO; + { Opt_override_compr, "lzo", JFFS2_COMPR_MODE_FORCELZO }, #endif #ifdef CONFIG_JFFS2_ZLIB - else if (!strcmp(name, "zlib")) - c->mount_opts.compr = - JFFS2_COMPR_MODE_FORCEZLIB; + { Opt_override_compr, "zlib", JFFS2_COMPR_MODE_FORCEZLIB }, #endif - else { - pr_err("Error: unknown compressor \"%s\"\n", - name); - kfree(name); - return -EINVAL; - } - kfree(name); - c->mount_opts.override_compr = true; - break; - case Opt_rp_size: - if (match_int(&args[0], &opt)) - return -EINVAL; - opt *= 1024; - if (opt > c->mtd->size) { - pr_warn("Too large reserve pool specified, max " - "is %llu KB\n", c->mtd->size / 1024); - return -EINVAL; - } - c->mount_opts.rp_size = opt; - break; - default: - pr_err("Error: unrecognized mount option '%s' or missing value\n", - p); - return -EINVAL; - } + {} +}; + +const struct fs_parameter_description jffs2_fs_parameters = { + .name = "jffs2", + .specs = jffs2_param_specs, + .enums = jffs2_param_enums, +}; + +static int jffs2_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct fs_parse_result result; + struct jffs2_sb_info *c = fc->s_fs_info; + int opt; + + opt = fs_parse(fc, &jffs2_fs_parameters, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_override_compr: + c->mount_opts.compr = result.uint_32; + c->mount_opts.override_compr = true; + break; + case Opt_rp_size: + if (result.uint_32 > UINT_MAX / 1024) + return invalf(fc, "jffs2: rp_size unrepresentable"); + opt = result.uint_32 * 1024; + if (opt > c->mtd->size) + return invalf(fc, "jffs2: Too large reserve pool specified, max is %llu KB", + c->mtd->size / 1024); + c->mount_opts.rp_size = opt; + break; + default: + return -EINVAL; } return 0; } -static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data) +static int jffs2_reconfigure(struct fs_context *fc) { - struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); - int err; + struct super_block *sb = fc->root->d_sb; sync_filesystem(sb); - err = jffs2_parse_options(c, data); - if (err) - return -EINVAL; - - return jffs2_do_remount_fs(sb, flags, data); + return jffs2_do_remount_fs(sb, fc); } static const struct super_operations jffs2_super_operations = @@ -255,7 +237,6 @@ static const struct super_operations jffs2_super_operations = .free_inode = jffs2_free_inode, .put_super = jffs2_put_super, .statfs = jffs2_statfs, - .remount_fs = jffs2_remount_fs, .evict_inode = jffs2_evict_inode, .dirty_inode = jffs2_dirty_inode, .show_options = jffs2_show_options, @@ -265,26 +246,16 @@ static const struct super_operations jffs2_super_operations = /* * fill in the superblock */ -static int jffs2_fill_super(struct super_block *sb, void *data, int silent) +static int jffs2_fill_super(struct super_block *sb, struct fs_context *fc) { - struct jffs2_sb_info *c; - int ret; + struct jffs2_sb_info *c = sb->s_fs_info; jffs2_dbg(1, "jffs2_get_sb_mtd():" " New superblock for device %d (\"%s\")\n", sb->s_mtd->index, sb->s_mtd->name); - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return -ENOMEM; - c->mtd = sb->s_mtd; c->os_priv = sb; - sb->s_fs_info = c; - - ret = jffs2_parse_options(c, data); - if (ret) - return -EINVAL; /* Initialize JFFS2 superblock locks, the further initialization will * be done later */ @@ -302,15 +273,37 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent) #ifdef CONFIG_JFFS2_FS_POSIX_ACL sb->s_flags |= SB_POSIXACL; #endif - ret = jffs2_do_fill_super(sb, data, silent); - return ret; + return jffs2_do_fill_super(sb, fc); } -static struct dentry *jffs2_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int jffs2_get_tree(struct fs_context *fc) { - return mount_mtd(fs_type, flags, dev_name, data, jffs2_fill_super); + return get_tree_mtd(fc, jffs2_fill_super); +} + +static void jffs2_free_fc(struct fs_context *fc) +{ + kfree(fc->s_fs_info); +} + +static const struct fs_context_operations jffs2_context_ops = { + .free = jffs2_free_fc, + .parse_param = jffs2_parse_param, + .get_tree = jffs2_get_tree, + .reconfigure = jffs2_reconfigure, +}; + +static int jffs2_init_fs_context(struct fs_context *fc) +{ + struct jffs2_sb_info *ctx; + + ctx = kzalloc(sizeof(struct jffs2_sb_info), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + fc->s_fs_info = ctx; + fc->ops = &jffs2_context_ops; + return 0; } static void jffs2_put_super (struct super_block *sb) @@ -347,7 +340,8 @@ static void jffs2_kill_sb(struct super_block *sb) static struct file_system_type jffs2_fs_type = { .owner = THIS_MODULE, .name = "jffs2", - .mount = jffs2_mount, + .init_fs_context = jffs2_init_fs_context, + .parameters = &jffs2_fs_parameters, .kill_sb = jffs2_kill_sb, }; MODULE_ALIAS_FS("jffs2"); From 6d56e4184368e4fe611df0cd5aee78431f5eda3e Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:32 +0000 Subject: [PATCH 05/15] mtd: Kill mount_mtd() Kill mount_mtd() as it has now been replaced by vfs_get_mtd_super() in the new mount API and nothing now uses it. Signed-off-by: David Howells cc: David Woodhouse cc: Brian Norris cc: Boris Brezillon cc: Marek Vasut cc: Richard Weinberger cc: linux-mtd@lists.infradead.org Signed-off-by: Al Viro --- drivers/mtd/mtdsuper.c | 189 -------------------------------------- include/linux/mtd/super.h | 3 - 2 files changed, 192 deletions(-) diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index 3f9a3b7b12c5..c3e2098372f2 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -193,195 +193,6 @@ int get_tree_mtd(struct fs_context *fc, } EXPORT_SYMBOL_GPL(get_tree_mtd); -/* - * compare superblocks to see if they're equivalent - * - they are if the underlying MTD device is the same - */ -static int get_sb_mtd_compare(struct super_block *sb, void *_mtd) -{ - struct mtd_info *mtd = _mtd; - - if (sb->s_mtd == mtd) { - pr_debug("MTDSB: Match on device %d (\"%s\")\n", - mtd->index, mtd->name); - return 1; - } - - pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n", - sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name); - return 0; -} - -/* - * mark the superblock by the MTD device it is using - * - set the device number to be the correct MTD block device for pesuperstence - * of NFS exports - */ -static int get_sb_mtd_set(struct super_block *sb, void *_mtd) -{ - struct mtd_info *mtd = _mtd; - - sb->s_mtd = mtd; - sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index); - sb->s_bdi = bdi_get(mtd_bdi); - - return 0; -} - -/* - * get a superblock on an MTD-backed filesystem - */ -static struct dentry *mount_mtd_aux(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - struct mtd_info *mtd, - int (*fill_super)(struct super_block *, void *, int)) -{ - struct super_block *sb; - int ret; - - sb = sget(fs_type, get_sb_mtd_compare, get_sb_mtd_set, flags, mtd); - if (IS_ERR(sb)) - goto out_error; - - if (sb->s_root) - goto already_mounted; - - /* fresh new superblock */ - pr_debug("MTDSB: New superblock for device %d (\"%s\")\n", - mtd->index, mtd->name); - - ret = fill_super(sb, data, flags & SB_SILENT ? 1 : 0); - if (ret < 0) { - deactivate_locked_super(sb); - return ERR_PTR(ret); - } - - /* go */ - sb->s_flags |= SB_ACTIVE; - return dget(sb->s_root); - - /* new mountpoint for an already mounted superblock */ -already_mounted: - pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n", - mtd->index, mtd->name); - put_mtd_device(mtd); - return dget(sb->s_root); - -out_error: - put_mtd_device(mtd); - return ERR_CAST(sb); -} - -/* - * get a superblock on an MTD-backed filesystem by MTD device number - */ -static struct dentry *mount_mtd_nr(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, int mtdnr, - int (*fill_super)(struct super_block *, void *, int)) -{ - struct mtd_info *mtd; - - mtd = get_mtd_device(NULL, mtdnr); - if (IS_ERR(mtd)) { - pr_debug("MTDSB: Device #%u doesn't appear to exist\n", mtdnr); - return ERR_CAST(mtd); - } - - return mount_mtd_aux(fs_type, flags, dev_name, data, mtd, fill_super); -} - -/* - * set up an MTD-based superblock - */ -struct dentry *mount_mtd(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - int (*fill_super)(struct super_block *, void *, int)) -{ -#ifdef CONFIG_BLOCK - struct block_device *bdev; - int ret, major; -#endif - int mtdnr; - - if (!dev_name) - return ERR_PTR(-EINVAL); - - pr_debug("MTDSB: dev_name \"%s\"\n", dev_name); - - /* the preferred way of mounting in future; especially when - * CONFIG_BLOCK=n - we specify the underlying MTD device by number or - * by name, so that we don't require block device support to be present - * in the kernel. */ - if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { - if (dev_name[3] == ':') { - struct mtd_info *mtd; - - /* mount by MTD device name */ - pr_debug("MTDSB: mtd:%%s, name \"%s\"\n", - dev_name + 4); - - mtd = get_mtd_device_nm(dev_name + 4); - if (!IS_ERR(mtd)) - return mount_mtd_aux( - fs_type, flags, - dev_name, data, mtd, - fill_super); - - printk(KERN_NOTICE "MTD:" - " MTD device with name \"%s\" not found.\n", - dev_name + 4); - - } else if (isdigit(dev_name[3])) { - /* mount by MTD device number name */ - char *endptr; - - mtdnr = simple_strtoul(dev_name + 3, &endptr, 0); - if (!*endptr) { - /* It was a valid number */ - pr_debug("MTDSB: mtd%%d, mtdnr %d\n", - mtdnr); - return mount_mtd_nr(fs_type, flags, - dev_name, data, - mtdnr, fill_super); - } - } - } - -#ifdef CONFIG_BLOCK - /* try the old way - the hack where we allowed users to mount - * /dev/mtdblock$(n) but didn't actually _use_ the blockdev - */ - bdev = lookup_bdev(dev_name); - if (IS_ERR(bdev)) { - ret = PTR_ERR(bdev); - pr_debug("MTDSB: lookup_bdev() returned %d\n", ret); - return ERR_PTR(ret); - } - pr_debug("MTDSB: lookup_bdev() returned 0\n"); - - ret = -EINVAL; - - major = MAJOR(bdev->bd_dev); - mtdnr = MINOR(bdev->bd_dev); - bdput(bdev); - - if (major != MTD_BLOCK_MAJOR) - goto not_an_MTD_device; - - return mount_mtd_nr(fs_type, flags, dev_name, data, mtdnr, fill_super); - -not_an_MTD_device: -#endif /* CONFIG_BLOCK */ - - if (!(flags & SB_SILENT)) - printk(KERN_NOTICE - "MTD: Attempt to mount non-MTD device \"%s\"\n", - dev_name); - return ERR_PTR(-EINVAL); -} - -EXPORT_SYMBOL_GPL(mount_mtd); - /* * destroy an MTD-based superblock */ diff --git a/include/linux/mtd/super.h b/include/linux/mtd/super.h index 42db3f8e8136..3608a6c36fac 100644 --- a/include/linux/mtd/super.h +++ b/include/linux/mtd/super.h @@ -17,9 +17,6 @@ extern int get_tree_mtd(struct fs_context *fc, int (*fill_super)(struct super_block *sb, struct fs_context *fc)); -extern struct dentry *mount_mtd(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - int (*fill_super)(struct super_block *, void *, int)); extern void kill_mtd_super(struct super_block *sb); From 5a2be1288b514d74acdb3f0131d4d8fa3d689f06 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:32 +0000 Subject: [PATCH 06/15] vfs: Convert squashfs to use the new mount API Convert the squashfs filesystem to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Signed-off-by: David Howells cc: Phillip Lougher cc: squashfs-devel@lists.sourceforge.net Signed-off-by: Al Viro --- fs/squashfs/super.c | 100 ++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index effa638d6d85..f908c6eb1b39 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -17,6 +17,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -36,26 +37,27 @@ static struct file_system_type squashfs_fs_type; static const struct super_operations squashfs_super_ops; -static const struct squashfs_decompressor *supported_squashfs_filesystem(short - major, short minor, short id) +static const struct squashfs_decompressor *supported_squashfs_filesystem( + struct fs_context *fc, + short major, short minor, short id) { const struct squashfs_decompressor *decompressor; if (major < SQUASHFS_MAJOR) { - ERROR("Major/Minor mismatch, older Squashfs %d.%d " - "filesystems are unsupported\n", major, minor); + errorf(fc, "Major/Minor mismatch, older Squashfs %d.%d " + "filesystems are unsupported", major, minor); return NULL; } else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) { - ERROR("Major/Minor mismatch, trying to mount newer " - "%d.%d filesystem\n", major, minor); - ERROR("Please update your kernel\n"); + errorf(fc, "Major/Minor mismatch, trying to mount newer " + "%d.%d filesystem", major, minor); + errorf(fc, "Please update your kernel"); return NULL; } decompressor = squashfs_lookup_decompressor(id); if (!decompressor->supported) { - ERROR("Filesystem uses \"%s\" compression. This is not " - "supported\n", decompressor->name); + errorf(fc, "Filesystem uses \"%s\" compression. This is not supported", + decompressor->name); return NULL; } @@ -63,7 +65,7 @@ static const struct squashfs_decompressor *supported_squashfs_filesystem(short } -static int squashfs_fill_super(struct super_block *sb, void *data, int silent) +static int squashfs_fill_super(struct super_block *sb, struct fs_context *fc) { struct squashfs_sb_info *msblk; struct squashfs_super_block *sblk = NULL; @@ -98,7 +100,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) sblk = squashfs_read_table(sb, SQUASHFS_START, sizeof(*sblk)); if (IS_ERR(sblk)) { - ERROR("unable to read squashfs_super_block\n"); + errorf(fc, "unable to read squashfs_super_block"); err = PTR_ERR(sblk); sblk = NULL; goto failed_mount; @@ -109,14 +111,15 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) /* Check it is a SQUASHFS superblock */ sb->s_magic = le32_to_cpu(sblk->s_magic); if (sb->s_magic != SQUASHFS_MAGIC) { - if (!silent) - ERROR("Can't find a SQUASHFS superblock on %pg\n", - sb->s_bdev); + if (!(fc->sb_flags & SB_SILENT)) + errorf(fc, "Can't find a SQUASHFS superblock on %pg", + sb->s_bdev); goto failed_mount; } /* Check the MAJOR & MINOR versions and lookup compression type */ msblk->decompressor = supported_squashfs_filesystem( + fc, le16_to_cpu(sblk->s_major), le16_to_cpu(sblk->s_minor), le16_to_cpu(sblk->compression)); @@ -133,15 +136,15 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) /* Check block size for sanity */ msblk->block_size = le32_to_cpu(sblk->block_size); if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE) - goto failed_mount; + goto insanity; /* * Check the system page size is not larger than the filesystem * block size (by default 128K). This is currently not supported. */ if (PAGE_SIZE > msblk->block_size) { - ERROR("Page size > filesystem block size (%d). This is " - "currently not supported!\n", msblk->block_size); + errorf(fc, "Page size > filesystem block size (%d). This is " + "currently not supported!", msblk->block_size); goto failed_mount; } @@ -152,12 +155,12 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) /* Check that block_size and block_log match */ if (msblk->block_size != (1 << msblk->block_log)) - goto failed_mount; + goto insanity; /* Check the root inode for sanity */ root_inode = le64_to_cpu(sblk->root_inode); if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE) - goto failed_mount; + goto insanity; msblk->inode_table = le64_to_cpu(sblk->inode_table_start); msblk->directory_table = le64_to_cpu(sblk->directory_table_start); @@ -197,7 +200,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->read_page = squashfs_cache_init("data", squashfs_max_decompressors(), msblk->block_size); if (msblk->read_page == NULL) { - ERROR("Failed to allocate read_page block\n"); + errorf(fc, "Failed to allocate read_page block"); goto failed_mount; } @@ -205,7 +208,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) if (IS_ERR(msblk->stream)) { err = PTR_ERR(msblk->stream); msblk->stream = NULL; - goto failed_mount; + goto insanity; } /* Handle xattrs */ @@ -220,7 +223,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->xattr_id_table = squashfs_read_xattr_id_table(sb, xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids); if (IS_ERR(msblk->xattr_id_table)) { - ERROR("unable to read xattr id index table\n"); + errorf(fc, "unable to read xattr id index table"); err = PTR_ERR(msblk->xattr_id_table); msblk->xattr_id_table = NULL; if (err != -ENOTSUPP) @@ -234,7 +237,7 @@ allocate_id_index_table: le64_to_cpu(sblk->id_table_start), next_table, le16_to_cpu(sblk->no_ids)); if (IS_ERR(msblk->id_table)) { - ERROR("unable to read id index table\n"); + errorf(fc, "unable to read id index table"); err = PTR_ERR(msblk->id_table); msblk->id_table = NULL; goto failed_mount; @@ -250,7 +253,7 @@ allocate_id_index_table: msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb, lookup_table_start, next_table, msblk->inodes); if (IS_ERR(msblk->inode_lookup_table)) { - ERROR("unable to read inode lookup table\n"); + errorf(fc, "unable to read inode lookup table"); err = PTR_ERR(msblk->inode_lookup_table); msblk->inode_lookup_table = NULL; goto failed_mount; @@ -275,7 +278,7 @@ handle_fragments: msblk->fragment_index = squashfs_read_fragment_index_table(sb, le64_to_cpu(sblk->fragment_table_start), next_table, fragments); if (IS_ERR(msblk->fragment_index)) { - ERROR("unable to read fragment index table\n"); + errorf(fc, "unable to read fragment index table"); err = PTR_ERR(msblk->fragment_index); msblk->fragment_index = NULL; goto failed_mount; @@ -286,13 +289,13 @@ check_directory_table: /* Sanity check directory_table */ if (msblk->directory_table > next_table) { err = -EINVAL; - goto failed_mount; + goto insanity; } /* Sanity check inode_table */ if (msblk->inode_table >= msblk->directory_table) { err = -EINVAL; - goto failed_mount; + goto insanity; } /* allocate root */ @@ -321,6 +324,8 @@ check_directory_table: kfree(sblk); return 0; +insanity: + errorf(fc, "squashfs image failed sanity check"); failed_mount: squashfs_cache_delete(msblk->block_cache); squashfs_cache_delete(msblk->fragment_cache); @@ -336,6 +341,28 @@ failed_mount: return err; } +static int squashfs_get_tree(struct fs_context *fc) +{ + return get_tree_bdev(fc, squashfs_fill_super); +} + +static int squashfs_reconfigure(struct fs_context *fc) +{ + sync_filesystem(fc->root->d_sb); + fc->sb_flags |= SB_RDONLY; + return 0; +} + +static const struct fs_context_operations squashfs_context_ops = { + .get_tree = squashfs_get_tree, + .reconfigure = squashfs_reconfigure, +}; + +static int squashfs_init_fs_context(struct fs_context *fc) +{ + fc->ops = &squashfs_context_ops; + return 0; +} static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) { @@ -358,14 +385,6 @@ static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) } -static int squashfs_remount(struct super_block *sb, int *flags, char *data) -{ - sync_filesystem(sb); - *flags |= SB_RDONLY; - return 0; -} - - static void squashfs_put_super(struct super_block *sb) { if (sb->s_fs_info) { @@ -384,14 +403,6 @@ static void squashfs_put_super(struct super_block *sb) } } - -static struct dentry *squashfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) -{ - return mount_bdev(fs_type, flags, dev_name, data, squashfs_fill_super); -} - - static struct kmem_cache *squashfs_inode_cachep; @@ -468,7 +479,7 @@ static void squashfs_free_inode(struct inode *inode) static struct file_system_type squashfs_fs_type = { .owner = THIS_MODULE, .name = "squashfs", - .mount = squashfs_mount, + .init_fs_context = squashfs_init_fs_context, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV }; @@ -479,7 +490,6 @@ static const struct super_operations squashfs_super_ops = { .free_inode = squashfs_free_inode, .statfs = squashfs_statfs, .put_super = squashfs_put_super, - .remount_fs = squashfs_remount }; module_init(init_squashfs_fs); From d401727ea0d7a48eaa01a8089f6b91a8121dcaac Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 1 Jun 2019 18:43:09 -0400 Subject: [PATCH 07/15] devtmpfs: don't mix {ramfs,shmem}_fill_super() with mount_single() Create an internal-only type matching the current devtmpfs, never register it and have one kernel-internal mount done. That thing gets mounted only once, so it is free to use mount_nodev(). The "public" devtmpfs (the one we do register, and only after the internal mount of the real thing is done) simply gets and returns an extra reference to the internal superblock. Signed-off-by: Al Viro --- drivers/base/devtmpfs.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index ba5c80903efe..bafdf86b0497 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -56,22 +56,38 @@ static int __init mount_param(char *str) } __setup("devtmpfs.mount=", mount_param); +static struct vfsmount *mnt; + +static struct dentry *public_dev_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) +{ + struct super_block *s = mnt->mnt_sb; + atomic_inc(&s->s_active); + down_write(&s->s_umount); + return dget(s->s_root); +} + static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { #ifdef CONFIG_TMPFS - return mount_single(fs_type, flags, data, shmem_fill_super); + return mount_nodev(fs_type, flags, data, shmem_fill_super); #else - return mount_single(fs_type, flags, data, ramfs_fill_super); + return ramfs_mount(fs_type, flags, dev_name, data); #endif } -static struct file_system_type dev_fs_type = { +static struct file_system_type internal_fs_type = { .name = "devtmpfs", .mount = dev_mount, .kill_sb = kill_litter_super, }; +static struct file_system_type dev_fs_type = { + .name = "devtmpfs", + .mount = public_dev_mount, +}; + #ifdef CONFIG_BLOCK static inline int is_blockdev(struct device *dev) { @@ -378,12 +394,11 @@ static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, static int devtmpfsd(void *p) { - char options[] = "mode=0755"; int *err = p; *err = ksys_unshare(CLONE_NEWNS); if (*err) goto out; - *err = ksys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, options); + *err = ksys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); if (*err) goto out; ksys_chdir("/.."); /* will traverse into overmounted root */ @@ -420,7 +435,16 @@ out: */ int __init devtmpfs_init(void) { - int err = register_filesystem(&dev_fs_type); + char opts[] = "mode=0755"; + int err; + + mnt = vfs_kern_mount(&internal_fs_type, 0, "devtmpfs", opts); + if (IS_ERR(mnt)) { + printk(KERN_ERR "devtmpfs: unable to create devtmpfs %ld\n", + PTR_ERR(mnt)); + return PTR_ERR(mnt); + } + err = register_filesystem(&dev_fs_type); if (err) { printk(KERN_ERR "devtmpfs: unable to register devtmpfs " "type %i\n", err); From df02450217c98e01b8b22f805314470df71f2b9b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 1 Jun 2019 18:51:15 -0400 Subject: [PATCH 08/15] make ramfs_fill_super() static all users should just call ramfs_mount() Signed-off-by: Al Viro --- fs/ramfs/inode.c | 2 +- include/linux/ramfs.h | 2 -- init/do_mounts.c | 6 ++---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 733c6b4193dc..b85d1e77e934 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -217,7 +217,7 @@ static int ramfs_parse_options(char *data, struct ramfs_mount_opts *opts) return 0; } -int ramfs_fill_super(struct super_block *sb, void *data, int silent) +static int ramfs_fill_super(struct super_block *sb, void *data, int silent) { struct ramfs_fs_info *fsi; struct inode *inode; diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index ee582bdb7fda..e4d7d141545e 100644 --- a/include/linux/ramfs.h +++ b/include/linux/ramfs.h @@ -20,6 +20,4 @@ extern int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize); extern const struct file_operations ramfs_file_operations; extern const struct vm_operations_struct generic_file_vm_ops; -int ramfs_fill_super(struct super_block *sb, void *data, int silent); - #endif diff --git a/init/do_mounts.c b/init/do_mounts.c index 53cb37b66227..baedc2ef579e 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -630,12 +630,10 @@ static bool is_tmpfs; static struct dentry *rootfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - void *fill = ramfs_fill_super; - if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - fill = shmem_fill_super; + return mount_nodev(fs_type, flags, data, shmem_fill_super); - return mount_nodev(fs_type, flags, data, fill); + return ramfs_mount(fs_type, flags, dev_name, data); } struct file_system_type rootfs_fs_type = { From 7e30d2a5eb0b2d5853f06cb8a2d44937d80a6bd6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 1 Jun 2019 18:56:53 -0400 Subject: [PATCH 09/15] make shmem_fill_super() static ... have callers use shmem_mount() Signed-off-by: Al Viro --- drivers/base/devtmpfs.c | 2 +- include/linux/shmem_fs.h | 3 ++- init/do_mounts.c | 2 +- mm/shmem.c | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index bafdf86b0497..dabf5006a254 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -71,7 +71,7 @@ static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { #ifdef CONFIG_TMPFS - return mount_nodev(fs_type, flags, data, shmem_fill_super); + return shmem_mount(fs_type, flags, dev_name, data); #else return ramfs_mount(fs_type, flags, dev_name, data); #endif diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 20d815a33145..88cb98b0e49b 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -50,7 +50,8 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) * Functions in mm/shmem.c called directly from elsewhere: */ extern int shmem_init(void); -extern int shmem_fill_super(struct super_block *sb, void *data, int silent); +extern struct dentry *shmem_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data); extern struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); extern struct file *shmem_kernel_file_setup(const char *name, loff_t size, diff --git a/init/do_mounts.c b/init/do_mounts.c index baedc2ef579e..16c29e57f224 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -631,7 +631,7 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - return mount_nodev(fs_type, flags, data, shmem_fill_super); + return shmem_mount(fs_type, flags, dev_name, data); return ramfs_mount(fs_type, flags, dev_name, data); } diff --git a/mm/shmem.c b/mm/shmem.c index 2bed4761f279..40f574c06375 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3547,7 +3547,7 @@ static void shmem_put_super(struct super_block *sb) sb->s_fs_info = NULL; } -int shmem_fill_super(struct super_block *sb, void *data, int silent) +static int shmem_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct shmem_sb_info *sbinfo; @@ -3759,7 +3759,7 @@ static const struct vm_operations_struct shmem_vm_ops = { #endif }; -static struct dentry *shmem_mount(struct file_system_type *fs_type, +struct dentry *shmem_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, shmem_fill_super); From 0b5071dd323da2e277bce7e68749dc0a5fba4703 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 18:49:18 -0400 Subject: [PATCH 10/15] shmem_parse_options(): use a separate structure to keep the results ... and copy the data from it into sbinfo in the callers. For use by remount we need to keep track whether there'd been options setting max_inodes, max_blocks and huge resp. and do the sanity checks (and copying) only if such options had been seen. uid/gid/mode is ignored by remount and NULL mpol is already explicitly treated as "ignore it", so we don't need to keep track of those. Note: theoretically, mpol_parse_string() may return NULL not in case of error (for default policy), so the assumption that NULL mpol means "change nothing" is incorrect. However, that's the mainline behaviour and any changes belong in a separate patch. If we go for that, we'll need to keep track of having encountered mpol= option too. [changes in remount logics from Hugh Dickins folded] Signed-off-by: Al Viro --- mm/shmem.c | 110 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 40f574c06375..98aa066c00f7 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -107,6 +107,20 @@ struct shmem_falloc { pgoff_t nr_unswapped; /* how often writepage refused to swap out */ }; +struct shmem_options { + unsigned long long blocks; + unsigned long long inodes; + struct mempolicy *mpol; + kuid_t uid; + kgid_t gid; + umode_t mode; + int huge; + int seen; +#define SHMEM_SEEN_BLOCKS 1 +#define SHMEM_SEEN_INODES 2 +#define SHMEM_SEEN_HUGE 4 +}; + #ifdef CONFIG_TMPFS static unsigned long shmem_default_max_blocks(void) { @@ -3349,8 +3363,7 @@ static const struct export_operations shmem_export_ops = { .fh_to_dentry = shmem_fh_to_dentry, }; -static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, - bool remount) +static int shmem_parse_options(char *options, struct shmem_options *ctx) { char *this_char, *value, *rest; struct mempolicy *mpol = NULL; @@ -3395,39 +3408,35 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, } if (*rest) goto bad_val; - sbinfo->max_blocks = - DIV_ROUND_UP(size, PAGE_SIZE); + ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); + ctx->seen |= SHMEM_SEEN_BLOCKS; } else if (!strcmp(this_char,"nr_blocks")) { - sbinfo->max_blocks = memparse(value, &rest); + ctx->blocks = memparse(value, &rest); if (*rest) goto bad_val; + ctx->seen |= SHMEM_SEEN_BLOCKS; } else if (!strcmp(this_char,"nr_inodes")) { - sbinfo->max_inodes = memparse(value, &rest); + ctx->inodes = memparse(value, &rest); if (*rest) goto bad_val; + ctx->seen |= SHMEM_SEEN_INODES; } else if (!strcmp(this_char,"mode")) { - if (remount) - continue; - sbinfo->mode = simple_strtoul(value, &rest, 8) & 07777; + ctx->mode = simple_strtoul(value, &rest, 8) & 07777; if (*rest) goto bad_val; } else if (!strcmp(this_char,"uid")) { - if (remount) - continue; uid = simple_strtoul(value, &rest, 0); if (*rest) goto bad_val; - sbinfo->uid = make_kuid(current_user_ns(), uid); - if (!uid_valid(sbinfo->uid)) + ctx->uid = make_kuid(current_user_ns(), uid); + if (!uid_valid(ctx->uid)) goto bad_val; } else if (!strcmp(this_char,"gid")) { - if (remount) - continue; gid = simple_strtoul(value, &rest, 0); if (*rest) goto bad_val; - sbinfo->gid = make_kgid(current_user_ns(), gid); - if (!gid_valid(sbinfo->gid)) + ctx->gid = make_kgid(current_user_ns(), gid); + if (!gid_valid(ctx->gid)) goto bad_val; #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE } else if (!strcmp(this_char, "huge")) { @@ -3438,7 +3447,8 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, if (!has_transparent_hugepage() && huge != SHMEM_HUGE_NEVER) goto bad_val; - sbinfo->huge = huge; + ctx->huge = huge; + ctx->seen |= SHMEM_SEEN_HUGE; #endif #ifdef CONFIG_NUMA } else if (!strcmp(this_char,"mpol")) { @@ -3452,7 +3462,7 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, goto error; } } - sbinfo->mpol = mpol; + ctx->mpol = mpol; return 0; bad_val: @@ -3467,42 +3477,50 @@ error: static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); - struct shmem_sb_info config = *sbinfo; + struct shmem_options ctx = {.seen = 0}; unsigned long inodes; int error = -EINVAL; - config.mpol = NULL; - if (shmem_parse_options(data, &config, true)) + if (shmem_parse_options(data, &ctx)) return error; spin_lock(&sbinfo->stat_lock); inodes = sbinfo->max_inodes - sbinfo->free_inodes; - if (percpu_counter_compare(&sbinfo->used_blocks, config.max_blocks) > 0) - goto out; - if (config.max_inodes < inodes) - goto out; /* * Those tests disallow limited->unlimited while any are in use; * but we must separately disallow unlimited->limited, because * in that case we have no record of how much is already in use. */ - if (config.max_blocks && !sbinfo->max_blocks) - goto out; - if (config.max_inodes && !sbinfo->max_inodes) - goto out; + if ((ctx.seen & SHMEM_SEEN_BLOCKS) && ctx.blocks) { + if (!sbinfo->max_blocks) + goto out; + if (percpu_counter_compare(&sbinfo->used_blocks, + ctx.blocks) > 0) + goto out; + } + if ((ctx.seen & SHMEM_SEEN_INODES) && ctx.inodes) { + if (!sbinfo->max_inodes) + goto out; + if (ctx.inodes < inodes) + goto out; + } error = 0; - sbinfo->huge = config.huge; - sbinfo->max_blocks = config.max_blocks; - sbinfo->max_inodes = config.max_inodes; - sbinfo->free_inodes = config.max_inodes - inodes; + if (ctx.seen & SHMEM_SEEN_HUGE) + sbinfo->huge = ctx.huge; + if (ctx.seen & SHMEM_SEEN_BLOCKS) + sbinfo->max_blocks = ctx.blocks; + if (ctx.seen & SHMEM_SEEN_INODES) { + sbinfo->max_inodes = ctx.inodes; + sbinfo->free_inodes = ctx.inodes - inodes; + } /* * Preserve previous mempolicy unless mpol remount option was specified. */ - if (config.mpol) { + if (ctx.mpol) { mpol_put(sbinfo->mpol); - sbinfo->mpol = config.mpol; /* transfers initial ref */ + sbinfo->mpol = ctx.mpol; /* transfers initial ref */ } out: spin_unlock(&sbinfo->stat_lock); @@ -3551,6 +3569,9 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct shmem_sb_info *sbinfo; + struct shmem_options ctx = {.mode = 0777 | S_ISVTX, + .uid = current_fsuid(), + .gid = current_fsgid()}; int err = -ENOMEM; /* Round up to L1_CACHE_BYTES to resist false sharing */ @@ -3559,9 +3580,6 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) if (!sbinfo) return -ENOMEM; - sbinfo->mode = 0777 | S_ISVTX; - sbinfo->uid = current_fsuid(); - sbinfo->gid = current_fsgid(); sb->s_fs_info = sbinfo; #ifdef CONFIG_TMPFS @@ -3571,9 +3589,9 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) * but the internal instance is left unlimited. */ if (!(sb->s_flags & SB_KERNMOUNT)) { - sbinfo->max_blocks = shmem_default_max_blocks(); - sbinfo->max_inodes = shmem_default_max_inodes(); - if (shmem_parse_options(data, sbinfo, false)) { + ctx.blocks = shmem_default_max_blocks(); + ctx.inodes = shmem_default_max_inodes(); + if (shmem_parse_options(data, &ctx)) { err = -EINVAL; goto failed; } @@ -3585,11 +3603,17 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) #else sb->s_flags |= SB_NOUSER; #endif + sbinfo->max_blocks = ctx.blocks; + sbinfo->free_inodes = sbinfo->max_inodes = ctx.inodes; + sbinfo->uid = ctx.uid; + sbinfo->gid = ctx.gid; + sbinfo->mode = ctx.mode; + sbinfo->huge = ctx.huge; + sbinfo->mpol = ctx.mpol; spin_lock_init(&sbinfo->stat_lock); if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL)) goto failed; - sbinfo->free_inodes = sbinfo->max_inodes; spin_lock_init(&sbinfo->shrinklist_lock); INIT_LIST_HEAD(&sbinfo->shrinklist); From f6490b7fbb8216e15c0b52409b5fd9d035fe3530 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 19:11:46 -0400 Subject: [PATCH 11/15] shmem_parse_options(): don't bother with mpol in separate variable just use ctx->mpol (note that callers always set ctx->mpol to NULL when calling that). Signed-off-by: Al Viro --- mm/shmem.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 98aa066c00f7..8bca06c12b9a 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3366,7 +3366,6 @@ static const struct export_operations shmem_export_ops = { static int shmem_parse_options(char *options, struct shmem_options *ctx) { char *this_char, *value, *rest; - struct mempolicy *mpol = NULL; uid_t uid; gid_t gid; @@ -3452,9 +3451,9 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) #endif #ifdef CONFIG_NUMA } else if (!strcmp(this_char,"mpol")) { - mpol_put(mpol); - mpol = NULL; - if (mpol_parse_str(value, &mpol)) + mpol_put(ctx->mpol); + ctx->mpol = NULL; + if (mpol_parse_str(value, &ctx->mpol)) goto bad_val; #endif } else { @@ -3462,14 +3461,14 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) goto error; } } - ctx->mpol = mpol; return 0; bad_val: pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", value, this_char); error: - mpol_put(mpol); + mpol_put(ctx->mpol); + ctx->mpol = NULL; return 1; } From e04dc423ae2c0fc862fef6b43ed9083226375e98 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 19:20:12 -0400 Subject: [PATCH 12/15] shmem_parse_options(): take handling a single option into a helper mechanical move. Signed-off-by: Al Viro --- mm/shmem.c | 148 ++++++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 69 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 8bca06c12b9a..b392a8263329 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3363,12 +3363,88 @@ static const struct export_operations shmem_export_ops = { .fh_to_dentry = shmem_fh_to_dentry, }; -static int shmem_parse_options(char *options, struct shmem_options *ctx) +static int shmem_parse_one(struct shmem_options *ctx, char *opt, char *value) { - char *this_char, *value, *rest; + char *rest; uid_t uid; gid_t gid; + if (!strcmp(opt, "size")) { + unsigned long long size; + size = memparse(value,&rest); + if (*rest == '%') { + size <<= PAGE_SHIFT; + size *= totalram_pages(); + do_div(size, 100); + rest++; + } + if (*rest) + goto bad_val; + ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); + ctx->seen |= SHMEM_SEEN_BLOCKS; + } else if (!strcmp(opt, "nr_blocks")) { + ctx->blocks = memparse(value, &rest); + if (*rest) + goto bad_val; + ctx->seen |= SHMEM_SEEN_BLOCKS; + } else if (!strcmp(opt, "nr_inodes")) { + ctx->inodes = memparse(value, &rest); + if (*rest) + goto bad_val; + ctx->seen |= SHMEM_SEEN_INODES; + } else if (!strcmp(opt, "mode")) { + ctx->mode = simple_strtoul(value, &rest, 8) & 07777; + if (*rest) + goto bad_val; + } else if (!strcmp(opt, "uid")) { + uid = simple_strtoul(value, &rest, 0); + if (*rest) + goto bad_val; + ctx->uid = make_kuid(current_user_ns(), uid); + if (!uid_valid(ctx->uid)) + goto bad_val; + } else if (!strcmp(opt, "gid")) { + gid = simple_strtoul(value, &rest, 0); + if (*rest) + goto bad_val; + ctx->gid = make_kgid(current_user_ns(), gid); + if (!gid_valid(ctx->gid)) + goto bad_val; +#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE + } else if (!strcmp(opt, "huge")) { + int huge; + huge = shmem_parse_huge(value); + if (huge < 0) + goto bad_val; + if (!has_transparent_hugepage() && + huge != SHMEM_HUGE_NEVER) + goto bad_val; + ctx->huge = huge; + ctx->seen |= SHMEM_SEEN_HUGE; +#endif +#ifdef CONFIG_NUMA + } else if (!strcmp(opt, "mpol")) { + mpol_put(ctx->mpol); + ctx->mpol = NULL; + if (mpol_parse_str(value, &ctx->mpol)) + goto bad_val; +#endif + } else { + pr_err("tmpfs: Bad mount option %s\n", opt); + return -EINVAL; + } + return 0; + +bad_val: + pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", + value, opt); + return -EINVAL; +} + +static int shmem_parse_options(char *options, struct shmem_options *ctx) +{ + char *this_char, *value; + while (options != NULL) { this_char = options; for (;;) { @@ -3395,77 +3471,11 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) this_char); goto error; } - - if (!strcmp(this_char,"size")) { - unsigned long long size; - size = memparse(value,&rest); - if (*rest == '%') { - size <<= PAGE_SHIFT; - size *= totalram_pages(); - do_div(size, 100); - rest++; - } - if (*rest) - goto bad_val; - ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); - ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(this_char,"nr_blocks")) { - ctx->blocks = memparse(value, &rest); - if (*rest) - goto bad_val; - ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(this_char,"nr_inodes")) { - ctx->inodes = memparse(value, &rest); - if (*rest) - goto bad_val; - ctx->seen |= SHMEM_SEEN_INODES; - } else if (!strcmp(this_char,"mode")) { - ctx->mode = simple_strtoul(value, &rest, 8) & 07777; - if (*rest) - goto bad_val; - } else if (!strcmp(this_char,"uid")) { - uid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->uid = make_kuid(current_user_ns(), uid); - if (!uid_valid(ctx->uid)) - goto bad_val; - } else if (!strcmp(this_char,"gid")) { - gid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->gid = make_kgid(current_user_ns(), gid); - if (!gid_valid(ctx->gid)) - goto bad_val; -#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE - } else if (!strcmp(this_char, "huge")) { - int huge; - huge = shmem_parse_huge(value); - if (huge < 0) - goto bad_val; - if (!has_transparent_hugepage() && - huge != SHMEM_HUGE_NEVER) - goto bad_val; - ctx->huge = huge; - ctx->seen |= SHMEM_SEEN_HUGE; -#endif -#ifdef CONFIG_NUMA - } else if (!strcmp(this_char,"mpol")) { - mpol_put(ctx->mpol); - ctx->mpol = NULL; - if (mpol_parse_str(value, &ctx->mpol)) - goto bad_val; -#endif - } else { - pr_err("tmpfs: Bad mount option %s\n", this_char); + if (shmem_parse_one(ctx, this_char, value) < 0) goto error; - } } return 0; -bad_val: - pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", - value, this_char); error: mpol_put(ctx->mpol); ctx->mpol = NULL; From 626c3920aeb4575f53c96b0d4ad4e651a21cbb66 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 20:28:06 -0400 Subject: [PATCH 13/15] shmem_parse_one(): switch to use of fs_parse() This thing will eventually become our ->parse_param(), while shmem_parse_options() - ->parse_monolithic(). At that point shmem_parse_options() will start calling vfs_parse_fs_string(), rather than calling shmem_parse_one() directly. Signed-off-by: Al Viro --- mm/shmem.c | 187 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 69 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index b392a8263329..6a41595dd1b3 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -37,6 +37,7 @@ #include #include #include +#include #include /* for arch/microblaze update_mmu_cache() */ @@ -3363,15 +3364,63 @@ static const struct export_operations shmem_export_ops = { .fh_to_dentry = shmem_fh_to_dentry, }; -static int shmem_parse_one(struct shmem_options *ctx, char *opt, char *value) -{ - char *rest; - uid_t uid; - gid_t gid; +enum shmem_param { + Opt_gid, + Opt_huge, + Opt_mode, + Opt_mpol, + Opt_nr_blocks, + Opt_nr_inodes, + Opt_size, + Opt_uid, +}; - if (!strcmp(opt, "size")) { - unsigned long long size; - size = memparse(value,&rest); +static const struct fs_parameter_spec shmem_param_specs[] = { + fsparam_u32 ("gid", Opt_gid), + fsparam_enum ("huge", Opt_huge), + fsparam_u32oct("mode", Opt_mode), + fsparam_string("mpol", Opt_mpol), + fsparam_string("nr_blocks", Opt_nr_blocks), + fsparam_string("nr_inodes", Opt_nr_inodes), + fsparam_string("size", Opt_size), + fsparam_u32 ("uid", Opt_uid), + {} +}; + +static const struct fs_parameter_enum shmem_param_enums[] = { + { Opt_huge, "never", SHMEM_HUGE_NEVER }, + { Opt_huge, "always", SHMEM_HUGE_ALWAYS }, + { Opt_huge, "within_size", SHMEM_HUGE_WITHIN_SIZE }, + { Opt_huge, "advise", SHMEM_HUGE_ADVISE }, + {} +}; + +const struct fs_parameter_description shmem_fs_parameters = { + .name = "tmpfs", + .specs = shmem_param_specs, + .enums = shmem_param_enums, +}; + +static int shmem_parse_one(struct shmem_options *ctx, + struct fs_parameter *param) +{ + struct fs_context *fc = NULL; + struct fs_parse_result result; + unsigned long long size; + char *rest; + int opt; + + opt = fs_parse(fc, &shmem_fs_parameters, param, &result); + if (opt < 0) { + if (opt == -ENOPARAM) + return invalf(fc, "tmpfs: Unknown parameter '%s'", + param->key); + return opt; + } + + switch (opt) { + case Opt_size: + size = memparse(param->string, &rest); if (*rest == '%') { size <<= PAGE_SHIFT; size *= totalram_pages(); @@ -3379,74 +3428,65 @@ static int shmem_parse_one(struct shmem_options *ctx, char *opt, char *value) rest++; } if (*rest) - goto bad_val; + goto bad_value; ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(opt, "nr_blocks")) { - ctx->blocks = memparse(value, &rest); + break; + case Opt_nr_blocks: + ctx->blocks = memparse(param->string, &rest); if (*rest) - goto bad_val; + goto bad_value; ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(opt, "nr_inodes")) { - ctx->inodes = memparse(value, &rest); + break; + case Opt_nr_inodes: + ctx->inodes = memparse(param->string, &rest); if (*rest) - goto bad_val; + goto bad_value; ctx->seen |= SHMEM_SEEN_INODES; - } else if (!strcmp(opt, "mode")) { - ctx->mode = simple_strtoul(value, &rest, 8) & 07777; - if (*rest) - goto bad_val; - } else if (!strcmp(opt, "uid")) { - uid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->uid = make_kuid(current_user_ns(), uid); + break; + case Opt_mode: + ctx->mode = result.uint_32 & 07777; + break; + case Opt_uid: + ctx->uid = make_kuid(current_user_ns(), result.uint_32); if (!uid_valid(ctx->uid)) - goto bad_val; - } else if (!strcmp(opt, "gid")) { - gid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->gid = make_kgid(current_user_ns(), gid); + goto bad_value; + break; + case Opt_gid: + ctx->gid = make_kgid(current_user_ns(), result.uint_32); if (!gid_valid(ctx->gid)) - goto bad_val; -#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE - } else if (!strcmp(opt, "huge")) { - int huge; - huge = shmem_parse_huge(value); - if (huge < 0) - goto bad_val; - if (!has_transparent_hugepage() && - huge != SHMEM_HUGE_NEVER) - goto bad_val; - ctx->huge = huge; + goto bad_value; + break; + case Opt_huge: + ctx->huge = result.uint_32; + if (ctx->huge != SHMEM_HUGE_NEVER && + !(IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) && + has_transparent_hugepage())) + goto unsupported_parameter; ctx->seen |= SHMEM_SEEN_HUGE; -#endif -#ifdef CONFIG_NUMA - } else if (!strcmp(opt, "mpol")) { - mpol_put(ctx->mpol); - ctx->mpol = NULL; - if (mpol_parse_str(value, &ctx->mpol)) - goto bad_val; -#endif - } else { - pr_err("tmpfs: Bad mount option %s\n", opt); - return -EINVAL; + break; + case Opt_mpol: + if (IS_ENABLED(CONFIG_NUMA)) { + mpol_put(ctx->mpol); + ctx->mpol = NULL; + if (mpol_parse_str(param->string, &ctx->mpol)) + goto bad_value; + break; + } + goto unsupported_parameter; } return 0; -bad_val: - pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", - value, opt); - return -EINVAL; +unsupported_parameter: + return invalf(fc, "tmpfs: Unsupported parameter '%s'", param->key); +bad_value: + return invalf(fc, "tmpfs: Bad value for '%s'", param->key); } static int shmem_parse_options(char *options, struct shmem_options *ctx) { - char *this_char, *value; - while (options != NULL) { - this_char = options; + char *this_char = options; for (;;) { /* * NUL-terminate this option: unfortunately, @@ -3462,17 +3502,26 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) break; } } - if (!*this_char) - continue; - if ((value = strchr(this_char,'=')) != NULL) { - *value++ = 0; - } else { - pr_err("tmpfs: No value for mount option '%s'\n", - this_char); - goto error; + if (*this_char) { + char *value = strchr(this_char,'='); + struct fs_parameter param = { + .key = this_char, + .type = fs_value_is_string, + }; + int err; + + if (value) { + *value++ = '\0'; + param.size = strlen(value); + param.string = kstrdup(value, GFP_KERNEL); + if (!param.string) + goto error; + } + err = shmem_parse_one(ctx, ¶m); + kfree(param.string); + if (err) + goto error; } - if (shmem_parse_one(ctx, this_char, value) < 0) - goto error; } return 0; From f32356261d44d580649a7abce1156d15d49cf20f Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:31 +0000 Subject: [PATCH 14/15] vfs: Convert ramfs, shmem, tmpfs, devtmpfs, rootfs to use the new mount API Convert the ramfs, shmem, tmpfs, devtmpfs and rootfs filesystems to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Note that tmpfs is slightly tricky as it can contain embedded commas, so it can't be trivially split up using strsep() to break on commas in generic_parse_monolithic(). Instead, tmpfs has to supply its own generic parser. However, if tmpfs changes, then devtmpfs and rootfs, which are wrappers around tmpfs or ramfs, must change too - and thus so must ramfs, so these had to be converted also. [AV: rewritten] Signed-off-by: David Howells cc: Hugh Dickins cc: linux-mm@kvack.org Signed-off-by: Al Viro --- drivers/base/devtmpfs.c | 18 ++-- fs/ramfs/inode.c | 97 +++++++++++--------- include/linux/ramfs.h | 4 +- include/linux/shmem_fs.h | 4 +- init/do_mounts.c | 9 +- mm/shmem.c | 187 ++++++++++++++++++++++----------------- 6 files changed, 180 insertions(+), 139 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index dabf5006a254..30d0523014e0 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -67,19 +67,15 @@ static struct dentry *public_dev_mount(struct file_system_type *fs_type, int fla return dget(s->s_root); } -static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) -{ -#ifdef CONFIG_TMPFS - return shmem_mount(fs_type, flags, dev_name, data); -#else - return ramfs_mount(fs_type, flags, dev_name, data); -#endif -} - static struct file_system_type internal_fs_type = { .name = "devtmpfs", - .mount = dev_mount, +#ifdef CONFIG_TMPFS + .init_fs_context = shmem_init_fs_context, + .parameters = &shmem_fs_parameters, +#else + .init_fs_context = ramfs_init_fs_context, + .parameters = &ramfs_fs_parameters, +#endif .kill_sb = kill_litter_super, }; diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index b85d1e77e934..d82636e8eb65 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "internal.h" struct ramfs_mount_opts { @@ -175,62 +177,52 @@ static const struct super_operations ramfs_ops = { .show_options = ramfs_show_options, }; -enum { +enum ramfs_param { Opt_mode, - Opt_err }; -static const match_table_t tokens = { - {Opt_mode, "mode=%o"}, - {Opt_err, NULL} +static const struct fs_parameter_spec ramfs_param_specs[] = { + fsparam_u32oct("mode", Opt_mode), + {} }; -static int ramfs_parse_options(char *data, struct ramfs_mount_opts *opts) +const struct fs_parameter_description ramfs_fs_parameters = { + .name = "ramfs", + .specs = ramfs_param_specs, +}; + +static int ramfs_parse_param(struct fs_context *fc, struct fs_parameter *param) { - substring_t args[MAX_OPT_ARGS]; - int option; - int token; - char *p; + struct fs_parse_result result; + struct ramfs_fs_info *fsi = fc->s_fs_info; + int opt; - opts->mode = RAMFS_DEFAULT_MODE; - - while ((p = strsep(&data, ",")) != NULL) { - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_mode: - if (match_octal(&args[0], &option)) - return -EINVAL; - opts->mode = option & S_IALLUGO; - break; + opt = fs_parse(fc, &ramfs_fs_parameters, param, &result); + if (opt < 0) { /* * We might like to report bad mount options here; * but traditionally ramfs has ignored all mount options, * and as it is used as a !CONFIG_SHMEM simple substitute * for tmpfs, better continue to ignore other mount options. */ - } + if (opt == -ENOPARAM) + opt = 0; + return opt; + } + + switch (opt) { + case Opt_mode: + fsi->mount_opts.mode = result.uint_32 & S_IALLUGO; + break; } return 0; } -static int ramfs_fill_super(struct super_block *sb, void *data, int silent) +static int ramfs_fill_super(struct super_block *sb, struct fs_context *fc) { - struct ramfs_fs_info *fsi; + struct ramfs_fs_info *fsi = sb->s_fs_info; struct inode *inode; - int err; - - fsi = kzalloc(sizeof(struct ramfs_fs_info), GFP_KERNEL); - sb->s_fs_info = fsi; - if (!fsi) - return -ENOMEM; - - err = ramfs_parse_options(data, &fsi->mount_opts); - if (err) - return err; sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_SIZE; @@ -247,10 +239,34 @@ static int ramfs_fill_super(struct super_block *sb, void *data, int silent) return 0; } -struct dentry *ramfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ramfs_get_tree(struct fs_context *fc) { - return mount_nodev(fs_type, flags, data, ramfs_fill_super); + return get_tree_nodev(fc, ramfs_fill_super); +} + +static void ramfs_free_fc(struct fs_context *fc) +{ + kfree(fc->s_fs_info); +} + +static const struct fs_context_operations ramfs_context_ops = { + .free = ramfs_free_fc, + .parse_param = ramfs_parse_param, + .get_tree = ramfs_get_tree, +}; + +int ramfs_init_fs_context(struct fs_context *fc) +{ + struct ramfs_fs_info *fsi; + + fsi = kzalloc(sizeof(*fsi), GFP_KERNEL); + if (!fsi) + return -ENOMEM; + + fsi->mount_opts.mode = RAMFS_DEFAULT_MODE; + fc->s_fs_info = fsi; + fc->ops = &ramfs_context_ops; + return 0; } static void ramfs_kill_sb(struct super_block *sb) @@ -261,7 +277,8 @@ static void ramfs_kill_sb(struct super_block *sb) static struct file_system_type ramfs_fs_type = { .name = "ramfs", - .mount = ramfs_mount, + .init_fs_context = ramfs_init_fs_context, + .parameters = &ramfs_fs_parameters, .kill_sb = ramfs_kill_sb, .fs_flags = FS_USERNS_MOUNT, }; diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index e4d7d141545e..b806a0ff6554 100644 --- a/include/linux/ramfs.h +++ b/include/linux/ramfs.h @@ -4,8 +4,7 @@ struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir, umode_t mode, dev_t dev); -extern struct dentry *ramfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data); +extern int ramfs_init_fs_context(struct fs_context *fc); #ifdef CONFIG_MMU static inline int @@ -17,6 +16,7 @@ ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) extern int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize); #endif +extern const struct fs_parameter_description ramfs_fs_parameters; extern const struct file_operations ramfs_file_operations; extern const struct vm_operations_struct generic_file_vm_ops; diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 88cb98b0e49b..de8e4b71e3ba 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -49,9 +49,9 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) /* * Functions in mm/shmem.c called directly from elsewhere: */ +extern const struct fs_parameter_description shmem_fs_parameters; extern int shmem_init(void); -extern struct dentry *shmem_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data); +extern int shmem_init_fs_context(struct fs_context *fc); extern struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); extern struct file *shmem_kernel_file_setup(const char *name, loff_t size, diff --git a/init/do_mounts.c b/init/do_mounts.c index 16c29e57f224..9634ecf3743d 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -627,18 +627,17 @@ out: } static bool is_tmpfs; -static struct dentry *rootfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int rootfs_init_fs_context(struct fs_context *fc) { if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - return shmem_mount(fs_type, flags, dev_name, data); + return shmem_init_fs_context(fc); - return ramfs_mount(fs_type, flags, dev_name, data); + return ramfs_init_fs_context(fc); } struct file_system_type rootfs_fs_type = { .name = "rootfs", - .mount = rootfs_mount, + .init_fs_context = rootfs_init_fs_context, .kill_sb = kill_litter_super, }; diff --git a/mm/shmem.c b/mm/shmem.c index 6a41595dd1b3..0f7fd4a85db6 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3401,22 +3401,17 @@ const struct fs_parameter_description shmem_fs_parameters = { .enums = shmem_param_enums, }; -static int shmem_parse_one(struct shmem_options *ctx, - struct fs_parameter *param) +static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) { - struct fs_context *fc = NULL; + struct shmem_options *ctx = fc->fs_private; struct fs_parse_result result; unsigned long long size; char *rest; int opt; opt = fs_parse(fc, &shmem_fs_parameters, param, &result); - if (opt < 0) { - if (opt == -ENOPARAM) - return invalf(fc, "tmpfs: Unknown parameter '%s'", - param->key); + if (opt < 0) return opt; - } switch (opt) { case Opt_size: @@ -3483,8 +3478,10 @@ bad_value: return invalf(fc, "tmpfs: Bad value for '%s'", param->key); } -static int shmem_parse_options(char *options, struct shmem_options *ctx) +static int shmem_parse_options(struct fs_context *fc, void *data) { + char *options = data; + while (options != NULL) { char *this_char = options; for (;;) { @@ -3504,85 +3501,81 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) } if (*this_char) { char *value = strchr(this_char,'='); - struct fs_parameter param = { - .key = this_char, - .type = fs_value_is_string, - }; + size_t len = 0; int err; if (value) { *value++ = '\0'; - param.size = strlen(value); - param.string = kstrdup(value, GFP_KERNEL); - if (!param.string) - goto error; + len = strlen(value); } - err = shmem_parse_one(ctx, ¶m); - kfree(param.string); - if (err) - goto error; + err = vfs_parse_fs_string(fc, this_char, value, len); + if (err < 0) + return err; } } return 0; - -error: - mpol_put(ctx->mpol); - ctx->mpol = NULL; - return 1; - } -static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) +/* + * Reconfigure a shmem filesystem. + * + * Note that we disallow change from limited->unlimited blocks/inodes while any + * are in use; but we must separately disallow unlimited->limited, because in + * that case we have no record of how much is already in use. + */ +static int shmem_reconfigure(struct fs_context *fc) { - struct shmem_sb_info *sbinfo = SHMEM_SB(sb); - struct shmem_options ctx = {.seen = 0}; + struct shmem_options *ctx = fc->fs_private; + struct shmem_sb_info *sbinfo = SHMEM_SB(fc->root->d_sb); unsigned long inodes; - int error = -EINVAL; - - if (shmem_parse_options(data, &ctx)) - return error; + const char *err; spin_lock(&sbinfo->stat_lock); inodes = sbinfo->max_inodes - sbinfo->free_inodes; - /* - * Those tests disallow limited->unlimited while any are in use; - * but we must separately disallow unlimited->limited, because - * in that case we have no record of how much is already in use. - */ - if ((ctx.seen & SHMEM_SEEN_BLOCKS) && ctx.blocks) { - if (!sbinfo->max_blocks) + if ((ctx->seen & SHMEM_SEEN_BLOCKS) && ctx->blocks) { + if (!sbinfo->max_blocks) { + err = "Cannot retroactively limit size"; goto out; + } if (percpu_counter_compare(&sbinfo->used_blocks, - ctx.blocks) > 0) + ctx->blocks) > 0) { + err = "Too small a size for current use"; goto out; + } } - if ((ctx.seen & SHMEM_SEEN_INODES) && ctx.inodes) { - if (!sbinfo->max_inodes) + if ((ctx->seen & SHMEM_SEEN_INODES) && ctx->inodes) { + if (!sbinfo->max_inodes) { + err = "Cannot retroactively limit inodes"; goto out; - if (ctx.inodes < inodes) + } + if (ctx->inodes < inodes) { + err = "Too few inodes for current use"; goto out; + } } - error = 0; - if (ctx.seen & SHMEM_SEEN_HUGE) - sbinfo->huge = ctx.huge; - if (ctx.seen & SHMEM_SEEN_BLOCKS) - sbinfo->max_blocks = ctx.blocks; - if (ctx.seen & SHMEM_SEEN_INODES) { - sbinfo->max_inodes = ctx.inodes; - sbinfo->free_inodes = ctx.inodes - inodes; + if (ctx->seen & SHMEM_SEEN_HUGE) + sbinfo->huge = ctx->huge; + if (ctx->seen & SHMEM_SEEN_BLOCKS) + sbinfo->max_blocks = ctx->blocks; + if (ctx->seen & SHMEM_SEEN_INODES) { + sbinfo->max_inodes = ctx->inodes; + sbinfo->free_inodes = ctx->inodes - inodes; } /* * Preserve previous mempolicy unless mpol remount option was specified. */ - if (ctx.mpol) { + if (ctx->mpol) { mpol_put(sbinfo->mpol); - sbinfo->mpol = ctx.mpol; /* transfers initial ref */ + sbinfo->mpol = ctx->mpol; /* transfers initial ref */ + ctx->mpol = NULL; } + spin_unlock(&sbinfo->stat_lock); + return 0; out: spin_unlock(&sbinfo->stat_lock); - return error; + return invalf(fc, "tmpfs: %s", err); } static int shmem_show_options(struct seq_file *seq, struct dentry *root) @@ -3623,13 +3616,11 @@ static void shmem_put_super(struct super_block *sb) sb->s_fs_info = NULL; } -static int shmem_fill_super(struct super_block *sb, void *data, int silent) +static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) { + struct shmem_options *ctx = fc->fs_private; struct inode *inode; struct shmem_sb_info *sbinfo; - struct shmem_options ctx = {.mode = 0777 | S_ISVTX, - .uid = current_fsuid(), - .gid = current_fsgid()}; int err = -ENOMEM; /* Round up to L1_CACHE_BYTES to resist false sharing */ @@ -3647,12 +3638,10 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) * but the internal instance is left unlimited. */ if (!(sb->s_flags & SB_KERNMOUNT)) { - ctx.blocks = shmem_default_max_blocks(); - ctx.inodes = shmem_default_max_inodes(); - if (shmem_parse_options(data, &ctx)) { - err = -EINVAL; - goto failed; - } + if (!(ctx->seen & SHMEM_SEEN_BLOCKS)) + ctx->blocks = shmem_default_max_blocks(); + if (!(ctx->seen & SHMEM_SEEN_INODES)) + ctx->inodes = shmem_default_max_inodes(); } else { sb->s_flags |= SB_NOUSER; } @@ -3661,13 +3650,14 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) #else sb->s_flags |= SB_NOUSER; #endif - sbinfo->max_blocks = ctx.blocks; - sbinfo->free_inodes = sbinfo->max_inodes = ctx.inodes; - sbinfo->uid = ctx.uid; - sbinfo->gid = ctx.gid; - sbinfo->mode = ctx.mode; - sbinfo->huge = ctx.huge; - sbinfo->mpol = ctx.mpol; + sbinfo->max_blocks = ctx->blocks; + sbinfo->free_inodes = sbinfo->max_inodes = ctx->inodes; + sbinfo->uid = ctx->uid; + sbinfo->gid = ctx->gid; + sbinfo->mode = ctx->mode; + sbinfo->huge = ctx->huge; + sbinfo->mpol = ctx->mpol; + ctx->mpol = NULL; spin_lock_init(&sbinfo->stat_lock); if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL)) @@ -3704,6 +3694,31 @@ failed: return err; } +static int shmem_get_tree(struct fs_context *fc) +{ + return get_tree_nodev(fc, shmem_fill_super); +} + +static void shmem_free_fc(struct fs_context *fc) +{ + struct shmem_options *ctx = fc->fs_private; + + if (ctx) { + mpol_put(ctx->mpol); + kfree(ctx); + } +} + +static const struct fs_context_operations shmem_fs_context_ops = { + .free = shmem_free_fc, + .get_tree = shmem_get_tree, +#ifdef CONFIG_TMPFS + .parse_monolithic = shmem_parse_options, + .parse_param = shmem_parse_one, + .reconfigure = shmem_reconfigure, +#endif +}; + static struct kmem_cache *shmem_inode_cachep; static struct inode *shmem_alloc_inode(struct super_block *sb) @@ -3820,7 +3835,6 @@ static const struct super_operations shmem_ops = { .destroy_inode = shmem_destroy_inode, #ifdef CONFIG_TMPFS .statfs = shmem_statfs, - .remount_fs = shmem_remount_fs, .show_options = shmem_show_options, #endif .evict_inode = shmem_evict_inode, @@ -3841,16 +3855,30 @@ static const struct vm_operations_struct shmem_vm_ops = { #endif }; -struct dentry *shmem_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +int shmem_init_fs_context(struct fs_context *fc) { - return mount_nodev(fs_type, flags, data, shmem_fill_super); + struct shmem_options *ctx; + + ctx = kzalloc(sizeof(struct shmem_options), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->mode = 0777 | S_ISVTX; + ctx->uid = current_fsuid(); + ctx->gid = current_fsgid(); + + fc->fs_private = ctx; + fc->ops = &shmem_fs_context_ops; + return 0; } static struct file_system_type shmem_fs_type = { .owner = THIS_MODULE, .name = "tmpfs", - .mount = shmem_mount, + .init_fs_context = shmem_init_fs_context, +#ifdef CONFIG_TMPFS + .parameters = &shmem_fs_parameters, +#endif .kill_sb = kill_litter_super, .fs_flags = FS_USERNS_MOUNT, }; @@ -3994,7 +4022,8 @@ bool shmem_huge_enabled(struct vm_area_struct *vma) static struct file_system_type shmem_fs_type = { .name = "tmpfs", - .mount = ramfs_mount, + .init_fs_context = ramfs_init_fs_context, + .parameters = &ramfs_fs_parameters, .kill_sb = kill_litter_super, .fs_flags = FS_USERNS_MOUNT, }; From 74983ac20aeafc88d9ceed64a8bf2a9024c488d5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:31 +0000 Subject: [PATCH 15/15] vfs: Make fs_parse() handle fs_param_is_fd-type params better Make fs_parse() handle fs_param_is_fd-type parameters that are passed a string by converting it to an integer (in addition to handling direct fd specification). Also range check the integer. [fix from Yin Fengwei folded] Signed-off-by: David Howells Signed-off-by: Al Viro --- fs/fs_parser.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/fs_parser.c b/fs/fs_parser.c index 460ea4206fa2..d1930adce68d 100644 --- a/fs/fs_parser.c +++ b/fs/fs_parser.c @@ -204,9 +204,23 @@ int fs_parse(struct fs_context *fc, goto okay; case fs_param_is_fd: { - if (param->type != fs_value_is_file) + switch (param->type) { + case fs_value_is_string: + if (!result->has_value) + goto bad_value; + + ret = kstrtouint(param->string, 0, &result->uint_32); + break; + case fs_value_is_file: + result->uint_32 = param->dirfd; + ret = 0; + default: goto bad_value; - goto okay; + } + + if (result->uint_32 > INT_MAX) + goto bad_value; + goto maybe_okay; } case fs_param_is_blockdev: