mirror of https://gitee.com/openkylin/linux.git
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:
commit
0b36c9eed2
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/fs_context.h>
|
||||||
|
#include <linux/fs_parser.h>
|
||||||
#include <linux/fsnotify.h>
|
#include <linux/fsnotify.h>
|
||||||
#include <linux/backing-dev.h>
|
#include <linux/backing-dev.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -20,7 +22,6 @@
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/poll.h>
|
#include <linux/poll.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/parser.h>
|
|
||||||
|
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
#include <asm/spu.h>
|
#include <asm/spu.h>
|
||||||
|
@ -30,7 +31,7 @@
|
||||||
#include "spufs.h"
|
#include "spufs.h"
|
||||||
|
|
||||||
struct spufs_sb_info {
|
struct spufs_sb_info {
|
||||||
int debug;
|
bool debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct kmem_cache *spufs_inode_cache;
|
static struct kmem_cache *spufs_inode_cache;
|
||||||
|
@ -574,16 +575,27 @@ long spufs_create(struct path *path, struct dentry *dentry,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* File system initialization */
|
/* File system initialization */
|
||||||
enum {
|
struct spufs_fs_context {
|
||||||
Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err,
|
kuid_t uid;
|
||||||
|
kgid_t gid;
|
||||||
|
umode_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const match_table_t spufs_tokens = {
|
enum {
|
||||||
{ Opt_uid, "uid=%d" },
|
Opt_uid, Opt_gid, Opt_mode, Opt_debug,
|
||||||
{ Opt_gid, "gid=%d" },
|
};
|
||||||
{ Opt_mode, "mode=%o" },
|
|
||||||
{ Opt_debug, "debug" },
|
static const struct fs_parameter_spec spufs_param_specs[] = {
|
||||||
{ Opt_err, NULL },
|
fsparam_u32 ("gid", Opt_gid),
|
||||||
|
fsparam_u32oct ("mode", Opt_mode),
|
||||||
|
fsparam_u32 ("uid", Opt_uid),
|
||||||
|
fsparam_flag ("debug", Opt_debug),
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fs_parameter_description spufs_fs_parameters = {
|
||||||
|
.name = "spufs",
|
||||||
|
.specs = spufs_param_specs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int spufs_show_options(struct seq_file *m, struct dentry *root)
|
static int spufs_show_options(struct seq_file *m, struct dentry *root)
|
||||||
|
@ -604,47 +616,41 @@ static int spufs_show_options(struct seq_file *m, struct dentry *root)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int spufs_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||||
spufs_parse_options(struct super_block *sb, char *options, struct inode *root)
|
|
||||||
{
|
{
|
||||||
char *p;
|
struct spufs_fs_context *ctx = fc->fs_private;
|
||||||
substring_t args[MAX_OPT_ARGS];
|
struct spufs_sb_info *sbi = fc->s_fs_info;
|
||||||
|
struct fs_parse_result result;
|
||||||
|
kuid_t uid;
|
||||||
|
kgid_t gid;
|
||||||
|
int opt;
|
||||||
|
|
||||||
while ((p = strsep(&options, ",")) != NULL) {
|
opt = fs_parse(fc, &spufs_fs_parameters, param, &result);
|
||||||
int token, option;
|
if (opt < 0)
|
||||||
|
return opt;
|
||||||
|
|
||||||
if (!*p)
|
switch (opt) {
|
||||||
continue;
|
|
||||||
|
|
||||||
token = match_token(p, spufs_tokens, args);
|
|
||||||
switch (token) {
|
|
||||||
case Opt_uid:
|
case Opt_uid:
|
||||||
if (match_int(&args[0], &option))
|
uid = make_kuid(current_user_ns(), result.uint_32);
|
||||||
return 0;
|
if (!uid_valid(uid))
|
||||||
root->i_uid = make_kuid(current_user_ns(), option);
|
return invalf(fc, "Unknown uid");
|
||||||
if (!uid_valid(root->i_uid))
|
ctx->uid = uid;
|
||||||
return 0;
|
|
||||||
break;
|
break;
|
||||||
case Opt_gid:
|
case Opt_gid:
|
||||||
if (match_int(&args[0], &option))
|
gid = make_kgid(current_user_ns(), result.uint_32);
|
||||||
return 0;
|
if (!gid_valid(gid))
|
||||||
root->i_gid = make_kgid(current_user_ns(), option);
|
return invalf(fc, "Unknown gid");
|
||||||
if (!gid_valid(root->i_gid))
|
ctx->gid = gid;
|
||||||
return 0;
|
|
||||||
break;
|
break;
|
||||||
case Opt_mode:
|
case Opt_mode:
|
||||||
if (match_octal(&args[0], &option))
|
ctx->mode = result.uint_32 & S_IALLUGO;
|
||||||
return 0;
|
|
||||||
root->i_mode = option | S_IFDIR;
|
|
||||||
break;
|
break;
|
||||||
case Opt_debug:
|
case Opt_debug:
|
||||||
spufs_get_sb_info(sb)->debug = 1;
|
sbi->debug = true;
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spufs_exit_isolated_loader(void)
|
static void spufs_exit_isolated_loader(void)
|
||||||
|
@ -678,79 +684,98 @@ spufs_init_isolated_loader(void)
|
||||||
printk(KERN_INFO "spufs: SPU isolation mode enabled\n");
|
printk(KERN_INFO "spufs: SPU isolation mode enabled\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int spufs_create_root(struct super_block *sb, struct fs_context *fc)
|
||||||
spufs_create_root(struct super_block *sb, void *data)
|
|
||||||
{
|
{
|
||||||
|
struct spufs_fs_context *ctx = fc->fs_private;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = -ENODEV;
|
|
||||||
if (!spu_management_ops)
|
if (!spu_management_ops)
|
||||||
goto out;
|
return -ENODEV;
|
||||||
|
|
||||||
ret = -ENOMEM;
|
inode = spufs_new_inode(sb, S_IFDIR | ctx->mode);
|
||||||
inode = spufs_new_inode(sb, S_IFDIR | 0775);
|
|
||||||
if (!inode)
|
if (!inode)
|
||||||
goto out;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
inode->i_uid = ctx->uid;
|
||||||
|
inode->i_gid = ctx->gid;
|
||||||
inode->i_op = &simple_dir_inode_operations;
|
inode->i_op = &simple_dir_inode_operations;
|
||||||
inode->i_fop = &simple_dir_operations;
|
inode->i_fop = &simple_dir_operations;
|
||||||
SPUFS_I(inode)->i_ctx = NULL;
|
SPUFS_I(inode)->i_ctx = NULL;
|
||||||
inc_nlink(inode);
|
inc_nlink(inode);
|
||||||
|
|
||||||
ret = -EINVAL;
|
|
||||||
if (!spufs_parse_options(sb, data, inode))
|
|
||||||
goto out_iput;
|
|
||||||
|
|
||||||
ret = -ENOMEM;
|
|
||||||
sb->s_root = d_make_root(inode);
|
sb->s_root = d_make_root(inode);
|
||||||
if (!sb->s_root)
|
if (!sb->s_root)
|
||||||
goto out;
|
return -ENOMEM;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
out_iput:
|
|
||||||
iput(inode);
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static const struct super_operations spufs_ops = {
|
||||||
spufs_fill_super(struct super_block *sb, void *data, int silent)
|
|
||||||
{
|
|
||||||
struct spufs_sb_info *info;
|
|
||||||
static const struct super_operations s_ops = {
|
|
||||||
.alloc_inode = spufs_alloc_inode,
|
.alloc_inode = spufs_alloc_inode,
|
||||||
.free_inode = spufs_free_inode,
|
.free_inode = spufs_free_inode,
|
||||||
.statfs = simple_statfs,
|
.statfs = simple_statfs,
|
||||||
.evict_inode = spufs_evict_inode,
|
.evict_inode = spufs_evict_inode,
|
||||||
.show_options = spufs_show_options,
|
.show_options = spufs_show_options,
|
||||||
};
|
};
|
||||||
|
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
||||||
if (!info)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
|
static int spufs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||||
|
{
|
||||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||||
sb->s_blocksize = PAGE_SIZE;
|
sb->s_blocksize = PAGE_SIZE;
|
||||||
sb->s_blocksize_bits = PAGE_SHIFT;
|
sb->s_blocksize_bits = PAGE_SHIFT;
|
||||||
sb->s_magic = SPUFS_MAGIC;
|
sb->s_magic = SPUFS_MAGIC;
|
||||||
sb->s_op = &s_ops;
|
sb->s_op = &spufs_ops;
|
||||||
sb->s_fs_info = info;
|
|
||||||
|
|
||||||
return spufs_create_root(sb, data);
|
return spufs_create_root(sb, fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dentry *
|
static int spufs_get_tree(struct fs_context *fc)
|
||||||
spufs_mount(struct file_system_type *fstype, int flags,
|
|
||||||
const char *name, void *data)
|
|
||||||
{
|
{
|
||||||
return mount_single(fstype, flags, data, spufs_fill_super);
|
return get_tree_single(fc, spufs_fill_super);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spufs_free_fc(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
kfree(fc->s_fs_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fs_context_operations spufs_context_ops = {
|
||||||
|
.free = spufs_free_fc,
|
||||||
|
.parse_param = spufs_parse_param,
|
||||||
|
.get_tree = spufs_get_tree,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int spufs_init_fs_context(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
struct spufs_fs_context *ctx;
|
||||||
|
struct spufs_sb_info *sbi;
|
||||||
|
|
||||||
|
ctx = kzalloc(sizeof(struct spufs_fs_context), GFP_KERNEL);
|
||||||
|
if (!ctx)
|
||||||
|
goto nomem;
|
||||||
|
|
||||||
|
sbi = kzalloc(sizeof(struct spufs_sb_info), GFP_KERNEL);
|
||||||
|
if (!sbi)
|
||||||
|
goto nomem_ctx;
|
||||||
|
|
||||||
|
ctx->uid = current_uid();
|
||||||
|
ctx->gid = current_gid();
|
||||||
|
ctx->mode = 0755;
|
||||||
|
|
||||||
|
fc->s_fs_info = sbi;
|
||||||
|
fc->ops = &spufs_context_ops;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nomem_ctx:
|
||||||
|
kfree(ctx);
|
||||||
|
nomem:
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct file_system_type spufs_type = {
|
static struct file_system_type spufs_type = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "spufs",
|
.name = "spufs",
|
||||||
.mount = spufs_mount,
|
.init_fs_context = spufs_init_fs_context,
|
||||||
|
.parameters = &spufs_fs_parameters,
|
||||||
.kill_sb = kill_litter_super,
|
.kill_sb = kill_litter_super,
|
||||||
};
|
};
|
||||||
MODULE_ALIAS_FS("spufs");
|
MODULE_ALIAS_FS("spufs");
|
||||||
|
|
|
@ -12,17 +12,17 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/fs_context.h>
|
||||||
|
#include <linux/fs_parser.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
#include <linux/vfs.h>
|
#include <linux/vfs.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/mount.h>
|
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include <asm/ebcdic.h>
|
#include <asm/ebcdic.h>
|
||||||
#include "hypfs.h"
|
#include "hypfs.h"
|
||||||
|
@ -207,52 +207,44 @@ static int hypfs_release(struct inode *inode, struct file *filp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum { opt_uid, opt_gid, opt_err };
|
enum { Opt_uid, Opt_gid, };
|
||||||
|
|
||||||
static const match_table_t hypfs_tokens = {
|
static const struct fs_parameter_spec hypfs_param_specs[] = {
|
||||||
{opt_uid, "uid=%u"},
|
fsparam_u32("gid", Opt_gid),
|
||||||
{opt_gid, "gid=%u"},
|
fsparam_u32("uid", Opt_uid),
|
||||||
{opt_err, NULL}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int hypfs_parse_options(char *options, struct super_block *sb)
|
static const struct fs_parameter_description hypfs_fs_parameters = {
|
||||||
|
.name = "hypfs",
|
||||||
|
.specs = hypfs_param_specs,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hypfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||||
{
|
{
|
||||||
char *str;
|
struct hypfs_sb_info *hypfs_info = fc->s_fs_info;
|
||||||
substring_t args[MAX_OPT_ARGS];
|
struct fs_parse_result result;
|
||||||
kuid_t uid;
|
kuid_t uid;
|
||||||
kgid_t gid;
|
kgid_t gid;
|
||||||
|
int opt;
|
||||||
|
|
||||||
if (!options)
|
opt = fs_parse(fc, &hypfs_fs_parameters, param, &result);
|
||||||
return 0;
|
if (opt < 0)
|
||||||
while ((str = strsep(&options, ",")) != NULL) {
|
return opt;
|
||||||
int token, option;
|
|
||||||
struct hypfs_sb_info *hypfs_info = sb->s_fs_info;
|
|
||||||
|
|
||||||
if (!*str)
|
switch (opt) {
|
||||||
continue;
|
case Opt_uid:
|
||||||
token = match_token(str, hypfs_tokens, args);
|
uid = make_kuid(current_user_ns(), result.uint_32);
|
||||||
switch (token) {
|
|
||||||
case opt_uid:
|
|
||||||
if (match_int(&args[0], &option))
|
|
||||||
return -EINVAL;
|
|
||||||
uid = make_kuid(current_user_ns(), option);
|
|
||||||
if (!uid_valid(uid))
|
if (!uid_valid(uid))
|
||||||
return -EINVAL;
|
return invalf(fc, "Unknown uid");
|
||||||
hypfs_info->uid = uid;
|
hypfs_info->uid = uid;
|
||||||
break;
|
break;
|
||||||
case opt_gid:
|
case Opt_gid:
|
||||||
if (match_int(&args[0], &option))
|
gid = make_kgid(current_user_ns(), result.uint_32);
|
||||||
return -EINVAL;
|
|
||||||
gid = make_kgid(current_user_ns(), option);
|
|
||||||
if (!gid_valid(gid))
|
if (!gid_valid(gid))
|
||||||
return -EINVAL;
|
return invalf(fc, "Unknown gid");
|
||||||
hypfs_info->gid = gid;
|
hypfs_info->gid = gid;
|
||||||
break;
|
break;
|
||||||
case opt_err:
|
|
||||||
default:
|
|
||||||
pr_err("%s is not a valid mount option\n", str);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -266,26 +258,18 @@ static int hypfs_show_options(struct seq_file *s, struct dentry *root)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
|
static int hypfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||||
{
|
{
|
||||||
|
struct hypfs_sb_info *sbi = sb->s_fs_info;
|
||||||
struct inode *root_inode;
|
struct inode *root_inode;
|
||||||
struct dentry *root_dentry;
|
struct dentry *root_dentry, *update_file;
|
||||||
int rc = 0;
|
int rc;
|
||||||
struct hypfs_sb_info *sbi;
|
|
||||||
|
|
||||||
sbi = kzalloc(sizeof(struct hypfs_sb_info), GFP_KERNEL);
|
|
||||||
if (!sbi)
|
|
||||||
return -ENOMEM;
|
|
||||||
mutex_init(&sbi->lock);
|
|
||||||
sbi->uid = current_uid();
|
|
||||||
sbi->gid = current_gid();
|
|
||||||
sb->s_fs_info = sbi;
|
|
||||||
sb->s_blocksize = PAGE_SIZE;
|
sb->s_blocksize = PAGE_SIZE;
|
||||||
sb->s_blocksize_bits = PAGE_SHIFT;
|
sb->s_blocksize_bits = PAGE_SHIFT;
|
||||||
sb->s_magic = HYPFS_MAGIC;
|
sb->s_magic = HYPFS_MAGIC;
|
||||||
sb->s_op = &hypfs_s_ops;
|
sb->s_op = &hypfs_s_ops;
|
||||||
if (hypfs_parse_options(data, sb))
|
|
||||||
return -EINVAL;
|
|
||||||
root_inode = hypfs_make_inode(sb, S_IFDIR | 0755);
|
root_inode = hypfs_make_inode(sb, S_IFDIR | 0755);
|
||||||
if (!root_inode)
|
if (!root_inode)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -300,18 +284,46 @@ static int hypfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
rc = hypfs_diag_create_files(root_dentry);
|
rc = hypfs_diag_create_files(root_dentry);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
sbi->update_file = hypfs_create_update_file(root_dentry);
|
update_file = hypfs_create_update_file(root_dentry);
|
||||||
if (IS_ERR(sbi->update_file))
|
if (IS_ERR(update_file))
|
||||||
return PTR_ERR(sbi->update_file);
|
return PTR_ERR(update_file);
|
||||||
|
sbi->update_file = update_file;
|
||||||
hypfs_update_update(sb);
|
hypfs_update_update(sb);
|
||||||
pr_info("Hypervisor filesystem mounted\n");
|
pr_info("Hypervisor filesystem mounted\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dentry *hypfs_mount(struct file_system_type *fst, int flags,
|
static int hypfs_get_tree(struct fs_context *fc)
|
||||||
const char *devname, void *data)
|
|
||||||
{
|
{
|
||||||
return mount_single(fst, flags, data, hypfs_fill_super);
|
return get_tree_single(fc, hypfs_fill_super);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hypfs_free_fc(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
kfree(fc->s_fs_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fs_context_operations hypfs_context_ops = {
|
||||||
|
.free = hypfs_free_fc,
|
||||||
|
.parse_param = hypfs_parse_param,
|
||||||
|
.get_tree = hypfs_get_tree,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hypfs_init_fs_context(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
struct hypfs_sb_info *sbi;
|
||||||
|
|
||||||
|
sbi = kzalloc(sizeof(struct hypfs_sb_info), GFP_KERNEL);
|
||||||
|
if (!sbi)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&sbi->lock);
|
||||||
|
sbi->uid = current_uid();
|
||||||
|
sbi->gid = current_gid();
|
||||||
|
|
||||||
|
fc->s_fs_info = sbi;
|
||||||
|
fc->ops = &hypfs_context_ops;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hypfs_kill_super(struct super_block *sb)
|
static void hypfs_kill_super(struct super_block *sb)
|
||||||
|
@ -442,7 +454,8 @@ static const struct file_operations hypfs_file_ops = {
|
||||||
static struct file_system_type hypfs_type = {
|
static struct file_system_type hypfs_type = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "s390_hypfs",
|
.name = "s390_hypfs",
|
||||||
.mount = hypfs_mount,
|
.init_fs_context = hypfs_init_fs_context,
|
||||||
|
.parameters = &hypfs_fs_parameters,
|
||||||
.kill_sb = hypfs_kill_super
|
.kill_sb = hypfs_kill_super
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
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)
|
||||||
return 0;
|
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();
|
ENTER();
|
||||||
|
|
||||||
if (!opts || !*opts)
|
opt = fs_parse(fc, &ffs_fs_fs_parameters, param, &result);
|
||||||
return 0;
|
if (opt < 0)
|
||||||
|
return opt;
|
||||||
|
|
||||||
for (;;) {
|
switch (opt) {
|
||||||
unsigned long value;
|
case Opt_no_disconnect:
|
||||||
char *eq, *comma;
|
data->no_disconnect = result.boolean;
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
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;
|
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;
|
break;
|
||||||
else if (!memcmp(opts, "fmode", 5))
|
case Opt_fmode:
|
||||||
data->perms.mode = (value & 0666) | S_IFREG;
|
data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
|
||||||
else
|
break;
|
||||||
goto invalid;
|
case Opt_mode:
|
||||||
|
data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
|
||||||
|
data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case Opt_uid:
|
||||||
if (!memcmp(opts, "mode", 4)) {
|
data->perms.uid = make_kuid(current_user_ns(), result.uint_32);
|
||||||
data->root_mode = (value & 0555) | S_IFDIR;
|
if (!uid_valid(data->perms.uid))
|
||||||
data->perms.mode = (value & 0666) | S_IFREG;
|
goto unmapped_value;
|
||||||
} else {
|
|
||||||
goto invalid;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
case Opt_gid:
|
||||||
case 3:
|
data->perms.gid = make_kgid(current_user_ns(), result.uint_32);
|
||||||
if (!memcmp(opts, "uid", 3)) {
|
if (!gid_valid(data->perms.gid))
|
||||||
data->perms.uid = make_kuid(current_user_ns(), value);
|
goto unmapped_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;
|
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;
|
||||||
|
|
||||||
|
unmapped_value:
|
||||||
|
return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "mount -t functionfs dev_name /dev/function" ends up here */
|
/*
|
||||||
|
* Set up the superblock for a mount.
|
||||||
static struct dentry *
|
*/
|
||||||
ffs_fs_mount(struct file_system_type *t, int flags,
|
static int ffs_fs_get_tree(struct fs_context *fc)
|
||||||
const char *dev_name, void *opts)
|
|
||||||
{
|
{
|
||||||
struct ffs_sb_fill_data data = {
|
struct ffs_sb_fill_data *ctx = fc->fs_private;
|
||||||
.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;
|
|
||||||
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;
|
|
||||||
data.ffs_data = ffs;
|
|
||||||
|
|
||||||
rv = mount_nodev(t, flags, &data, ffs_sb_fill);
|
ffs->private_data = ffs_dev;
|
||||||
if (IS_ERR(rv) && data.ffs_data) {
|
ctx->ffs_data = ffs;
|
||||||
ffs_release_dev(data.ffs_data);
|
return get_tree_nodev(fc, ffs_sb_fill);
|
||||||
ffs_data_put(data.ffs_data);
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
return rv;
|
|
||||||
|
kfree(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
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");
|
||||||
|
|
|
@ -584,10 +584,10 @@ struct gfs2_args {
|
||||||
unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */
|
unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */
|
||||||
unsigned int ar_loccookie:1; /* use location based readdir
|
unsigned int ar_loccookie:1; /* use location based readdir
|
||||||
cookies */
|
cookies */
|
||||||
int ar_commit; /* Commit interval */
|
s32 ar_commit; /* Commit interval */
|
||||||
int ar_statfs_quantum; /* The fast statfs interval */
|
s32 ar_statfs_quantum; /* The fast statfs interval */
|
||||||
int ar_quota_quantum; /* The quota interval */
|
s32 ar_quota_quantum; /* The quota interval */
|
||||||
int ar_statfs_percent; /* The % change to force sync */
|
s32 ar_statfs_percent; /* The % change to force sync */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gfs2_tune {
|
struct gfs2_tune {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/lockdep.h>
|
#include <linux/lockdep.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/backing-dev.h>
|
#include <linux/backing-dev.h>
|
||||||
|
#include <linux/fs_parser.h>
|
||||||
|
|
||||||
#include "gfs2.h"
|
#include "gfs2.h"
|
||||||
#include "incore.h"
|
#include "incore.h"
|
||||||
|
@ -1031,16 +1032,17 @@ void gfs2_online_uevent(struct gfs2_sbd *sdp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fill_super - Read in superblock
|
* gfs2_fill_super - Read in superblock
|
||||||
* @sb: The VFS superblock
|
* @sb: The VFS superblock
|
||||||
* @data: Mount options
|
* @args: Mount options
|
||||||
* @silent: Don't complain if it's not a GFS2 filesystem
|
* @silent: Don't complain if it's not a GFS2 filesystem
|
||||||
*
|
*
|
||||||
* Returns: errno
|
* Returns: -errno
|
||||||
*/
|
*/
|
||||||
|
static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||||
static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent)
|
|
||||||
{
|
{
|
||||||
|
struct gfs2_args *args = fc->fs_private;
|
||||||
|
int silent = fc->sb_flags & SB_SILENT;
|
||||||
struct gfs2_sbd *sdp;
|
struct gfs2_sbd *sdp;
|
||||||
struct gfs2_holder mount_gh;
|
struct gfs2_holder mount_gh;
|
||||||
int error;
|
int error;
|
||||||
|
@ -1205,161 +1207,411 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_gfs2_super(struct super_block *s, void *data)
|
/**
|
||||||
|
* gfs2_get_tree - Get the GFS2 superblock and root directory
|
||||||
|
* @fc: The filesystem context
|
||||||
|
*
|
||||||
|
* Returns: 0 or -errno on error
|
||||||
|
*/
|
||||||
|
static int gfs2_get_tree(struct fs_context *fc)
|
||||||
{
|
{
|
||||||
s->s_bdev = data;
|
struct gfs2_args *args = fc->fs_private;
|
||||||
s->s_dev = s->s_bdev->bd_dev;
|
struct gfs2_sbd *sdp;
|
||||||
s->s_bdi = bdi_get(s->s_bdev->bd_bdi);
|
int error;
|
||||||
|
|
||||||
|
error = get_tree_bdev(fc, gfs2_fill_super);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
sdp = fc->root->d_sb->s_fs_info;
|
||||||
|
dput(fc->root);
|
||||||
|
if (args->ar_meta)
|
||||||
|
fc->root = dget(sdp->sd_master_dir);
|
||||||
|
else
|
||||||
|
fc->root = dget(sdp->sd_root_dir);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_gfs2_super(struct super_block *s, void *ptr)
|
static void gfs2_fc_free(struct fs_context *fc)
|
||||||
{
|
{
|
||||||
struct block_device *bdev = ptr;
|
struct gfs2_args *args = fc->fs_private;
|
||||||
return (bdev == s->s_bdev);
|
|
||||||
|
kfree(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
enum gfs2_param {
|
||||||
* gfs2_mount - Get the GFS2 superblock
|
Opt_lockproto,
|
||||||
* @fs_type: The GFS2 filesystem type
|
Opt_locktable,
|
||||||
* @flags: Mount flags
|
Opt_hostdata,
|
||||||
* @dev_name: The name of the device
|
Opt_spectator,
|
||||||
* @data: The mount arguments
|
Opt_ignore_local_fs,
|
||||||
*
|
Opt_localflocks,
|
||||||
* Q. Why not use get_sb_bdev() ?
|
Opt_localcaching,
|
||||||
* A. We need to select one of two root directories to mount, independent
|
Opt_debug,
|
||||||
* of whether this is the initial, or subsequent, mount of this sb
|
Opt_upgrade,
|
||||||
*
|
Opt_acl,
|
||||||
* Returns: 0 or -ve on error
|
Opt_quota,
|
||||||
*/
|
Opt_suiddir,
|
||||||
|
Opt_data,
|
||||||
|
Opt_meta,
|
||||||
|
Opt_discard,
|
||||||
|
Opt_commit,
|
||||||
|
Opt_errors,
|
||||||
|
Opt_statfs_quantum,
|
||||||
|
Opt_statfs_percent,
|
||||||
|
Opt_quota_quantum,
|
||||||
|
Opt_barrier,
|
||||||
|
Opt_rgrplvb,
|
||||||
|
Opt_loccookie,
|
||||||
|
};
|
||||||
|
|
||||||
static struct dentry *gfs2_mount(struct file_system_type *fs_type, int flags,
|
enum opt_quota {
|
||||||
const char *dev_name, void *data)
|
Opt_quota_unset = 0,
|
||||||
|
Opt_quota_off,
|
||||||
|
Opt_quota_account,
|
||||||
|
Opt_quota_on,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int opt_quota_values[] = {
|
||||||
|
[Opt_quota_off] = GFS2_QUOTA_OFF,
|
||||||
|
[Opt_quota_account] = GFS2_QUOTA_ACCOUNT,
|
||||||
|
[Opt_quota_on] = GFS2_QUOTA_ON,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum opt_data {
|
||||||
|
Opt_data_writeback = GFS2_DATA_WRITEBACK,
|
||||||
|
Opt_data_ordered = GFS2_DATA_ORDERED,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum opt_errors {
|
||||||
|
Opt_errors_withdraw = GFS2_ERRORS_WITHDRAW,
|
||||||
|
Opt_errors_panic = GFS2_ERRORS_PANIC,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fs_parameter_spec gfs2_param_specs[] = {
|
||||||
|
fsparam_string ("lockproto", Opt_lockproto),
|
||||||
|
fsparam_string ("locktable", Opt_locktable),
|
||||||
|
fsparam_string ("hostdata", Opt_hostdata),
|
||||||
|
fsparam_flag ("spectator", Opt_spectator),
|
||||||
|
fsparam_flag ("norecovery", Opt_spectator),
|
||||||
|
fsparam_flag ("ignore_local_fs", Opt_ignore_local_fs),
|
||||||
|
fsparam_flag ("localflocks", Opt_localflocks),
|
||||||
|
fsparam_flag ("localcaching", Opt_localcaching),
|
||||||
|
fsparam_flag_no("debug", Opt_debug),
|
||||||
|
fsparam_flag ("upgrade", Opt_upgrade),
|
||||||
|
fsparam_flag_no("acl", Opt_acl),
|
||||||
|
fsparam_flag_no("suiddir", Opt_suiddir),
|
||||||
|
fsparam_enum ("data", Opt_data),
|
||||||
|
fsparam_flag ("meta", Opt_meta),
|
||||||
|
fsparam_flag_no("discard", Opt_discard),
|
||||||
|
fsparam_s32 ("commit", Opt_commit),
|
||||||
|
fsparam_enum ("errors", Opt_errors),
|
||||||
|
fsparam_s32 ("statfs_quantum", Opt_statfs_quantum),
|
||||||
|
fsparam_s32 ("statfs_percent", Opt_statfs_percent),
|
||||||
|
fsparam_s32 ("quota_quantum", Opt_quota_quantum),
|
||||||
|
fsparam_flag_no("barrier", Opt_barrier),
|
||||||
|
fsparam_flag_no("rgrplvb", Opt_rgrplvb),
|
||||||
|
fsparam_flag_no("loccookie", Opt_loccookie),
|
||||||
|
/* quota can be a flag or an enum so it gets special treatment */
|
||||||
|
__fsparam(fs_param_is_enum, "quota", Opt_quota, fs_param_neg_with_no|fs_param_v_optional),
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fs_parameter_enum gfs2_param_enums[] = {
|
||||||
|
{ Opt_quota, "off", Opt_quota_off },
|
||||||
|
{ Opt_quota, "account", Opt_quota_account },
|
||||||
|
{ Opt_quota, "on", Opt_quota_on },
|
||||||
|
{ Opt_data, "writeback", Opt_data_writeback },
|
||||||
|
{ Opt_data, "ordered", Opt_data_ordered },
|
||||||
|
{ Opt_errors, "withdraw", Opt_errors_withdraw },
|
||||||
|
{ Opt_errors, "panic", Opt_errors_panic },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct fs_parameter_description gfs2_fs_parameters = {
|
||||||
|
.name = "gfs2",
|
||||||
|
.specs = gfs2_param_specs,
|
||||||
|
.enums = gfs2_param_enums,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Parse a single mount parameter */
|
||||||
|
static int gfs2_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||||
{
|
{
|
||||||
struct block_device *bdev;
|
struct gfs2_args *args = fc->fs_private;
|
||||||
struct super_block *s;
|
struct fs_parse_result result;
|
||||||
fmode_t mode = FMODE_READ | FMODE_EXCL;
|
int o;
|
||||||
int error;
|
|
||||||
struct gfs2_args args;
|
|
||||||
struct gfs2_sbd *sdp;
|
|
||||||
|
|
||||||
if (!(flags & SB_RDONLY))
|
o = fs_parse(fc, &gfs2_fs_parameters, param, &result);
|
||||||
mode |= FMODE_WRITE;
|
if (o < 0)
|
||||||
|
return o;
|
||||||
|
|
||||||
bdev = blkdev_get_by_path(dev_name, mode, fs_type);
|
switch (o) {
|
||||||
if (IS_ERR(bdev))
|
case Opt_lockproto:
|
||||||
return ERR_CAST(bdev);
|
strlcpy(args->ar_lockproto, param->string, GFS2_LOCKNAME_LEN);
|
||||||
|
break;
|
||||||
/*
|
case Opt_locktable:
|
||||||
* once the super is inserted into the list by sget, s_umount
|
strlcpy(args->ar_locktable, param->string, GFS2_LOCKNAME_LEN);
|
||||||
* will protect the lockfs code from trying to start a snapshot
|
break;
|
||||||
* while we are mounting
|
case Opt_hostdata:
|
||||||
*/
|
strlcpy(args->ar_hostdata, param->string, GFS2_LOCKNAME_LEN);
|
||||||
mutex_lock(&bdev->bd_fsfreeze_mutex);
|
break;
|
||||||
if (bdev->bd_fsfreeze_count > 0) {
|
case Opt_spectator:
|
||||||
mutex_unlock(&bdev->bd_fsfreeze_mutex);
|
args->ar_spectator = 1;
|
||||||
error = -EBUSY;
|
break;
|
||||||
goto error_bdev;
|
case Opt_ignore_local_fs:
|
||||||
}
|
/* Retained for backwards compat only */
|
||||||
s = sget(fs_type, test_gfs2_super, set_gfs2_super, flags, bdev);
|
break;
|
||||||
mutex_unlock(&bdev->bd_fsfreeze_mutex);
|
case Opt_localflocks:
|
||||||
error = PTR_ERR(s);
|
args->ar_localflocks = 1;
|
||||||
if (IS_ERR(s))
|
break;
|
||||||
goto error_bdev;
|
case Opt_localcaching:
|
||||||
|
/* Retained for backwards compat only */
|
||||||
if (s->s_root) {
|
break;
|
||||||
/*
|
case Opt_debug:
|
||||||
* s_umount nests inside bd_mutex during
|
if (result.boolean && args->ar_errors == GFS2_ERRORS_PANIC)
|
||||||
* __invalidate_device(). blkdev_put() acquires
|
return invalf(fc, "gfs2: -o debug and -o errors=panic are mutually exclusive");
|
||||||
* bd_mutex and can't be called under s_umount. Drop
|
args->ar_debug = result.boolean;
|
||||||
* s_umount temporarily. This is safe as we're
|
break;
|
||||||
* holding an active reference.
|
case Opt_upgrade:
|
||||||
*/
|
/* Retained for backwards compat only */
|
||||||
up_write(&s->s_umount);
|
break;
|
||||||
blkdev_put(bdev, mode);
|
case Opt_acl:
|
||||||
down_write(&s->s_umount);
|
args->ar_posix_acl = result.boolean;
|
||||||
} else {
|
break;
|
||||||
/* s_mode must be set before deactivate_locked_super calls */
|
case Opt_quota:
|
||||||
s->s_mode = mode;
|
/* The quota option can be a flag or an enum. A non-zero int_32
|
||||||
}
|
result means that we have an enum index. Otherwise we have
|
||||||
|
to rely on the 'negated' flag to tell us whether 'quota' or
|
||||||
memset(&args, 0, sizeof(args));
|
'noquota' was specified. */
|
||||||
args.ar_quota = GFS2_QUOTA_DEFAULT;
|
if (result.negated)
|
||||||
args.ar_data = GFS2_DATA_DEFAULT;
|
args->ar_quota = GFS2_QUOTA_OFF;
|
||||||
args.ar_commit = 30;
|
else if (result.int_32 > 0)
|
||||||
args.ar_statfs_quantum = 30;
|
args->ar_quota = opt_quota_values[result.int_32];
|
||||||
args.ar_quota_quantum = 60;
|
|
||||||
args.ar_errors = GFS2_ERRORS_DEFAULT;
|
|
||||||
|
|
||||||
error = gfs2_mount_args(&args, data);
|
|
||||||
if (error) {
|
|
||||||
pr_warn("can't parse mount arguments\n");
|
|
||||||
goto error_super;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->s_root) {
|
|
||||||
error = -EBUSY;
|
|
||||||
if ((flags ^ s->s_flags) & SB_RDONLY)
|
|
||||||
goto error_super;
|
|
||||||
} else {
|
|
||||||
snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
|
|
||||||
sb_set_blocksize(s, block_size(bdev));
|
|
||||||
error = fill_super(s, &args, flags & SB_SILENT ? 1 : 0);
|
|
||||||
if (error)
|
|
||||||
goto error_super;
|
|
||||||
s->s_flags |= SB_ACTIVE;
|
|
||||||
bdev->bd_super = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
sdp = s->s_fs_info;
|
|
||||||
if (args.ar_meta)
|
|
||||||
return dget(sdp->sd_master_dir);
|
|
||||||
else
|
else
|
||||||
return dget(sdp->sd_root_dir);
|
args->ar_quota = GFS2_QUOTA_ON;
|
||||||
|
break;
|
||||||
error_super:
|
case Opt_suiddir:
|
||||||
deactivate_locked_super(s);
|
args->ar_suiddir = result.boolean;
|
||||||
return ERR_PTR(error);
|
break;
|
||||||
error_bdev:
|
case Opt_data:
|
||||||
blkdev_put(bdev, mode);
|
/* The uint_32 result maps directly to GFS2_DATA_* */
|
||||||
return ERR_PTR(error);
|
args->ar_data = result.uint_32;
|
||||||
|
break;
|
||||||
|
case Opt_meta:
|
||||||
|
args->ar_meta = 1;
|
||||||
|
break;
|
||||||
|
case Opt_discard:
|
||||||
|
args->ar_discard = result.boolean;
|
||||||
|
break;
|
||||||
|
case Opt_commit:
|
||||||
|
if (result.int_32 <= 0)
|
||||||
|
return invalf(fc, "gfs2: commit mount option requires a positive numeric argument");
|
||||||
|
args->ar_commit = result.int_32;
|
||||||
|
break;
|
||||||
|
case Opt_statfs_quantum:
|
||||||
|
if (result.int_32 < 0)
|
||||||
|
return invalf(fc, "gfs2: statfs_quantum mount option requires a non-negative numeric argument");
|
||||||
|
args->ar_statfs_quantum = result.int_32;
|
||||||
|
break;
|
||||||
|
case Opt_quota_quantum:
|
||||||
|
if (result.int_32 <= 0)
|
||||||
|
return invalf(fc, "gfs2: quota_quantum mount option requires a positive numeric argument");
|
||||||
|
args->ar_quota_quantum = result.int_32;
|
||||||
|
break;
|
||||||
|
case Opt_statfs_percent:
|
||||||
|
if (result.int_32 < 0 || result.int_32 > 100)
|
||||||
|
return invalf(fc, "gfs2: statfs_percent mount option requires a numeric argument between 0 and 100");
|
||||||
|
args->ar_statfs_percent = result.int_32;
|
||||||
|
break;
|
||||||
|
case Opt_errors:
|
||||||
|
if (args->ar_debug && result.uint_32 == GFS2_ERRORS_PANIC)
|
||||||
|
return invalf(fc, "gfs2: -o debug and -o errors=panic are mutually exclusive");
|
||||||
|
args->ar_errors = result.uint_32;
|
||||||
|
break;
|
||||||
|
case Opt_barrier:
|
||||||
|
args->ar_nobarrier = result.boolean;
|
||||||
|
break;
|
||||||
|
case Opt_rgrplvb:
|
||||||
|
args->ar_rgrplvb = result.boolean;
|
||||||
|
break;
|
||||||
|
case Opt_loccookie:
|
||||||
|
args->ar_loccookie = result.boolean;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return invalf(fc, "gfs2: invalid mount option: %s", param->key);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_meta_super(struct super_block *s, void *ptr)
|
static int gfs2_reconfigure(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
struct super_block *sb = fc->root->d_sb;
|
||||||
|
struct gfs2_sbd *sdp = sb->s_fs_info;
|
||||||
|
struct gfs2_args *oldargs = &sdp->sd_args;
|
||||||
|
struct gfs2_args *newargs = fc->fs_private;
|
||||||
|
struct gfs2_tune *gt = &sdp->sd_tune;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
sync_filesystem(sb);
|
||||||
|
|
||||||
|
spin_lock(>->gt_spin);
|
||||||
|
oldargs->ar_commit = gt->gt_logd_secs;
|
||||||
|
oldargs->ar_quota_quantum = gt->gt_quota_quantum;
|
||||||
|
if (gt->gt_statfs_slow)
|
||||||
|
oldargs->ar_statfs_quantum = 0;
|
||||||
|
else
|
||||||
|
oldargs->ar_statfs_quantum = gt->gt_statfs_quantum;
|
||||||
|
spin_unlock(>->gt_spin);
|
||||||
|
|
||||||
|
if (strcmp(newargs->ar_lockproto, oldargs->ar_lockproto)) {
|
||||||
|
errorf(fc, "gfs2: reconfiguration of locking protocol not allowed");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (strcmp(newargs->ar_locktable, oldargs->ar_locktable)) {
|
||||||
|
errorf(fc, "gfs2: reconfiguration of lock table not allowed");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (strcmp(newargs->ar_hostdata, oldargs->ar_hostdata)) {
|
||||||
|
errorf(fc, "gfs2: reconfiguration of host data not allowed");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (newargs->ar_spectator != oldargs->ar_spectator) {
|
||||||
|
errorf(fc, "gfs2: reconfiguration of spectator mode not allowed");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (newargs->ar_localflocks != oldargs->ar_localflocks) {
|
||||||
|
errorf(fc, "gfs2: reconfiguration of localflocks not allowed");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (newargs->ar_meta != oldargs->ar_meta) {
|
||||||
|
errorf(fc, "gfs2: switching between gfs2 and gfs2meta not allowed");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (oldargs->ar_spectator)
|
||||||
|
fc->sb_flags |= SB_RDONLY;
|
||||||
|
|
||||||
|
if ((sb->s_flags ^ fc->sb_flags) & SB_RDONLY) {
|
||||||
|
if (fc->sb_flags & SB_RDONLY) {
|
||||||
|
error = gfs2_make_fs_ro(sdp);
|
||||||
|
if (error)
|
||||||
|
errorf(fc, "gfs2: unable to remount read-only");
|
||||||
|
} else {
|
||||||
|
error = gfs2_make_fs_rw(sdp);
|
||||||
|
if (error)
|
||||||
|
errorf(fc, "gfs2: unable to remount read-write");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sdp->sd_args = *newargs;
|
||||||
|
|
||||||
|
if (sdp->sd_args.ar_posix_acl)
|
||||||
|
sb->s_flags |= SB_POSIXACL;
|
||||||
|
else
|
||||||
|
sb->s_flags &= ~SB_POSIXACL;
|
||||||
|
if (sdp->sd_args.ar_nobarrier)
|
||||||
|
set_bit(SDF_NOBARRIERS, &sdp->sd_flags);
|
||||||
|
else
|
||||||
|
clear_bit(SDF_NOBARRIERS, &sdp->sd_flags);
|
||||||
|
spin_lock(>->gt_spin);
|
||||||
|
gt->gt_logd_secs = newargs->ar_commit;
|
||||||
|
gt->gt_quota_quantum = newargs->ar_quota_quantum;
|
||||||
|
if (newargs->ar_statfs_quantum) {
|
||||||
|
gt->gt_statfs_slow = 0;
|
||||||
|
gt->gt_statfs_quantum = newargs->ar_statfs_quantum;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gt->gt_statfs_slow = 1;
|
||||||
|
gt->gt_statfs_quantum = 30;
|
||||||
|
}
|
||||||
|
spin_unlock(>->gt_spin);
|
||||||
|
|
||||||
|
gfs2_online_uevent(sdp);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fs_context_operations gfs2_context_ops = {
|
||||||
|
.free = gfs2_fc_free,
|
||||||
|
.parse_param = gfs2_parse_param,
|
||||||
|
.get_tree = gfs2_get_tree,
|
||||||
|
.reconfigure = gfs2_reconfigure,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Set up the filesystem mount context */
|
||||||
|
static int gfs2_init_fs_context(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
struct gfs2_args *args;
|
||||||
|
|
||||||
|
args = kzalloc(sizeof(*args), GFP_KERNEL);
|
||||||
|
if (args == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
args->ar_quota = GFS2_QUOTA_DEFAULT;
|
||||||
|
args->ar_data = GFS2_DATA_DEFAULT;
|
||||||
|
args->ar_commit = 30;
|
||||||
|
args->ar_statfs_quantum = 30;
|
||||||
|
args->ar_quota_quantum = 60;
|
||||||
|
args->ar_errors = GFS2_ERRORS_DEFAULT;
|
||||||
|
|
||||||
|
fc->fs_private = args;
|
||||||
|
fc->ops = &gfs2_context_ops;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_meta_super(struct super_block *s, struct fs_context *fc)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dentry *gfs2_mount_meta(struct file_system_type *fs_type,
|
static int test_meta_super(struct super_block *s, struct fs_context *fc)
|
||||||
int flags, const char *dev_name, void *data)
|
{
|
||||||
|
return (fc->sget_key == s->s_bdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gfs2_meta_get_tree(struct fs_context *fc)
|
||||||
{
|
{
|
||||||
struct super_block *s;
|
struct super_block *s;
|
||||||
struct gfs2_sbd *sdp;
|
struct gfs2_sbd *sdp;
|
||||||
struct path path;
|
struct path path;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (!dev_name || !*dev_name)
|
if (!fc->source || !*fc->source)
|
||||||
return ERR_PTR(-EINVAL);
|
return -EINVAL;
|
||||||
|
|
||||||
error = kern_path(dev_name, LOOKUP_FOLLOW, &path);
|
error = kern_path(fc->source, LOOKUP_FOLLOW, &path);
|
||||||
if (error) {
|
if (error) {
|
||||||
pr_warn("path_lookup on %s returned error %d\n",
|
pr_warn("path_lookup on %s returned error %d\n",
|
||||||
dev_name, error);
|
fc->source, error);
|
||||||
return ERR_PTR(error);
|
return error;
|
||||||
}
|
}
|
||||||
s = sget(&gfs2_fs_type, test_gfs2_super, set_meta_super, flags,
|
fc->fs_type = &gfs2_fs_type;
|
||||||
path.dentry->d_sb->s_bdev);
|
fc->sget_key = path.dentry->d_sb->s_bdev;
|
||||||
|
s = sget_fc(fc, test_meta_super, set_meta_super);
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
if (IS_ERR(s)) {
|
if (IS_ERR(s)) {
|
||||||
pr_warn("gfs2 mount does not exist\n");
|
pr_warn("gfs2 mount does not exist\n");
|
||||||
return ERR_CAST(s);
|
return PTR_ERR(s);
|
||||||
}
|
}
|
||||||
if ((flags ^ s->s_flags) & SB_RDONLY) {
|
if ((fc->sb_flags ^ s->s_flags) & SB_RDONLY) {
|
||||||
deactivate_locked_super(s);
|
deactivate_locked_super(s);
|
||||||
return ERR_PTR(-EBUSY);
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
sdp = s->s_fs_info;
|
sdp = s->s_fs_info;
|
||||||
return dget(sdp->sd_master_dir);
|
fc->root = dget(sdp->sd_master_dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fs_context_operations gfs2_meta_context_ops = {
|
||||||
|
.get_tree = gfs2_meta_get_tree,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gfs2_meta_init_fs_context(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
int ret = gfs2_init_fs_context(fc);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fc->ops = &gfs2_meta_context_ops;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gfs2_kill_sb(struct super_block *sb)
|
static void gfs2_kill_sb(struct super_block *sb)
|
||||||
|
@ -1383,7 +1635,8 @@ static void gfs2_kill_sb(struct super_block *sb)
|
||||||
struct file_system_type gfs2_fs_type = {
|
struct file_system_type gfs2_fs_type = {
|
||||||
.name = "gfs2",
|
.name = "gfs2",
|
||||||
.fs_flags = FS_REQUIRES_DEV,
|
.fs_flags = FS_REQUIRES_DEV,
|
||||||
.mount = gfs2_mount,
|
.init_fs_context = gfs2_init_fs_context,
|
||||||
|
.parameters = &gfs2_fs_parameters,
|
||||||
.kill_sb = gfs2_kill_sb,
|
.kill_sb = gfs2_kill_sb,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
@ -1392,7 +1645,7 @@ MODULE_ALIAS_FS("gfs2");
|
||||||
struct file_system_type gfs2meta_fs_type = {
|
struct file_system_type gfs2meta_fs_type = {
|
||||||
.name = "gfs2meta",
|
.name = "gfs2meta",
|
||||||
.fs_flags = FS_REQUIRES_DEV,
|
.fs_flags = FS_REQUIRES_DEV,
|
||||||
.mount = gfs2_mount_meta,
|
.init_fs_context = gfs2_meta_init_fs_context,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
MODULE_ALIAS_FS("gfs2meta");
|
MODULE_ALIAS_FS("gfs2meta");
|
||||||
|
|
333
fs/gfs2/super.c
333
fs/gfs2/super.c
|
@ -44,258 +44,6 @@
|
||||||
#include "xattr.h"
|
#include "xattr.h"
|
||||||
#include "lops.h"
|
#include "lops.h"
|
||||||
|
|
||||||
#define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x)
|
|
||||||
|
|
||||||
enum {
|
|
||||||
Opt_lockproto,
|
|
||||||
Opt_locktable,
|
|
||||||
Opt_hostdata,
|
|
||||||
Opt_spectator,
|
|
||||||
Opt_ignore_local_fs,
|
|
||||||
Opt_localflocks,
|
|
||||||
Opt_localcaching,
|
|
||||||
Opt_debug,
|
|
||||||
Opt_nodebug,
|
|
||||||
Opt_upgrade,
|
|
||||||
Opt_acl,
|
|
||||||
Opt_noacl,
|
|
||||||
Opt_quota_off,
|
|
||||||
Opt_quota_account,
|
|
||||||
Opt_quota_on,
|
|
||||||
Opt_quota,
|
|
||||||
Opt_noquota,
|
|
||||||
Opt_suiddir,
|
|
||||||
Opt_nosuiddir,
|
|
||||||
Opt_data_writeback,
|
|
||||||
Opt_data_ordered,
|
|
||||||
Opt_meta,
|
|
||||||
Opt_discard,
|
|
||||||
Opt_nodiscard,
|
|
||||||
Opt_commit,
|
|
||||||
Opt_err_withdraw,
|
|
||||||
Opt_err_panic,
|
|
||||||
Opt_statfs_quantum,
|
|
||||||
Opt_statfs_percent,
|
|
||||||
Opt_quota_quantum,
|
|
||||||
Opt_barrier,
|
|
||||||
Opt_nobarrier,
|
|
||||||
Opt_rgrplvb,
|
|
||||||
Opt_norgrplvb,
|
|
||||||
Opt_loccookie,
|
|
||||||
Opt_noloccookie,
|
|
||||||
Opt_error,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const match_table_t tokens = {
|
|
||||||
{Opt_lockproto, "lockproto=%s"},
|
|
||||||
{Opt_locktable, "locktable=%s"},
|
|
||||||
{Opt_hostdata, "hostdata=%s"},
|
|
||||||
{Opt_spectator, "spectator"},
|
|
||||||
{Opt_spectator, "norecovery"},
|
|
||||||
{Opt_ignore_local_fs, "ignore_local_fs"},
|
|
||||||
{Opt_localflocks, "localflocks"},
|
|
||||||
{Opt_localcaching, "localcaching"},
|
|
||||||
{Opt_debug, "debug"},
|
|
||||||
{Opt_nodebug, "nodebug"},
|
|
||||||
{Opt_upgrade, "upgrade"},
|
|
||||||
{Opt_acl, "acl"},
|
|
||||||
{Opt_noacl, "noacl"},
|
|
||||||
{Opt_quota_off, "quota=off"},
|
|
||||||
{Opt_quota_account, "quota=account"},
|
|
||||||
{Opt_quota_on, "quota=on"},
|
|
||||||
{Opt_quota, "quota"},
|
|
||||||
{Opt_noquota, "noquota"},
|
|
||||||
{Opt_suiddir, "suiddir"},
|
|
||||||
{Opt_nosuiddir, "nosuiddir"},
|
|
||||||
{Opt_data_writeback, "data=writeback"},
|
|
||||||
{Opt_data_ordered, "data=ordered"},
|
|
||||||
{Opt_meta, "meta"},
|
|
||||||
{Opt_discard, "discard"},
|
|
||||||
{Opt_nodiscard, "nodiscard"},
|
|
||||||
{Opt_commit, "commit=%d"},
|
|
||||||
{Opt_err_withdraw, "errors=withdraw"},
|
|
||||||
{Opt_err_panic, "errors=panic"},
|
|
||||||
{Opt_statfs_quantum, "statfs_quantum=%d"},
|
|
||||||
{Opt_statfs_percent, "statfs_percent=%d"},
|
|
||||||
{Opt_quota_quantum, "quota_quantum=%d"},
|
|
||||||
{Opt_barrier, "barrier"},
|
|
||||||
{Opt_nobarrier, "nobarrier"},
|
|
||||||
{Opt_rgrplvb, "rgrplvb"},
|
|
||||||
{Opt_norgrplvb, "norgrplvb"},
|
|
||||||
{Opt_loccookie, "loccookie"},
|
|
||||||
{Opt_noloccookie, "noloccookie"},
|
|
||||||
{Opt_error, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gfs2_mount_args - Parse mount options
|
|
||||||
* @args: The structure into which the parsed options will be written
|
|
||||||
* @options: The options to parse
|
|
||||||
*
|
|
||||||
* Return: errno
|
|
||||||
*/
|
|
||||||
|
|
||||||
int gfs2_mount_args(struct gfs2_args *args, char *options)
|
|
||||||
{
|
|
||||||
char *o;
|
|
||||||
int token;
|
|
||||||
substring_t tmp[MAX_OPT_ARGS];
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
/* Split the options into tokens with the "," character and
|
|
||||||
process them */
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
o = strsep(&options, ",");
|
|
||||||
if (o == NULL)
|
|
||||||
break;
|
|
||||||
if (*o == '\0')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
token = match_token(o, tokens, tmp);
|
|
||||||
switch (token) {
|
|
||||||
case Opt_lockproto:
|
|
||||||
match_strlcpy(args->ar_lockproto, &tmp[0],
|
|
||||||
GFS2_LOCKNAME_LEN);
|
|
||||||
break;
|
|
||||||
case Opt_locktable:
|
|
||||||
match_strlcpy(args->ar_locktable, &tmp[0],
|
|
||||||
GFS2_LOCKNAME_LEN);
|
|
||||||
break;
|
|
||||||
case Opt_hostdata:
|
|
||||||
match_strlcpy(args->ar_hostdata, &tmp[0],
|
|
||||||
GFS2_LOCKNAME_LEN);
|
|
||||||
break;
|
|
||||||
case Opt_spectator:
|
|
||||||
args->ar_spectator = 1;
|
|
||||||
break;
|
|
||||||
case Opt_ignore_local_fs:
|
|
||||||
/* Retained for backwards compat only */
|
|
||||||
break;
|
|
||||||
case Opt_localflocks:
|
|
||||||
args->ar_localflocks = 1;
|
|
||||||
break;
|
|
||||||
case Opt_localcaching:
|
|
||||||
/* Retained for backwards compat only */
|
|
||||||
break;
|
|
||||||
case Opt_debug:
|
|
||||||
if (args->ar_errors == GFS2_ERRORS_PANIC) {
|
|
||||||
pr_warn("-o debug and -o errors=panic are mutually exclusive\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
args->ar_debug = 1;
|
|
||||||
break;
|
|
||||||
case Opt_nodebug:
|
|
||||||
args->ar_debug = 0;
|
|
||||||
break;
|
|
||||||
case Opt_upgrade:
|
|
||||||
/* Retained for backwards compat only */
|
|
||||||
break;
|
|
||||||
case Opt_acl:
|
|
||||||
args->ar_posix_acl = 1;
|
|
||||||
break;
|
|
||||||
case Opt_noacl:
|
|
||||||
args->ar_posix_acl = 0;
|
|
||||||
break;
|
|
||||||
case Opt_quota_off:
|
|
||||||
case Opt_noquota:
|
|
||||||
args->ar_quota = GFS2_QUOTA_OFF;
|
|
||||||
break;
|
|
||||||
case Opt_quota_account:
|
|
||||||
args->ar_quota = GFS2_QUOTA_ACCOUNT;
|
|
||||||
break;
|
|
||||||
case Opt_quota_on:
|
|
||||||
case Opt_quota:
|
|
||||||
args->ar_quota = GFS2_QUOTA_ON;
|
|
||||||
break;
|
|
||||||
case Opt_suiddir:
|
|
||||||
args->ar_suiddir = 1;
|
|
||||||
break;
|
|
||||||
case Opt_nosuiddir:
|
|
||||||
args->ar_suiddir = 0;
|
|
||||||
break;
|
|
||||||
case Opt_data_writeback:
|
|
||||||
args->ar_data = GFS2_DATA_WRITEBACK;
|
|
||||||
break;
|
|
||||||
case Opt_data_ordered:
|
|
||||||
args->ar_data = GFS2_DATA_ORDERED;
|
|
||||||
break;
|
|
||||||
case Opt_meta:
|
|
||||||
args->ar_meta = 1;
|
|
||||||
break;
|
|
||||||
case Opt_discard:
|
|
||||||
args->ar_discard = 1;
|
|
||||||
break;
|
|
||||||
case Opt_nodiscard:
|
|
||||||
args->ar_discard = 0;
|
|
||||||
break;
|
|
||||||
case Opt_commit:
|
|
||||||
rv = match_int(&tmp[0], &args->ar_commit);
|
|
||||||
if (rv || args->ar_commit <= 0) {
|
|
||||||
pr_warn("commit mount option requires a positive numeric argument\n");
|
|
||||||
return rv ? rv : -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_statfs_quantum:
|
|
||||||
rv = match_int(&tmp[0], &args->ar_statfs_quantum);
|
|
||||||
if (rv || args->ar_statfs_quantum < 0) {
|
|
||||||
pr_warn("statfs_quantum mount option requires a non-negative numeric argument\n");
|
|
||||||
return rv ? rv : -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_quota_quantum:
|
|
||||||
rv = match_int(&tmp[0], &args->ar_quota_quantum);
|
|
||||||
if (rv || args->ar_quota_quantum <= 0) {
|
|
||||||
pr_warn("quota_quantum mount option requires a positive numeric argument\n");
|
|
||||||
return rv ? rv : -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_statfs_percent:
|
|
||||||
rv = match_int(&tmp[0], &args->ar_statfs_percent);
|
|
||||||
if (rv || args->ar_statfs_percent < 0 ||
|
|
||||||
args->ar_statfs_percent > 100) {
|
|
||||||
pr_warn("statfs_percent mount option requires a numeric argument between 0 and 100\n");
|
|
||||||
return rv ? rv : -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Opt_err_withdraw:
|
|
||||||
args->ar_errors = GFS2_ERRORS_WITHDRAW;
|
|
||||||
break;
|
|
||||||
case Opt_err_panic:
|
|
||||||
if (args->ar_debug) {
|
|
||||||
pr_warn("-o debug and -o errors=panic are mutually exclusive\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
args->ar_errors = GFS2_ERRORS_PANIC;
|
|
||||||
break;
|
|
||||||
case Opt_barrier:
|
|
||||||
args->ar_nobarrier = 0;
|
|
||||||
break;
|
|
||||||
case Opt_nobarrier:
|
|
||||||
args->ar_nobarrier = 1;
|
|
||||||
break;
|
|
||||||
case Opt_rgrplvb:
|
|
||||||
args->ar_rgrplvb = 1;
|
|
||||||
break;
|
|
||||||
case Opt_norgrplvb:
|
|
||||||
args->ar_rgrplvb = 0;
|
|
||||||
break;
|
|
||||||
case Opt_loccookie:
|
|
||||||
args->ar_loccookie = 1;
|
|
||||||
break;
|
|
||||||
case Opt_noloccookie:
|
|
||||||
args->ar_loccookie = 0;
|
|
||||||
break;
|
|
||||||
case Opt_error:
|
|
||||||
default:
|
|
||||||
pr_warn("invalid mount option: %s\n", o);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gfs2_jindex_free - Clear all the journal index information
|
* gfs2_jindex_free - Clear all the journal index information
|
||||||
* @sdp: The GFS2 superblock
|
* @sdp: The GFS2 superblock
|
||||||
|
@ -847,7 +595,7 @@ static void gfs2_dirty_inode(struct inode *inode, int flags)
|
||||||
* Returns: errno
|
* Returns: errno
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
|
int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
|
||||||
{
|
{
|
||||||
struct gfs2_holder freeze_gh;
|
struct gfs2_holder freeze_gh;
|
||||||
int error;
|
int error;
|
||||||
|
@ -1226,84 +974,6 @@ static int gfs2_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* gfs2_remount_fs - called when the FS is remounted
|
|
||||||
* @sb: the filesystem
|
|
||||||
* @flags: the remount flags
|
|
||||||
* @data: extra data passed in (not used right now)
|
|
||||||
*
|
|
||||||
* Returns: errno
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
|
|
||||||
{
|
|
||||||
struct gfs2_sbd *sdp = sb->s_fs_info;
|
|
||||||
struct gfs2_args args = sdp->sd_args; /* Default to current settings */
|
|
||||||
struct gfs2_tune *gt = &sdp->sd_tune;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
sync_filesystem(sb);
|
|
||||||
|
|
||||||
spin_lock(>->gt_spin);
|
|
||||||
args.ar_commit = gt->gt_logd_secs;
|
|
||||||
args.ar_quota_quantum = gt->gt_quota_quantum;
|
|
||||||
if (gt->gt_statfs_slow)
|
|
||||||
args.ar_statfs_quantum = 0;
|
|
||||||
else
|
|
||||||
args.ar_statfs_quantum = gt->gt_statfs_quantum;
|
|
||||||
spin_unlock(>->gt_spin);
|
|
||||||
error = gfs2_mount_args(&args, data);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
/* Not allowed to change locking details */
|
|
||||||
if (strcmp(args.ar_lockproto, sdp->sd_args.ar_lockproto) ||
|
|
||||||
strcmp(args.ar_locktable, sdp->sd_args.ar_locktable) ||
|
|
||||||
strcmp(args.ar_hostdata, sdp->sd_args.ar_hostdata))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Some flags must not be changed */
|
|
||||||
if (args_neq(&args, &sdp->sd_args, spectator) ||
|
|
||||||
args_neq(&args, &sdp->sd_args, localflocks) ||
|
|
||||||
args_neq(&args, &sdp->sd_args, meta))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (sdp->sd_args.ar_spectator)
|
|
||||||
*flags |= SB_RDONLY;
|
|
||||||
|
|
||||||
if ((sb->s_flags ^ *flags) & SB_RDONLY) {
|
|
||||||
if (*flags & SB_RDONLY)
|
|
||||||
error = gfs2_make_fs_ro(sdp);
|
|
||||||
else
|
|
||||||
error = gfs2_make_fs_rw(sdp);
|
|
||||||
}
|
|
||||||
|
|
||||||
sdp->sd_args = args;
|
|
||||||
if (sdp->sd_args.ar_posix_acl)
|
|
||||||
sb->s_flags |= SB_POSIXACL;
|
|
||||||
else
|
|
||||||
sb->s_flags &= ~SB_POSIXACL;
|
|
||||||
if (sdp->sd_args.ar_nobarrier)
|
|
||||||
set_bit(SDF_NOBARRIERS, &sdp->sd_flags);
|
|
||||||
else
|
|
||||||
clear_bit(SDF_NOBARRIERS, &sdp->sd_flags);
|
|
||||||
spin_lock(>->gt_spin);
|
|
||||||
gt->gt_logd_secs = args.ar_commit;
|
|
||||||
gt->gt_quota_quantum = args.ar_quota_quantum;
|
|
||||||
if (args.ar_statfs_quantum) {
|
|
||||||
gt->gt_statfs_slow = 0;
|
|
||||||
gt->gt_statfs_quantum = args.ar_statfs_quantum;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gt->gt_statfs_slow = 1;
|
|
||||||
gt->gt_statfs_quantum = 30;
|
|
||||||
}
|
|
||||||
spin_unlock(>->gt_spin);
|
|
||||||
|
|
||||||
gfs2_online_uevent(sdp);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gfs2_drop_inode - Drop an inode (test for remote unlink)
|
* gfs2_drop_inode - Drop an inode (test for remote unlink)
|
||||||
* @inode: The inode to drop
|
* @inode: The inode to drop
|
||||||
|
@ -1748,7 +1418,6 @@ const struct super_operations gfs2_super_ops = {
|
||||||
.freeze_super = gfs2_freeze,
|
.freeze_super = gfs2_freeze,
|
||||||
.thaw_super = gfs2_unfreeze,
|
.thaw_super = gfs2_unfreeze,
|
||||||
.statfs = gfs2_statfs,
|
.statfs = gfs2_statfs,
|
||||||
.remount_fs = gfs2_remount_fs,
|
|
||||||
.drop_inode = gfs2_drop_inode,
|
.drop_inode = gfs2_drop_inode,
|
||||||
.show_options = gfs2_show_options,
|
.show_options = gfs2_show_options,
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,8 +24,6 @@ static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp)
|
||||||
|
|
||||||
extern void gfs2_jindex_free(struct gfs2_sbd *sdp);
|
extern void gfs2_jindex_free(struct gfs2_sbd *sdp);
|
||||||
|
|
||||||
extern int gfs2_mount_args(struct gfs2_args *args, char *data);
|
|
||||||
|
|
||||||
extern struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid);
|
extern struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid);
|
||||||
extern int gfs2_jdesc_check(struct gfs2_jdesc *jd);
|
extern int gfs2_jdesc_check(struct gfs2_jdesc *jd);
|
||||||
|
|
||||||
|
@ -33,6 +31,7 @@ extern int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename,
|
||||||
struct gfs2_inode **ipp);
|
struct gfs2_inode **ipp);
|
||||||
|
|
||||||
extern int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
|
extern int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
|
||||||
|
extern int gfs2_make_fs_ro(struct gfs2_sbd *sdp);
|
||||||
extern void gfs2_online_uevent(struct gfs2_sbd *sdp);
|
extern void gfs2_online_uevent(struct gfs2_sbd *sdp);
|
||||||
extern int gfs2_statfs_init(struct gfs2_sbd *sdp);
|
extern int gfs2_statfs_init(struct gfs2_sbd *sdp);
|
||||||
extern void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free,
|
extern void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free,
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/fs_context.h>
|
||||||
|
#include <linux/fs_parser.h>
|
||||||
#include <linux/kdev_t.h>
|
#include <linux/kdev_t.h>
|
||||||
#include <linux/parser.h>
|
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include <linux/bpf_trace.h>
|
#include <linux/bpf_trace.h>
|
||||||
|
@ -583,58 +584,52 @@ static const struct super_operations bpf_super_ops = {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
OPT_MODE,
|
OPT_MODE,
|
||||||
OPT_ERR,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const match_table_t bpf_mount_tokens = {
|
static const struct fs_parameter_spec bpf_param_specs[] = {
|
||||||
{ OPT_MODE, "mode=%o" },
|
fsparam_u32oct ("mode", OPT_MODE),
|
||||||
{ OPT_ERR, NULL },
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fs_parameter_description bpf_fs_parameters = {
|
||||||
|
.name = "bpf",
|
||||||
|
.specs = bpf_param_specs,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bpf_mount_opts {
|
struct bpf_mount_opts {
|
||||||
umode_t mode;
|
umode_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int bpf_parse_options(char *data, struct bpf_mount_opts *opts)
|
static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||||
{
|
{
|
||||||
substring_t args[MAX_OPT_ARGS];
|
struct bpf_mount_opts *opts = fc->fs_private;
|
||||||
int option, token;
|
struct fs_parse_result result;
|
||||||
char *ptr;
|
int opt;
|
||||||
|
|
||||||
opts->mode = S_IRWXUGO;
|
opt = fs_parse(fc, &bpf_fs_parameters, param, &result);
|
||||||
|
if (opt < 0)
|
||||||
while ((ptr = strsep(&data, ",")) != NULL) {
|
|
||||||
if (!*ptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
token = match_token(ptr, bpf_mount_tokens, args);
|
|
||||||
switch (token) {
|
|
||||||
case OPT_MODE:
|
|
||||||
if (match_octal(&args[0], &option))
|
|
||||||
return -EINVAL;
|
|
||||||
opts->mode = option & S_IALLUGO;
|
|
||||||
break;
|
|
||||||
/* We might like to report bad mount options here, but
|
/* We might like to report bad mount options here, but
|
||||||
* traditionally we've ignored all mount options, so we'd
|
* traditionally we've ignored all mount options, so we'd
|
||||||
* better continue to ignore non-existing options for bpf.
|
* better continue to ignore non-existing options for bpf.
|
||||||
*/
|
*/
|
||||||
}
|
return opt == -ENOPARAM ? 0 : opt;
|
||||||
|
|
||||||
|
switch (opt) {
|
||||||
|
case OPT_MODE:
|
||||||
|
opts->mode = result.uint_32 & S_IALLUGO;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpf_fill_super(struct super_block *sb, void *data, int silent)
|
static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||||
{
|
{
|
||||||
static const struct tree_descr bpf_rfiles[] = { { "" } };
|
static const struct tree_descr bpf_rfiles[] = { { "" } };
|
||||||
struct bpf_mount_opts opts;
|
struct bpf_mount_opts *opts = fc->fs_private;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bpf_parse_options(data, &opts);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = simple_fill_super(sb, BPF_FS_MAGIC, bpf_rfiles);
|
ret = simple_fill_super(sb, BPF_FS_MAGIC, bpf_rfiles);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -644,21 +639,50 @@ static int bpf_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
inode = sb->s_root->d_inode;
|
inode = sb->s_root->d_inode;
|
||||||
inode->i_op = &bpf_dir_iops;
|
inode->i_op = &bpf_dir_iops;
|
||||||
inode->i_mode &= ~S_IALLUGO;
|
inode->i_mode &= ~S_IALLUGO;
|
||||||
inode->i_mode |= S_ISVTX | opts.mode;
|
inode->i_mode |= S_ISVTX | opts->mode;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dentry *bpf_mount(struct file_system_type *type, int flags,
|
static int bpf_get_tree(struct fs_context *fc)
|
||||||
const char *dev_name, void *data)
|
|
||||||
{
|
{
|
||||||
return mount_nodev(type, flags, data, bpf_fill_super);
|
return get_tree_nodev(fc, bpf_fill_super);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bpf_free_fc(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
kfree(fc->fs_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct fs_context_operations bpf_context_ops = {
|
||||||
|
.free = bpf_free_fc,
|
||||||
|
.parse_param = bpf_parse_param,
|
||||||
|
.get_tree = bpf_get_tree,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the filesystem mount context.
|
||||||
|
*/
|
||||||
|
static int bpf_init_fs_context(struct fs_context *fc)
|
||||||
|
{
|
||||||
|
struct bpf_mount_opts *opts;
|
||||||
|
|
||||||
|
opts = kzalloc(sizeof(struct bpf_mount_opts), GFP_KERNEL);
|
||||||
|
if (!opts)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
opts->mode = S_IRWXUGO;
|
||||||
|
|
||||||
|
fc->fs_private = opts;
|
||||||
|
fc->ops = &bpf_context_ops;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct file_system_type bpf_fs_type = {
|
static struct file_system_type bpf_fs_type = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.name = "bpf",
|
.name = "bpf",
|
||||||
.mount = bpf_mount,
|
.init_fs_context = bpf_init_fs_context,
|
||||||
|
.parameters = &bpf_fs_parameters,
|
||||||
.kill_sb = kill_litter_super,
|
.kill_sb = kill_litter_super,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue