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

vfs: Convert functionfs to use the new mount API

Convert the functionfs 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: default avatarDavid Howells <dhowells@redhat.com>
Acked-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
Acked-by: default avatarMichal Nazarewicz <mina86@mina86.com>
cc: linux-usb@vger.kernel.org
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent d2935de7
......@@ -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)
{
ENTER();
enum {
Opt_no_disconnect,
Opt_rmode,
Opt_fmode,
Opt_mode,
Opt_uid,
Opt_gid,
};
if (!opts || !*opts)
return 0;
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),
{}
};
for (;;) {
unsigned long value;
char *eq, *comma;
/* Option limit */
comma = strchr(opts, ',');
if (comma)
*comma = 0;
/* Value limit */
eq = strchr(opts, '=');
if (unlikely(!eq)) {
pr_err("'=' missing in %s\n", opts);
return -EINVAL;
}
*eq = 0;
static const struct fs_parameter_description ffs_fs_fs_parameters = {
.name = "kAFS",
.specs = ffs_fs_param_specs,
};
/* Parse value */
if (kstrtoul(eq + 1, 0, &value)) {
pr_err("%s: invalid value: %s\n", opts, eq + 1);
return -EINVAL;
}
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;
/* Interpret option */
switch (eq - opts) {
case 13:
if (!memcmp(opts, "no_disconnect", 13))
data->no_disconnect = !!value;
else
goto invalid;
ENTER();
opt = fs_parse(fc, &ffs_fs_fs_parameters, param, &result);
if (opt < 0)
return opt;
switch (opt) {
case Opt_no_disconnect:
data->no_disconnect = result.boolean;
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;
case Opt_rmode:
data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
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;
}
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;
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;
}
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;
default:
invalid:
pr_err("%s: invalid option\n", opts);
return -EINVAL;
}
/* Next iteration */
if (!comma)
break;
opts = comma + 1;
return -ENOPARAM;
}
return 0;
}
/* "mount -t functionfs dev_name /dev/function" ends up here */
unmapped_value:
return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32);
}
static struct dentry *
ffs_fs_mount(struct file_system_type *t, int flags,
const char *dev_name, void *opts)
{
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;
/*
* Set up the superblock for a mount.
*/
static int ffs_fs_get_tree(struct fs_context *fc)
{
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;
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);
}
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);
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");
......
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