---

 fs/fuse/inode.c |  272 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 147 insertions(+), 125 deletions(-)

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index ec5d9953dfb6..3aebff5b902a 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -15,7 +15,8 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/statfs.h>
 #include <linux/random.h>
 #include <linux/sched.h>
@@ -59,7 +60,12 @@ MODULE_PARM_DESC(max_user_congthresh,
 /** Congestion starts at 75% of maximum */
 #define FUSE_DEFAULT_CONGESTION_THRESHOLD (FUSE_DEFAULT_MAX_BACKGROUND * 3 / 4)
 
-struct fuse_mount_data {
+#ifdef CONFIG_BLOCK
+static struct file_system_type fuseblk_fs_type;
+#endif
+
+struct fuse_fs_context {
+       bool            is_bdev;
        int fd;
        unsigned rootmode;
        kuid_t user_id;
@@ -448,6 +454,7 @@ static int fuse_statfs(struct dentry *dentry, struct 
kstatfs *buf)
 }
 
 enum {
+       OPT_SOURCE,
        OPT_FD,
        OPT_ROOTMODE,
        OPT_USER_ID,
@@ -459,111 +466,97 @@ enum {
        OPT_ERR
 };
 
-static const match_table_t tokens = {
-       {OPT_FD,                        "fd=%u"},
-       {OPT_ROOTMODE,                  "rootmode=%o"},
-       {OPT_USER_ID,                   "user_id=%u"},
-       {OPT_GROUP_ID,                  "group_id=%u"},
-       {OPT_DEFAULT_PERMISSIONS,       "default_permissions"},
-       {OPT_ALLOW_OTHER,               "allow_other"},
-       {OPT_MAX_READ,                  "max_read=%u"},
-       {OPT_BLKSIZE,                   "blksize=%u"},
-       {OPT_ERR,                       NULL}
+static const struct fs_parameter_spec fuse_param_specs[] = {
+       fsparam_string  ("source",              OPT_SOURCE),
+       fsparam_fd      ("fd",                  OPT_FD),
+       fsparam_u32oct  ("rootmode",            OPT_ROOTMODE),
+       fsparam_u32     ("user_id",             OPT_USER_ID),
+       fsparam_u32     ("group_id",            OPT_GROUP_ID),
+       fsparam_flag    ("default_permissions", OPT_DEFAULT_PERMISSIONS),
+       fsparam_flag    ("allow_other",         OPT_ALLOW_OTHER),
+       fsparam_u32     ("max_read",            OPT_MAX_READ),
+       fsparam_u32     ("blksize",             OPT_BLKSIZE),
+       {}
 };
 
-static int fuse_match_uint(substring_t *s, unsigned int *res)
+static const struct fs_parameter_description fuse_fs_parameters = {
+       .name           = "fuse",
+       .specs          = fuse_param_specs,
+};
+
+static int fuse_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-       int err = -ENOMEM;
-       char *buf = match_strdup(s);
-       if (buf) {
-               err = kstrtouint(buf, 10, res);
-               kfree(buf);
+       struct fs_parse_result result;
+       struct fuse_fs_context *ctx = fc->fs_private;
+       int opt;
+
+       opt = fs_parse(fc, &fuse_fs_parameters, param, &result);
+       if (opt < 0)
+               return opt;
+
+       switch (opt) {
+       case OPT_SOURCE:
+               kfree(fc->source);
+               fc->source = param->string;
+               param->string = NULL;
+               break;
+
+       case OPT_FD:
+               ctx->fd = result.uint_32;
+               ctx->fd_present = 1;
+               break;
+
+       case OPT_ROOTMODE:
+               if (!fuse_valid_type(result.uint_32))
+                       return invalf(fc, "fuse: Invalid rootmode");
+               ctx->rootmode = result.uint_32;
+               ctx->rootmode_present = 1;
+               break;
+
+       case OPT_USER_ID:
+               ctx->user_id = make_kuid(fc->user_ns, result.uint_32);
+               if (!uid_valid(ctx->user_id))
+                       return invalf(fc, "fuse: Invalid user_id");
+               ctx->user_id_present = 1;
+               break;
+
+       case OPT_GROUP_ID:
+               ctx->group_id = make_kgid(fc->user_ns, result.uint_32);
+               if (!gid_valid(ctx->group_id))
+                       return invalf(fc, "fuse: Invalid group_id");
+               ctx->group_id_present = 1;
+               break;
+
+       case OPT_DEFAULT_PERMISSIONS:
+               ctx->default_permissions = 1;
+               break;
+
+       case OPT_ALLOW_OTHER:
+               ctx->allow_other = 1;
+               break;
+
+       case OPT_MAX_READ:
+               ctx->max_read = result.uint_32;
+               break;
+
+       case OPT_BLKSIZE:
+               if (!ctx->is_bdev)
+                       return invalf(fc, "fuse: blksize only supported for 
fuseblk");
+               ctx->blksize = result.uint_32;
+               break;
+
+       default:
+               return -EINVAL;
        }
-       return err;
+
+       return 0;
 }
 
-static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev,
-                         struct user_namespace *user_ns)
+static void fuse_free_fc(struct fs_context *fc)
 {
-       char *p;
-       memset(d, 0, sizeof(struct fuse_mount_data));
-       d->max_read = ~0;
-       d->blksize = FUSE_DEFAULT_BLKSIZE;
-
-       while ((p = strsep(&opt, ",")) != NULL) {
-               int token;
-               int value;
-               unsigned uv;
-               substring_t args[MAX_OPT_ARGS];
-               if (!*p)
-                       continue;
-
-               token = match_token(p, tokens, args);
-               switch (token) {
-               case OPT_FD:
-                       if (match_int(&args[0], &value))
-                               return 0;
-                       d->fd = value;
-                       d->fd_present = 1;
-                       break;
-
-               case OPT_ROOTMODE:
-                       if (match_octal(&args[0], &value))
-                               return 0;
-                       if (!fuse_valid_type(value))
-                               return 0;
-                       d->rootmode = value;
-                       d->rootmode_present = 1;
-                       break;
-
-               case OPT_USER_ID:
-                       if (fuse_match_uint(&args[0], &uv))
-                               return 0;
-                       d->user_id = make_kuid(user_ns, uv);
-                       if (!uid_valid(d->user_id))
-                               return 0;
-                       d->user_id_present = 1;
-                       break;
-
-               case OPT_GROUP_ID:
-                       if (fuse_match_uint(&args[0], &uv))
-                               return 0;
-                       d->group_id = make_kgid(user_ns, uv);
-                       if (!gid_valid(d->group_id))
-                               return 0;
-                       d->group_id_present = 1;
-                       break;
-
-               case OPT_DEFAULT_PERMISSIONS:
-                       d->default_permissions = 1;
-                       break;
-
-               case OPT_ALLOW_OTHER:
-                       d->allow_other = 1;
-                       break;
-
-               case OPT_MAX_READ:
-                       if (match_int(&args[0], &value))
-                               return 0;
-                       d->max_read = value;
-                       break;
-
-               case OPT_BLKSIZE:
-                       if (!is_bdev || match_int(&args[0], &value))
-                               return 0;
-                       d->blksize = value;
-                       break;
-
-               default:
-                       return 0;
-               }
-       }
-
-       if (!d->fd_present || !d->rootmode_present ||
-           !d->user_id_present || !d->group_id_present)
-               return 0;
+       struct fuse_fs_context *ctx = fc->fs_private;
 
-       return 1;
+       kfree(ctx);
 }
 
 static int fuse_show_options(struct seq_file *m, struct dentry *root)
@@ -1078,12 +1071,12 @@ void fuse_dev_free(struct fuse_dev *fud)
 }
 EXPORT_SYMBOL_GPL(fuse_dev_free);
 
-static int fuse_fill_super(struct super_block *sb, void *data, int silent)
+static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
 {
+       struct fuse_fs_context *ctx = fsc->fs_private;
        struct fuse_dev *fud;
        struct fuse_conn *fc;
        struct inode *root;
-       struct fuse_mount_data d;
        struct file *file;
        struct dentry *root_dentry;
        struct fuse_req *init_req;
@@ -1096,13 +1089,10 @@ static int fuse_fill_super(struct super_block *sb, void 
*data, int silent)
 
        sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
 
-       if (!parse_fuse_opt(data, &d, is_bdev, sb->s_user_ns))
-               goto err;
-
        if (is_bdev) {
 #ifdef CONFIG_BLOCK
                err = -EINVAL;
-               if (!sb_set_blocksize(sb, d.blksize))
+               if (!sb_set_blocksize(sb, ctx->blksize))
                        goto err;
 #endif
        } else {
@@ -1119,7 +1109,7 @@ static int fuse_fill_super(struct super_block *sb, void 
*data, int silent)
        if (sb->s_user_ns != &init_user_ns)
                sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
 
-       file = fget(d.fd);
+       file = fget(ctx->fd);
        err = -EINVAL;
        if (!file)
                goto err;
@@ -1162,17 +1152,17 @@ static int fuse_fill_super(struct super_block *sb, void 
*data, int silent)
                fc->dont_mask = 1;
        sb->s_flags |= SB_POSIXACL;
 
-       fc->default_permissions = d.default_permissions;
-       fc->allow_other = d.allow_other;
-       fc->user_id = d.user_id;
-       fc->group_id = d.group_id;
-       fc->max_read = max_t(unsigned, 4096, d.max_read);
+       fc->default_permissions = ctx->default_permissions;
+       fc->allow_other = ctx->allow_other;
+       fc->user_id = ctx->user_id;
+       fc->group_id = ctx->group_id;
+       fc->max_read = max_t(unsigned, 4096, ctx->max_read);
 
        /* Used by get_root_inode() */
        sb->s_fs_info = fc;
 
        err = -ENOMEM;
-       root = fuse_get_root_inode(sb, d.rootmode);
+       root = fuse_get_root_inode(sb, ctx->rootmode);
        sb->s_d_op = &fuse_root_dentry_operations;
        root_dentry = d_make_root(root);
        if (!root_dentry)
@@ -1232,11 +1222,48 @@ static int fuse_fill_super(struct super_block *sb, void 
*data, int silent)
        return err;
 }
 
-static struct dentry *fuse_mount(struct file_system_type *fs_type,
-                      int flags, const char *dev_name,
-                      void *raw_data)
+static int fuse_get_tree(struct fs_context *fc)
+{
+       struct fuse_fs_context *ctx = fc->fs_private;
+
+       if (!ctx->fd_present || !ctx->rootmode_present ||
+           !ctx->user_id_present || !ctx->group_id_present)
+               return invalf(fc, "fuse: Missing mount parameter(s)");
+
+       if (ctx->is_bdev)
+               return vfs_get_block_super(fc, fuse_fill_super);
+
+       return vfs_get_super(fc, vfs_get_independent_super, fuse_fill_super);
+}
+
+static const struct fs_context_operations fuse_context_ops = {
+       .free           = fuse_free_fc,
+       .parse_param    = fuse_parse_param,
+       .get_tree       = fuse_get_tree,
+};
+
+/*
+ * Set up the filesystem mount context.
+ */
+static int fuse_init_fs_context(struct fs_context *fc)
 {
-       return mount_nodev(fs_type, flags, raw_data, fuse_fill_super);
+       struct fuse_fs_context *ctx;
+
+       ctx = kzalloc(sizeof(struct fuse_fs_context), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->max_read = ~0;
+       ctx->blksize = FUSE_DEFAULT_BLKSIZE;
+
+#ifdef CONFIG_BLOCK
+       if (fc->fs_type == &fuseblk_fs_type)
+               ctx->is_bdev = true;
+#endif
+
+       fc->fs_private = ctx;
+       fc->ops = &fuse_context_ops;
+       return 0;
 }
 
 static void fuse_sb_destroy(struct super_block *sb)
@@ -1265,19 +1292,13 @@ static struct file_system_type fuse_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "fuse",
        .fs_flags       = FS_HAS_SUBTYPE | FS_USERNS_MOUNT,
-       .mount          = fuse_mount,
+       .init_fs_context = fuse_init_fs_context,
+       .parameters     = &fuse_fs_parameters,
        .kill_sb        = fuse_kill_sb_anon,
 };
 MODULE_ALIAS_FS("fuse");
 
 #ifdef CONFIG_BLOCK
-static struct dentry *fuse_mount_blk(struct file_system_type *fs_type,
-                          int flags, const char *dev_name,
-                          void *raw_data)
-{
-       return mount_bdev(fs_type, flags, dev_name, raw_data, fuse_fill_super);
-}
-
 static void fuse_kill_sb_blk(struct super_block *sb)
 {
        fuse_sb_destroy(sb);
@@ -1287,7 +1308,8 @@ static void fuse_kill_sb_blk(struct super_block *sb)
 static struct file_system_type fuseblk_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "fuseblk",
-       .mount          = fuse_mount_blk,
+       .init_fs_context = fuse_init_fs_context,
+       .parameters     = &fuse_fs_parameters,
        .kill_sb        = fuse_kill_sb_blk,
        .fs_flags       = FS_REQUIRES_DEV | FS_HAS_SUBTYPE,
 };

Reply via email to