Merge branch 'work.mount3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull more mount API conversions from Al Viro:
 "Assorted conversions of options parsing to new API.

  gfs2 is probably the most serious one here; the rest is trivial stuff.

  Other things in what used to be #work.mount are going to wait for the
  next cycle (and preferably go via git trees of the filesystems
  involved)"

* 'work.mount3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  gfs2: Convert gfs2 to fs_context
  vfs: Convert spufs to use the new mount API
  vfs: Convert hypfs to use the new mount API
  hypfs: Fix error number left in struct pointer member
  vfs: Convert functionfs to use the new mount API
  vfs: Convert bpf to use the new mount API
This commit is contained in:
Linus Torvalds
2019-09-24 12:33:34 -07:00
8 changed files with 748 additions and 758 deletions

View File

@@ -17,6 +17,7 @@
#include <linux/blkdev.h>
#include <linux/pagemap.h>
#include <linux/export.h>
#include <linux/fs_parser.h>
#include <linux/hid.h>
#include <linux/mm.h>
#include <linux/module.h>
@@ -1451,9 +1452,9 @@ struct ffs_sb_fill_data {
struct ffs_data *ffs_data;
};
static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
{
struct ffs_sb_fill_data *data = _data;
struct ffs_sb_fill_data *data = fc->fs_private;
struct inode *inode;
struct ffs_data *ffs = data->ffs_data;
@@ -1486,147 +1487,152 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
return 0;
}
static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
enum {
Opt_no_disconnect,
Opt_rmode,
Opt_fmode,
Opt_mode,
Opt_uid,
Opt_gid,
};
static const struct fs_parameter_spec ffs_fs_param_specs[] = {
fsparam_bool ("no_disconnect", Opt_no_disconnect),
fsparam_u32 ("rmode", Opt_rmode),
fsparam_u32 ("fmode", Opt_fmode),
fsparam_u32 ("mode", Opt_mode),
fsparam_u32 ("uid", Opt_uid),
fsparam_u32 ("gid", Opt_gid),
{}
};
static const struct fs_parameter_description ffs_fs_fs_parameters = {
.name = "kAFS",
.specs = ffs_fs_param_specs,
};
static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
struct ffs_sb_fill_data *data = fc->fs_private;
struct fs_parse_result result;
int opt;
ENTER();
if (!opts || !*opts)
return 0;
opt = fs_parse(fc, &ffs_fs_fs_parameters, param, &result);
if (opt < 0)
return opt;
for (;;) {
unsigned long value;
char *eq, *comma;
switch (opt) {
case Opt_no_disconnect:
data->no_disconnect = result.boolean;
break;
case Opt_rmode:
data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
break;
case Opt_fmode:
data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
break;
case Opt_mode:
data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
break;
/* Option limit */
comma = strchr(opts, ',');
if (comma)
*comma = 0;
case Opt_uid:
data->perms.uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(data->perms.uid))
goto unmapped_value;
break;
case Opt_gid:
data->perms.gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(data->perms.gid))
goto unmapped_value;
break;
/* Value limit */
eq = strchr(opts, '=');
if (unlikely(!eq)) {
pr_err("'=' missing in %s\n", opts);
return -EINVAL;
}
*eq = 0;
/* Parse value */
if (kstrtoul(eq + 1, 0, &value)) {
pr_err("%s: invalid value: %s\n", opts, eq + 1);
return -EINVAL;
}
/* Interpret option */
switch (eq - opts) {
case 13:
if (!memcmp(opts, "no_disconnect", 13))
data->no_disconnect = !!value;
else
goto invalid;
break;
case 5:
if (!memcmp(opts, "rmode", 5))
data->root_mode = (value & 0555) | S_IFDIR;
else if (!memcmp(opts, "fmode", 5))
data->perms.mode = (value & 0666) | S_IFREG;
else
goto invalid;
break;
case 4:
if (!memcmp(opts, "mode", 4)) {
data->root_mode = (value & 0555) | S_IFDIR;
data->perms.mode = (value & 0666) | S_IFREG;
} else {
goto invalid;
}
break;
case 3:
if (!memcmp(opts, "uid", 3)) {
data->perms.uid = make_kuid(current_user_ns(), value);
if (!uid_valid(data->perms.uid)) {
pr_err("%s: unmapped value: %lu\n", opts, value);
return -EINVAL;
}
} else if (!memcmp(opts, "gid", 3)) {
data->perms.gid = make_kgid(current_user_ns(), value);
if (!gid_valid(data->perms.gid)) {
pr_err("%s: unmapped value: %lu\n", opts, value);
return -EINVAL;
}
} else {
goto invalid;
}
break;
default:
invalid:
pr_err("%s: invalid option\n", opts);
return -EINVAL;
}
/* Next iteration */
if (!comma)
break;
opts = comma + 1;
default:
return -ENOPARAM;
}
return 0;
unmapped_value:
return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32);
}
/* "mount -t functionfs dev_name /dev/function" ends up here */
static struct dentry *
ffs_fs_mount(struct file_system_type *t, int flags,
const char *dev_name, void *opts)
/*
* Set up the superblock for a mount.
*/
static int ffs_fs_get_tree(struct fs_context *fc)
{
struct ffs_sb_fill_data data = {
.perms = {
.mode = S_IFREG | 0600,
.uid = GLOBAL_ROOT_UID,
.gid = GLOBAL_ROOT_GID,
},
.root_mode = S_IFDIR | 0500,
.no_disconnect = false,
};
struct dentry *rv;
int ret;
struct ffs_sb_fill_data *ctx = fc->fs_private;
void *ffs_dev;
struct ffs_data *ffs;
ENTER();
ret = ffs_fs_parse_opts(&data, opts);
if (unlikely(ret < 0))
return ERR_PTR(ret);
if (!fc->source)
return invalf(fc, "No source specified");
ffs = ffs_data_new(dev_name);
ffs = ffs_data_new(fc->source);
if (unlikely(!ffs))
return ERR_PTR(-ENOMEM);
ffs->file_perms = data.perms;
ffs->no_disconnect = data.no_disconnect;
return -ENOMEM;
ffs->file_perms = ctx->perms;
ffs->no_disconnect = ctx->no_disconnect;
ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
ffs->dev_name = kstrdup(fc->source, GFP_KERNEL);
if (unlikely(!ffs->dev_name)) {
ffs_data_put(ffs);
return ERR_PTR(-ENOMEM);
return -ENOMEM;
}
ffs_dev = ffs_acquire_dev(dev_name);
ffs_dev = ffs_acquire_dev(ffs->dev_name);
if (IS_ERR(ffs_dev)) {
ffs_data_put(ffs);
return ERR_CAST(ffs_dev);
return PTR_ERR(ffs_dev);
}
ffs->private_data = ffs_dev;
data.ffs_data = ffs;
rv = mount_nodev(t, flags, &data, ffs_sb_fill);
if (IS_ERR(rv) && data.ffs_data) {
ffs_release_dev(data.ffs_data);
ffs_data_put(data.ffs_data);
ffs->private_data = ffs_dev;
ctx->ffs_data = ffs;
return get_tree_nodev(fc, ffs_sb_fill);
}
static void ffs_fs_free_fc(struct fs_context *fc)
{
struct ffs_sb_fill_data *ctx = fc->fs_private;
if (ctx) {
if (ctx->ffs_data) {
ffs_release_dev(ctx->ffs_data);
ffs_data_put(ctx->ffs_data);
}
kfree(ctx);
}
return rv;
}
static const struct fs_context_operations ffs_fs_context_ops = {
.free = ffs_fs_free_fc,
.parse_param = ffs_fs_parse_param,
.get_tree = ffs_fs_get_tree,
};
static int ffs_fs_init_fs_context(struct fs_context *fc)
{
struct ffs_sb_fill_data *ctx;
ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->perms.mode = S_IFREG | 0600;
ctx->perms.uid = GLOBAL_ROOT_UID;
ctx->perms.gid = GLOBAL_ROOT_GID;
ctx->root_mode = S_IFDIR | 0500;
ctx->no_disconnect = false;
fc->fs_private = ctx;
fc->ops = &ffs_fs_context_ops;
return 0;
}
static void
@@ -1644,7 +1650,8 @@ ffs_fs_kill_sb(struct super_block *sb)
static struct file_system_type ffs_fs_type = {
.owner = THIS_MODULE,
.name = "functionfs",
.mount = ffs_fs_mount,
.init_fs_context = ffs_fs_init_fs_context,
.parameters = &ffs_fs_fs_parameters,
.kill_sb = ffs_fs_kill_sb,
};
MODULE_ALIAS_FS("functionfs");