Commit f3235626 authored by David Howells's avatar David Howells Committed by Al Viro

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: default avatarDavid Howells <dhowells@redhat.com>
cc: Hugh Dickins <hughd@google.com>
cc: linux-mm@kvack.org
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 626c3920
...@@ -67,19 +67,15 @@ static struct dentry *public_dev_mount(struct file_system_type *fs_type, int fla ...@@ -67,19 +67,15 @@ static struct dentry *public_dev_mount(struct file_system_type *fs_type, int fla
return dget(s->s_root); return dget(s->s_root);
} }
static struct dentry *dev_mount(struct file_system_type *fs_type, int flags, static struct file_system_type internal_fs_type = {
const char *dev_name, void *data) .name = "devtmpfs",
{
#ifdef CONFIG_TMPFS #ifdef CONFIG_TMPFS
return shmem_mount(fs_type, flags, dev_name, data); .init_fs_context = shmem_init_fs_context,
.parameters = &shmem_fs_parameters,
#else #else
return ramfs_mount(fs_type, flags, dev_name, data); .init_fs_context = ramfs_init_fs_context,
.parameters = &ramfs_fs_parameters,
#endif #endif
}
static struct file_system_type internal_fs_type = {
.name = "devtmpfs",
.mount = dev_mount,
.kill_sb = kill_litter_super, .kill_sb = kill_litter_super,
}; };
......
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include "internal.h" #include "internal.h"
struct ramfs_mount_opts { struct ramfs_mount_opts {
...@@ -175,62 +177,52 @@ static const struct super_operations ramfs_ops = { ...@@ -175,62 +177,52 @@ static const struct super_operations ramfs_ops = {
.show_options = ramfs_show_options, .show_options = ramfs_show_options,
}; };
enum { enum ramfs_param {
Opt_mode, Opt_mode,
Opt_err
}; };
static const match_table_t tokens = { static const struct fs_parameter_spec ramfs_param_specs[] = {
{Opt_mode, "mode=%o"}, fsparam_u32oct("mode", Opt_mode),
{Opt_err, NULL} {}
}; };
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]; struct fs_parse_result result;
int option; struct ramfs_fs_info *fsi = fc->s_fs_info;
int token; int opt;
char *p;
opt = fs_parse(fc, &ramfs_fs_parameters, param, &result);
opts->mode = RAMFS_DEFAULT_MODE; if (opt < 0) {
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;
/* /*
* We might like to report bad mount options here; * We might like to report bad mount options here;
* but traditionally ramfs has ignored all mount options, * but traditionally ramfs has ignored all mount options,
* and as it is used as a !CONFIG_SHMEM simple substitute * and as it is used as a !CONFIG_SHMEM simple substitute
* for tmpfs, better continue to ignore other mount options. * 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; 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; 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_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_SIZE; sb->s_blocksize = PAGE_SIZE;
...@@ -247,10 +239,34 @@ static int ramfs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -247,10 +239,34 @@ static int ramfs_fill_super(struct super_block *sb, void *data, int silent)
return 0; return 0;
} }
struct dentry *ramfs_mount(struct file_system_type *fs_type, static int ramfs_get_tree(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
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) static void ramfs_kill_sb(struct super_block *sb)
...@@ -261,7 +277,8 @@ 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 = { static struct file_system_type ramfs_fs_type = {
.name = "ramfs", .name = "ramfs",
.mount = ramfs_mount, .init_fs_context = ramfs_init_fs_context,
.parameters = &ramfs_fs_parameters,
.kill_sb = ramfs_kill_sb, .kill_sb = ramfs_kill_sb,
.fs_flags = FS_USERNS_MOUNT, .fs_flags = FS_USERNS_MOUNT,
}; };
......
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir, struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir,
umode_t mode, dev_t dev); umode_t mode, dev_t dev);
extern struct dentry *ramfs_mount(struct file_system_type *fs_type, extern int ramfs_init_fs_context(struct fs_context *fc);
int flags, const char *dev_name, void *data);
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
static inline int static inline int
...@@ -17,6 +16,7 @@ ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) ...@@ -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); extern int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize);
#endif #endif
extern const struct fs_parameter_description ramfs_fs_parameters;
extern const struct file_operations ramfs_file_operations; extern const struct file_operations ramfs_file_operations;
extern const struct vm_operations_struct generic_file_vm_ops; extern const struct vm_operations_struct generic_file_vm_ops;
......
...@@ -49,9 +49,9 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) ...@@ -49,9 +49,9 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
/* /*
* Functions in mm/shmem.c called directly from elsewhere: * 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_init(void);
extern struct dentry *shmem_mount(struct file_system_type *fs_type, extern int shmem_init_fs_context(struct fs_context *fc);
int flags, const char *dev_name, void *data);
extern struct file *shmem_file_setup(const char *name, extern struct file *shmem_file_setup(const char *name,
loff_t size, unsigned long flags); loff_t size, unsigned long flags);
extern struct file *shmem_kernel_file_setup(const char *name, loff_t size, extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
......
...@@ -627,18 +627,17 @@ void __init prepare_namespace(void) ...@@ -627,18 +627,17 @@ void __init prepare_namespace(void)
} }
static bool is_tmpfs; static bool is_tmpfs;
static struct dentry *rootfs_mount(struct file_system_type *fs_type, static int rootfs_init_fs_context(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) 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 = { struct file_system_type rootfs_fs_type = {
.name = "rootfs", .name = "rootfs",
.mount = rootfs_mount, .init_fs_context = rootfs_init_fs_context,
.kill_sb = kill_litter_super, .kill_sb = kill_litter_super,
}; };
......
...@@ -3401,22 +3401,17 @@ const struct fs_parameter_description shmem_fs_parameters = { ...@@ -3401,22 +3401,17 @@ const struct fs_parameter_description shmem_fs_parameters = {
.enums = shmem_param_enums, .enums = shmem_param_enums,
}; };
static int shmem_parse_one(struct shmem_options *ctx, static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
struct fs_parameter *param)
{ {
struct fs_context *fc = NULL; struct shmem_options *ctx = fc->fs_private;
struct fs_parse_result result; struct fs_parse_result result;
unsigned long long size; unsigned long long size;
char *rest; char *rest;
int opt; int opt;
opt = fs_parse(fc, &shmem_fs_parameters, param, &result); opt = fs_parse(fc, &shmem_fs_parameters, param, &result);
if (opt < 0) { if (opt < 0)
if (opt == -ENOPARAM)
return invalf(fc, "tmpfs: Unknown parameter '%s'",
param->key);
return opt; return opt;
}
switch (opt) { switch (opt) {
case Opt_size: case Opt_size:
...@@ -3483,8 +3478,10 @@ static int shmem_parse_one(struct shmem_options *ctx, ...@@ -3483,8 +3478,10 @@ static int shmem_parse_one(struct shmem_options *ctx,
return invalf(fc, "tmpfs: Bad value for '%s'", param->key); 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) { while (options != NULL) {
char *this_char = options; char *this_char = options;
for (;;) { for (;;) {
...@@ -3504,85 +3501,81 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) ...@@ -3504,85 +3501,81 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx)
} }
if (*this_char) { if (*this_char) {
char *value = strchr(this_char,'='); char *value = strchr(this_char,'=');
struct fs_parameter param = { size_t len = 0;
.key = this_char,
.type = fs_value_is_string,
};
int err; int err;
if (value) { if (value) {
*value++ = '\0'; *value++ = '\0';
param.size = strlen(value); len = strlen(value);
param.string = kstrdup(value, GFP_KERNEL);
if (!param.string)
goto error;
} }
err = shmem_parse_one(ctx, &param); err = vfs_parse_fs_string(fc, this_char, value, len);
kfree(param.string); if (err < 0)
if (err) return err;
goto error;
} }
} }
return 0; 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 = fc->fs_private;
struct shmem_options ctx = {.seen = 0}; struct shmem_sb_info *sbinfo = SHMEM_SB(fc->root->d_sb);
unsigned long inodes; unsigned long inodes;
int error = -EINVAL; const char *err;
if (shmem_parse_options(data, &ctx))
return error;
spin_lock(&sbinfo->stat_lock); spin_lock(&sbinfo->stat_lock);
inodes = sbinfo->max_inodes - sbinfo->free_inodes; inodes = sbinfo->max_inodes - sbinfo->free_inodes;
/* if ((ctx->seen & SHMEM_SEEN_BLOCKS) && ctx->blocks) {
* Those tests disallow limited->unlimited while any are in use; if (!sbinfo->max_blocks) {
* but we must separately disallow unlimited->limited, because err = "Cannot retroactively limit size";
* 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)
goto out; goto out;
}
if (percpu_counter_compare(&sbinfo->used_blocks, if (percpu_counter_compare(&sbinfo->used_blocks,
ctx.blocks) > 0) ctx->blocks) > 0) {
err = "Too small a size for current use";
goto out; goto out;
}
} }
if ((ctx.seen & SHMEM_SEEN_INODES) && ctx.inodes) { if ((ctx->seen & SHMEM_SEEN_INODES) && ctx->inodes) {
if (!sbinfo->max_inodes) if (!sbinfo->max_inodes) {
err = "Cannot retroactively limit inodes";
goto out; goto out;
if (ctx.inodes < inodes) }
if (ctx->inodes < inodes) {
err = "Too few inodes for current use";
goto out; goto out;
}
} }
error = 0; if (ctx->seen & SHMEM_SEEN_HUGE)
if (ctx.seen & SHMEM_SEEN_HUGE) sbinfo->huge = ctx->huge;
sbinfo->huge = ctx.huge; if (ctx->seen & SHMEM_SEEN_BLOCKS)
if (ctx.seen & SHMEM_SEEN_BLOCKS) sbinfo->max_blocks = ctx->blocks;
sbinfo->max_blocks = ctx.blocks; if (ctx->seen & SHMEM_SEEN_INODES) {
if (ctx.seen & SHMEM_SEEN_INODES) { sbinfo->max_inodes = ctx->inodes;
sbinfo->max_inodes = ctx.inodes; sbinfo->free_inodes = ctx->inodes - inodes;
sbinfo->free_inodes = ctx.inodes - inodes;
} }
/* /*
* Preserve previous mempolicy unless mpol remount option was specified. * Preserve previous mempolicy unless mpol remount option was specified.
*/ */
if (ctx.mpol) { if (ctx->mpol) {
mpol_put(sbinfo->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: out:
spin_unlock(&sbinfo->stat_lock); 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) 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) ...@@ -3623,13 +3616,11 @@ static void shmem_put_super(struct super_block *sb)
sb->s_fs_info = NULL; 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 inode *inode;
struct shmem_sb_info *sbinfo; struct shmem_sb_info *sbinfo;
struct shmem_options ctx = {.mode = 0777 | S_ISVTX,
.uid = current_fsuid(),
.gid = current_fsgid()};
int err = -ENOMEM; int err = -ENOMEM;
/* Round up to L1_CACHE_BYTES to resist false sharing */ /* 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) ...@@ -3647,12 +3638,10 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent)
* but the internal instance is left unlimited. * but the internal instance is left unlimited.
*/ */
if (!(sb->s_flags & SB_KERNMOUNT)) { if (!(sb->s_flags & SB_KERNMOUNT)) {
ctx.blocks = shmem_default_max_blocks(); if (!(ctx->seen & SHMEM_SEEN_BLOCKS))
ctx.inodes = shmem_default_max_inodes(); ctx->blocks = shmem_default_max_blocks();
if (shmem_parse_options(data, &ctx)) { if (!(ctx->seen & SHMEM_SEEN_INODES))
err = -EINVAL; ctx->inodes = shmem_default_max_inodes();
goto failed;
}
} else { } else {
sb->s_flags |= SB_NOUSER; sb->s_flags |= SB_NOUSER;
} }
...@@ -3661,13 +3650,14 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3661,13 +3650,14 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent)
#else #else
sb->s_flags |= SB_NOUSER; sb->s_flags |= SB_NOUSER;
#endif #endif
sbinfo->max_blocks = ctx.blocks; sbinfo->max_blocks = ctx->blocks;
sbinfo->free_inodes = sbinfo->max_inodes = ctx.inodes; sbinfo->free_inodes = sbinfo->max_inodes = ctx->inodes;
sbinfo->uid = ctx.uid; sbinfo->uid = ctx->uid;
sbinfo->gid = ctx.gid; sbinfo->gid = ctx->gid;
sbinfo->mode = ctx.mode; sbinfo->mode = ctx->mode;
sbinfo->huge = ctx.huge; sbinfo->huge = ctx->huge;
sbinfo->mpol = ctx.mpol; sbinfo->mpol = ctx->mpol;
ctx->mpol = NULL;
spin_lock_init(&sbinfo->stat_lock); spin_lock_init(&sbinfo->stat_lock);
if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL)) if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL))
...@@ -3704,6 +3694,31 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3704,6 +3694,31 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent)
return err; 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 kmem_cache *shmem_inode_cachep;
static struct inode *shmem_alloc_inode(struct super_block *sb) static struct inode *shmem_alloc_inode(struct super_block *sb)
...@@ -3820,7 +3835,6 @@ static const struct super_operations shmem_ops = { ...@@ -3820,7 +3835,6 @@ static const struct super_operations shmem_ops = {
.destroy_inode = shmem_destroy_inode, .destroy_inode = shmem_destroy_inode,
#ifdef CONFIG_TMPFS #ifdef CONFIG_TMPFS
.statfs = shmem_statfs, .statfs = shmem_statfs,
.remount_fs = shmem_remount_fs,
.show_options = shmem_show_options, .show_options = shmem_show_options,
#endif #endif
.evict_inode = shmem_evict_inode, .evict_inode = shmem_evict_inode,
...@@ -3841,16 +3855,30 @@ static const struct vm_operations_struct shmem_vm_ops = { ...@@ -3841,16 +3855,30 @@ static const struct vm_operations_struct shmem_vm_ops = {
#endif #endif
}; };
struct dentry *shmem_mount(struct file_system_type *fs_type, int shmem_init_fs_context(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
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 = { static struct file_system_type shmem_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "tmpfs", .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, .kill_sb = kill_litter_super,
.fs_flags = FS_USERNS_MOUNT, .fs_flags = FS_USERNS_MOUNT,
}; };
...@@ -3994,7 +4022,8 @@ bool shmem_huge_enabled(struct vm_area_struct *vma) ...@@ -3994,7 +4022,8 @@ bool shmem_huge_enabled(struct vm_area_struct *vma)
static struct file_system_type shmem_fs_type = { static struct file_system_type shmem_fs_type = {
.name = "tmpfs", .name = "tmpfs",
.mount = ramfs_mount, .init_fs_context = ramfs_init_fs_context,
.parameters = &ramfs_fs_parameters,
.kill_sb = kill_litter_super, .kill_sb = kill_litter_super,
.fs_flags = FS_USERNS_MOUNT, .fs_flags = FS_USERNS_MOUNT,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment