Skip to content

Commit

Permalink
vfs: Convert fuse to use the new mount API
Browse files Browse the repository at this point in the history
Convert the fuse 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: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
dhowells authored and Al Viro committed Jul 5, 2019
1 parent 461a27a commit 408cbe6
Showing 1 changed file with 149 additions and 125 deletions.
274 changes: 149 additions & 125 deletions fs/fuse/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -443,6 +449,7 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
}

enum {
OPT_SOURCE,
OPT_FD,
OPT_ROOTMODE,
OPT_USER_ID,
Expand All @@ -454,111 +461,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 const struct fs_parameter_description fuse_fs_parameters = {
.name = "fuse",
.specs = fuse_param_specs,
};

static int fuse_match_uint(substring_t *s, unsigned int *res)
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;
}
}
struct fuse_fs_context *ctx = fc->fs_private;

if (!d->fd_present || !d->rootmode_present ||
!d->user_id_present || !d->group_id_present)
return 0;

return 1;
kfree(ctx);
}

static int fuse_show_options(struct seq_file *m, struct dentry *root)
Expand Down Expand Up @@ -1075,12 +1068,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;
Expand All @@ -1093,13 +1086,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 {
Expand All @@ -1116,7 +1106,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;
Expand Down Expand Up @@ -1159,17 +1149,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)
Expand Down Expand Up @@ -1229,11 +1219,50 @@ 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)");

#ifdef CONFIG_BLOCK
if (ctx->is_bdev)
return vfs_get_block_super(fc, fuse_fill_super);
#endif

return get_tree_nodev(fc, 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)
Expand Down Expand Up @@ -1262,19 +1291,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);
Expand All @@ -1284,7 +1307,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,
};
Expand Down

0 comments on commit 408cbe6

Please sign in to comment.