diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index ba5c80903efe..30d0523014e0 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -56,20 +56,32 @@ static int __init mount_param(char *str) } __setup("devtmpfs.mount=", mount_param); -static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, +static struct vfsmount *mnt; + +static struct dentry *public_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); -#else - return mount_single(fs_type, flags, data, ramfs_fill_super); -#endif + struct super_block *s = mnt->mnt_sb; + atomic_inc(&s->s_active); + down_write(&s->s_umount); + return dget(s->s_root); } +static struct file_system_type internal_fs_type = { + .name = "devtmpfs", +#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, +}; + static struct file_system_type dev_fs_type = { .name = "devtmpfs", - .mount = dev_mount, - .kill_sb = kill_litter_super, + .mount = public_dev_mount, }; #ifdef CONFIG_BLOCK @@ -378,12 +390,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 +431,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); 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/fs/cramfs/inode.c b/fs/cramfs/inode.c index 4d1d8b7761ed..d12ea28836a5 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; } @@ -609,8 +611,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; @@ -625,14 +626,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; @@ -654,7 +654,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; @@ -949,32 +949,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, }; 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: diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index d0b59d03a7a9..05fe6cf5f1ac 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"); diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 733c6b4193dc..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; } -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 @@ 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/fs/romfs/super.c b/fs/romfs/super.c index a42c0e3079dc..e582d001f792 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; @@ -506,8 +505,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; } @@ -520,7 +519,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); @@ -550,23 +549,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 */ @@ -589,7 +599,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, }; diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index a9e9837617a9..0cc4ceec0562 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); @@ -199,7 +202,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; } @@ -207,7 +210,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 */ @@ -222,7 +225,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) @@ -236,7 +239,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; @@ -252,7 +255,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; @@ -277,7 +280,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; @@ -288,13 +291,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 */ @@ -323,6 +326,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); @@ -338,6 +343,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) { @@ -360,14 +387,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) { @@ -386,14 +405,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; @@ -470,7 +481,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 }; @@ -481,7 +492,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); diff --git a/fs/super.c b/fs/super.c index 2d679db9e8c7..8020974b2a68 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1164,9 +1164,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: @@ -1184,18 +1186,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); @@ -1215,6 +1228,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), 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); diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index ee582bdb7fda..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,9 +16,8 @@ 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; -int ramfs_fill_super(struct super_block *sb, void *data, int silent); - #endif diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 20d815a33145..de8e4b71e3ba 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -49,8 +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 int shmem_fill_super(struct super_block *sb, void *data, int silent); +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 53cb37b66227..9634ecf3743d 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -627,20 +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) { - void *fill = ramfs_fill_super; - if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - fill = shmem_fill_super; + return shmem_init_fs_context(fc); - return mount_nodev(fs_type, flags, data, fill); + 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 2bed4761f279..0f7fd4a85db6 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -37,6 +37,7 @@ #include #include #include +#include #include /* for arch/microblaze update_mmu_cache() */ @@ -107,6 +108,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,16 +3364,126 @@ 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) +enum shmem_param { + Opt_gid, + Opt_huge, + Opt_mode, + Opt_mpol, + Opt_nr_blocks, + Opt_nr_inodes, + Opt_size, + Opt_uid, +}; + +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 fs_context *fc, struct fs_parameter *param) { - char *this_char, *value, *rest; - struct mempolicy *mpol = NULL; - uid_t uid; - gid_t gid; + 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) + return opt; + + switch (opt) { + case Opt_size: + size = memparse(param->string, &rest); + if (*rest == '%') { + size <<= PAGE_SHIFT; + size *= totalram_pages(); + do_div(size, 100); + rest++; + } + if (*rest) + goto bad_value; + ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); + ctx->seen |= SHMEM_SEEN_BLOCKS; + break; + case Opt_nr_blocks: + ctx->blocks = memparse(param->string, &rest); + if (*rest) + goto bad_value; + ctx->seen |= SHMEM_SEEN_BLOCKS; + break; + case Opt_nr_inodes: + ctx->inodes = memparse(param->string, &rest); + if (*rest) + goto bad_value; + ctx->seen |= SHMEM_SEEN_INODES; + 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_value; + break; + case Opt_gid: + ctx->gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(ctx->gid)) + 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; + 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; + +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(struct fs_context *fc, void *data) +{ + char *options = data; while (options != NULL) { - this_char = options; + char *this_char = options; for (;;) { /* * NUL-terminate this option: unfortunately, @@ -3374,139 +3499,83 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, 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,'='); + size_t len = 0; + int err; - 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 (value) { + *value++ = '\0'; + len = strlen(value); } - if (*rest) - goto bad_val; - sbinfo->max_blocks = - DIV_ROUND_UP(size, PAGE_SIZE); - } else if (!strcmp(this_char,"nr_blocks")) { - sbinfo->max_blocks = memparse(value, &rest); - if (*rest) - goto bad_val; - } else if (!strcmp(this_char,"nr_inodes")) { - sbinfo->max_inodes = memparse(value, &rest); - if (*rest) - goto bad_val; - } else if (!strcmp(this_char,"mode")) { - if (remount) - continue; - sbinfo->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)) - 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)) - 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; - sbinfo->huge = huge; -#endif -#ifdef CONFIG_NUMA - } else if (!strcmp(this_char,"mpol")) { - mpol_put(mpol); - mpol = NULL; - if (mpol_parse_str(value, &mpol)) - goto bad_val; -#endif - } else { - pr_err("tmpfs: Bad mount option %s\n", this_char); - goto error; + err = vfs_parse_fs_string(fc, this_char, value, len); + if (err < 0) + return err; } } - sbinfo->mpol = mpol; return 0; - -bad_val: - pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", - value, this_char); -error: - mpol_put(mpol); - 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_sb_info config = *sbinfo; + struct shmem_options *ctx = fc->fs_private; + struct shmem_sb_info *sbinfo = SHMEM_SB(fc->root->d_sb); unsigned long inodes; - int error = -EINVAL; - - config.mpol = NULL; - if (shmem_parse_options(data, &config, true)) - return error; + const char *err; 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) { + err = "Cannot retroactively limit size"; + goto out; + } + if (percpu_counter_compare(&sbinfo->used_blocks, + 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) { + err = "Cannot retroactively limit inodes"; + goto out; + } + if (ctx->inodes < inodes) { + err = "Too few inodes for current use"; + 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 */ + 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) @@ -3547,8 +3616,9 @@ 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, struct fs_context *fc) { + struct shmem_options *ctx = fc->fs_private; struct inode *inode; struct shmem_sb_info *sbinfo; int err = -ENOMEM; @@ -3559,9 +3629,6 @@ 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,12 +3638,10 @@ 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)) { - 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; } @@ -3585,11 +3650,18 @@ 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; + ctx->mpol = NULL; 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); @@ -3622,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) @@ -3738,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, @@ -3759,16 +3855,30 @@ static const struct vm_operations_struct shmem_vm_ops = { #endif }; -static 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, }; @@ -3912,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, };