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