Merge patch series "adfs, affs, befs, hfs, hfsplus: convert to new mount api"

Eric Sandeen <sandeen@redhat.com> says:

These were all tested against images I created or obtained, using a
script to test random combinations of valid and invalid mount and
remount options, and comparing the results before and after the changes.

AFAICT, all parsing works as expected and behavior is unchanged.

* patches from https://lore.kernel.org/r/20240916172735.866916-1-sandeen@redhat.com:
  hfsplus: convert hfsplus to use the new mount api
  hfs: convert hfs to use the new mount api
  befs: convert befs to use the new mount api
  affs: convert affs to use the new mount api
  adfs: convert adfs to use the new mount api

Link: https://lore.kernel.org/r/20240916172735.866916-1-sandeen@redhat.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2024-09-18 11:44:58 +02:00
commit 5b00a0f96d
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
7 changed files with 677 additions and 751 deletions

View File

@ -6,7 +6,8 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/parser.h> #include <linux/fs_parser.h>
#include <linux/fs_context.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -115,87 +116,61 @@ static int adfs_show_options(struct seq_file *seq, struct dentry *root)
return 0; return 0;
} }
enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_ftsuffix, Opt_err}; enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_ftsuffix};
static const match_table_t tokens = { static const struct fs_parameter_spec adfs_param_spec[] = {
{Opt_uid, "uid=%u"}, fsparam_uid ("uid", Opt_uid),
{Opt_gid, "gid=%u"}, fsparam_gid ("gid", Opt_gid),
{Opt_ownmask, "ownmask=%o"}, fsparam_u32oct ("ownmask", Opt_ownmask),
{Opt_othmask, "othmask=%o"}, fsparam_u32oct ("othmask", Opt_othmask),
{Opt_ftsuffix, "ftsuffix=%u"}, fsparam_u32 ("ftsuffix", Opt_ftsuffix),
{Opt_err, NULL} {}
}; };
static int parse_options(struct super_block *sb, struct adfs_sb_info *asb, static int adfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
char *options)
{ {
char *p; struct adfs_sb_info *asb = fc->s_fs_info;
int option; struct fs_parse_result result;
int opt;
if (!options) opt = fs_parse(fc, adfs_param_spec, param, &result);
return 0; if (opt < 0)
return opt;
while ((p = strsep(&options, ",")) != NULL) { switch (opt) {
substring_t args[MAX_OPT_ARGS];
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case Opt_uid: case Opt_uid:
if (match_int(args, &option)) asb->s_uid = result.uid;
return -EINVAL;
asb->s_uid = make_kuid(current_user_ns(), option);
if (!uid_valid(asb->s_uid))
return -EINVAL;
break; break;
case Opt_gid: case Opt_gid:
if (match_int(args, &option)) asb->s_gid = result.gid;
return -EINVAL;
asb->s_gid = make_kgid(current_user_ns(), option);
if (!gid_valid(asb->s_gid))
return -EINVAL;
break; break;
case Opt_ownmask: case Opt_ownmask:
if (match_octal(args, &option)) asb->s_owner_mask = result.uint_32;
return -EINVAL;
asb->s_owner_mask = option;
break; break;
case Opt_othmask: case Opt_othmask:
if (match_octal(args, &option)) asb->s_other_mask = result.uint_32;
return -EINVAL;
asb->s_other_mask = option;
break; break;
case Opt_ftsuffix: case Opt_ftsuffix:
if (match_int(args, &option)) asb->s_ftsuffix = result.uint_32;
return -EINVAL;
asb->s_ftsuffix = option;
break; break;
default: default:
adfs_msg(sb, KERN_ERR,
"unrecognised mount option \"%s\" or missing value",
p);
return -EINVAL; return -EINVAL;
} }
}
return 0; return 0;
} }
static int adfs_remount(struct super_block *sb, int *flags, char *data) static int adfs_reconfigure(struct fs_context *fc)
{ {
struct adfs_sb_info temp_asb; struct adfs_sb_info *new_asb = fc->s_fs_info;
int ret; struct adfs_sb_info *asb = ADFS_SB(fc->root->d_sb);
sync_filesystem(sb); sync_filesystem(fc->root->d_sb);
*flags |= ADFS_SB_FLAGS; fc->sb_flags |= ADFS_SB_FLAGS;
temp_asb = *ADFS_SB(sb); /* Structure copy newly parsed options */
ret = parse_options(sb, &temp_asb, data); *asb = *new_asb;
if (ret == 0)
*ADFS_SB(sb) = temp_asb;
return ret; return 0;
} }
static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf)
@ -273,7 +248,6 @@ static const struct super_operations adfs_sops = {
.write_inode = adfs_write_inode, .write_inode = adfs_write_inode,
.put_super = adfs_put_super, .put_super = adfs_put_super,
.statfs = adfs_statfs, .statfs = adfs_statfs,
.remount_fs = adfs_remount,
.show_options = adfs_show_options, .show_options = adfs_show_options,
}; };
@ -361,34 +335,21 @@ static int adfs_validate_dr0(struct super_block *sb, struct buffer_head *bh,
return 0; return 0;
} }
static int adfs_fill_super(struct super_block *sb, void *data, int silent) static int adfs_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
struct adfs_discrecord *dr; struct adfs_discrecord *dr;
struct object_info root_obj; struct object_info root_obj;
struct adfs_sb_info *asb; struct adfs_sb_info *asb = sb->s_fs_info;
struct inode *root; struct inode *root;
int ret = -EINVAL; int ret = -EINVAL;
int silent = fc->sb_flags & SB_SILENT;
sb->s_flags |= ADFS_SB_FLAGS; sb->s_flags |= ADFS_SB_FLAGS;
asb = kzalloc(sizeof(*asb), GFP_KERNEL);
if (!asb)
return -ENOMEM;
sb->s_fs_info = asb; sb->s_fs_info = asb;
sb->s_magic = ADFS_SUPER_MAGIC; sb->s_magic = ADFS_SUPER_MAGIC;
sb->s_time_gran = 10000000; sb->s_time_gran = 10000000;
/* set default options */
asb->s_uid = GLOBAL_ROOT_UID;
asb->s_gid = GLOBAL_ROOT_GID;
asb->s_owner_mask = ADFS_DEFAULT_OWNER_MASK;
asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK;
asb->s_ftsuffix = 0;
if (parse_options(sb, asb, data))
goto error;
/* Try to probe the filesystem boot block */ /* Try to probe the filesystem boot block */
ret = adfs_probe(sb, ADFS_DISCRECORD, 1, adfs_validate_bblk); ret = adfs_probe(sb, ADFS_DISCRECORD, 1, adfs_validate_bblk);
if (ret == -EILSEQ) if (ret == -EILSEQ)
@ -453,18 +414,61 @@ error:
return ret; return ret;
} }
static struct dentry *adfs_mount(struct file_system_type *fs_type, static int adfs_get_tree(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
return mount_bdev(fs_type, flags, dev_name, data, adfs_fill_super); return get_tree_bdev(fc, adfs_fill_super);
}
static void adfs_free_fc(struct fs_context *fc)
{
struct adfs_context *asb = fc->s_fs_info;
kfree(asb);
}
static const struct fs_context_operations adfs_context_ops = {
.parse_param = adfs_parse_param,
.get_tree = adfs_get_tree,
.reconfigure = adfs_reconfigure,
.free = adfs_free_fc,
};
static int adfs_init_fs_context(struct fs_context *fc)
{
struct adfs_sb_info *asb;
asb = kzalloc(sizeof(struct adfs_sb_info), GFP_KERNEL);
if (!asb)
return -ENOMEM;
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
struct super_block *sb = fc->root->d_sb;
struct adfs_sb_info *old_asb = ADFS_SB(sb);
/* structure copy existing options before parsing */
*asb = *old_asb;
} else {
/* set default options */
asb->s_uid = GLOBAL_ROOT_UID;
asb->s_gid = GLOBAL_ROOT_GID;
asb->s_owner_mask = ADFS_DEFAULT_OWNER_MASK;
asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK;
asb->s_ftsuffix = 0;
}
fc->ops = &adfs_context_ops;
fc->s_fs_info = asb;
return 0;
} }
static struct file_system_type adfs_fs_type = { static struct file_system_type adfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "adfs", .name = "adfs",
.mount = adfs_mount,
.kill_sb = kill_block_super, .kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV, .fs_flags = FS_REQUIRES_DEV,
.init_fs_context = adfs_init_fs_context,
.parameters = adfs_param_spec,
}; };
MODULE_ALIAS_FS("adfs"); MODULE_ALIAS_FS("adfs");

View File

@ -14,7 +14,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/statfs.h> #include <linux/statfs.h>
#include <linux/parser.h> #include <linux/fs_parser.h>
#include <linux/fs_context.h>
#include <linux/magic.h> #include <linux/magic.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/cred.h> #include <linux/cred.h>
@ -27,7 +28,6 @@
static int affs_statfs(struct dentry *dentry, struct kstatfs *buf); static int affs_statfs(struct dentry *dentry, struct kstatfs *buf);
static int affs_show_options(struct seq_file *m, struct dentry *root); static int affs_show_options(struct seq_file *m, struct dentry *root);
static int affs_remount (struct super_block *sb, int *flags, char *data);
static void static void
affs_commit_super(struct super_block *sb, int wait) affs_commit_super(struct super_block *sb, int wait)
@ -155,141 +155,115 @@ static const struct super_operations affs_sops = {
.put_super = affs_put_super, .put_super = affs_put_super,
.sync_fs = affs_sync_fs, .sync_fs = affs_sync_fs,
.statfs = affs_statfs, .statfs = affs_statfs,
.remount_fs = affs_remount,
.show_options = affs_show_options, .show_options = affs_show_options,
}; };
enum { enum {
Opt_bs, Opt_mode, Opt_mufs, Opt_notruncate, Opt_prefix, Opt_protect, Opt_bs, Opt_mode, Opt_mufs, Opt_notruncate, Opt_prefix, Opt_protect,
Opt_reserved, Opt_root, Opt_setgid, Opt_setuid, Opt_reserved, Opt_root, Opt_setgid, Opt_setuid,
Opt_verbose, Opt_volume, Opt_ignore, Opt_err, Opt_verbose, Opt_volume, Opt_ignore,
}; };
static const match_table_t tokens = { struct affs_context {
{Opt_bs, "bs=%u"}, kuid_t uid; /* uid to override */
{Opt_mode, "mode=%o"}, kgid_t gid; /* gid to override */
{Opt_mufs, "mufs"}, unsigned int mode; /* mode to override */
{Opt_notruncate, "nofilenametruncate"}, unsigned int reserved; /* Number of reserved blocks */
{Opt_prefix, "prefix=%s"}, int root_block; /* FFS root block number */
{Opt_protect, "protect"}, int blocksize; /* Initial device blksize */
{Opt_reserved, "reserved=%u"}, char *prefix; /* Prefix for volumes and assigns */
{Opt_root, "root=%u"}, char volume[32]; /* Vol. prefix for absolute symlinks */
{Opt_setgid, "setgid=%u"}, unsigned long mount_flags; /* Options */
{Opt_setuid, "setuid=%u"},
{Opt_verbose, "verbose"},
{Opt_volume, "volume=%s"},
{Opt_ignore, "grpquota"},
{Opt_ignore, "noquota"},
{Opt_ignore, "quota"},
{Opt_ignore, "usrquota"},
{Opt_err, NULL},
}; };
static int static const struct fs_parameter_spec affs_param_spec[] = {
parse_options(char *options, kuid_t *uid, kgid_t *gid, int *mode, int *reserved, s32 *root, fsparam_u32 ("bs", Opt_bs),
int *blocksize, char **prefix, char *volume, unsigned long *mount_opts) fsparam_u32oct ("mode", Opt_mode),
fsparam_flag ("mufs", Opt_mufs),
fsparam_flag ("nofilenametruncate", Opt_notruncate),
fsparam_string ("prefix", Opt_prefix),
fsparam_flag ("protect", Opt_protect),
fsparam_u32 ("reserved", Opt_reserved),
fsparam_u32 ("root", Opt_root),
fsparam_gid ("setgid", Opt_setgid),
fsparam_uid ("setuid", Opt_setuid),
fsparam_flag ("verbose", Opt_verbose),
fsparam_string ("volume", Opt_volume),
fsparam_flag ("grpquota", Opt_ignore),
fsparam_flag ("noquota", Opt_ignore),
fsparam_flag ("quota", Opt_ignore),
fsparam_flag ("usrquota", Opt_ignore),
{},
};
static int affs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{ {
char *p; struct affs_context *ctx = fc->fs_private;
substring_t args[MAX_OPT_ARGS]; struct fs_parse_result result;
int n;
int opt;
/* Fill in defaults */ opt = fs_parse(fc, affs_param_spec, param, &result);
if (opt < 0)
return opt;
*uid = current_uid(); switch (opt) {
*gid = current_gid();
*reserved = 2;
*root = -1;
*blocksize = -1;
volume[0] = ':';
volume[1] = 0;
*mount_opts = 0;
if (!options)
return 1;
while ((p = strsep(&options, ",")) != NULL) {
int token, n, option;
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case Opt_bs: case Opt_bs:
if (match_int(&args[0], &n)) n = result.uint_32;
return 0;
if (n != 512 && n != 1024 && n != 2048 if (n != 512 && n != 1024 && n != 2048
&& n != 4096) { && n != 4096) {
pr_warn("Invalid blocksize (512, 1024, 2048, 4096 allowed)\n"); pr_warn("Invalid blocksize (512, 1024, 2048, 4096 allowed)\n");
return 0; return -EINVAL;
} }
*blocksize = n; ctx->blocksize = n;
break; break;
case Opt_mode: case Opt_mode:
if (match_octal(&args[0], &option)) ctx->mode = result.uint_32 & 0777;
return 0; affs_set_opt(ctx->mount_flags, SF_SETMODE);
*mode = option & 0777;
affs_set_opt(*mount_opts, SF_SETMODE);
break; break;
case Opt_mufs: case Opt_mufs:
affs_set_opt(*mount_opts, SF_MUFS); affs_set_opt(ctx->mount_flags, SF_MUFS);
break; break;
case Opt_notruncate: case Opt_notruncate:
affs_set_opt(*mount_opts, SF_NO_TRUNCATE); affs_set_opt(ctx->mount_flags, SF_NO_TRUNCATE);
break; break;
case Opt_prefix: case Opt_prefix:
kfree(*prefix); kfree(ctx->prefix);
*prefix = match_strdup(&args[0]); ctx->prefix = param->string;
if (!*prefix) param->string = NULL;
return 0; affs_set_opt(ctx->mount_flags, SF_PREFIX);
affs_set_opt(*mount_opts, SF_PREFIX);
break; break;
case Opt_protect: case Opt_protect:
affs_set_opt(*mount_opts, SF_IMMUTABLE); affs_set_opt(ctx->mount_flags, SF_IMMUTABLE);
break; break;
case Opt_reserved: case Opt_reserved:
if (match_int(&args[0], reserved)) ctx->reserved = result.uint_32;
return 0;
break; break;
case Opt_root: case Opt_root:
if (match_int(&args[0], root)) ctx->root_block = result.uint_32;
return 0;
break; break;
case Opt_setgid: case Opt_setgid:
if (match_int(&args[0], &option)) ctx->gid = result.gid;
return 0; affs_set_opt(ctx->mount_flags, SF_SETGID);
*gid = make_kgid(current_user_ns(), option);
if (!gid_valid(*gid))
return 0;
affs_set_opt(*mount_opts, SF_SETGID);
break; break;
case Opt_setuid: case Opt_setuid:
if (match_int(&args[0], &option)) ctx->uid = result.uid;
return 0; affs_set_opt(ctx->mount_flags, SF_SETUID);
*uid = make_kuid(current_user_ns(), option);
if (!uid_valid(*uid))
return 0;
affs_set_opt(*mount_opts, SF_SETUID);
break; break;
case Opt_verbose: case Opt_verbose:
affs_set_opt(*mount_opts, SF_VERBOSE); affs_set_opt(ctx->mount_flags, SF_VERBOSE);
break; break;
case Opt_volume: { case Opt_volume:
char *vol = match_strdup(&args[0]); strscpy(ctx->volume, param->string, 32);
if (!vol)
return 0;
strscpy(volume, vol, 32);
kfree(vol);
break; break;
}
case Opt_ignore: case Opt_ignore:
/* Silently ignore the quota options */ /* Silently ignore the quota options */
break; break;
default: default:
pr_warn("Unrecognized mount option \"%s\" or missing value\n", return -EINVAL;
p); }
return 0; return 0;
} }
}
return 1;
}
static int affs_show_options(struct seq_file *m, struct dentry *root) static int affs_show_options(struct seq_file *m, struct dentry *root)
{ {
@ -329,27 +303,22 @@ static int affs_show_options(struct seq_file *m, struct dentry *root)
* hopefully have the guts to do so. Until then: sorry for the mess. * hopefully have the guts to do so. Until then: sorry for the mess.
*/ */
static int affs_fill_super(struct super_block *sb, void *data, int silent) static int affs_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
struct affs_sb_info *sbi; struct affs_sb_info *sbi;
struct affs_context *ctx = fc->fs_private;
struct buffer_head *root_bh = NULL; struct buffer_head *root_bh = NULL;
struct buffer_head *boot_bh; struct buffer_head *boot_bh;
struct inode *root_inode = NULL; struct inode *root_inode = NULL;
s32 root_block; int silent = fc->sb_flags & SB_SILENT;
int size, blocksize; int size, blocksize;
u32 chksum; u32 chksum;
int num_bm; int num_bm;
int i, j; int i, j;
kuid_t uid;
kgid_t gid;
int reserved;
unsigned long mount_flags;
int tmp_flags; /* fix remount prototype... */ int tmp_flags; /* fix remount prototype... */
u8 sig[4]; u8 sig[4];
int ret; int ret;
pr_debug("read_super(%s)\n", data ? (const char *)data : "no options");
sb->s_magic = AFFS_SUPER_MAGIC; sb->s_magic = AFFS_SUPER_MAGIC;
sb->s_op = &affs_sops; sb->s_op = &affs_sops;
sb->s_flags |= SB_NODIRATIME; sb->s_flags |= SB_NODIRATIME;
@ -369,19 +338,16 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent)
spin_lock_init(&sbi->work_lock); spin_lock_init(&sbi->work_lock);
INIT_DELAYED_WORK(&sbi->sb_work, flush_superblock); INIT_DELAYED_WORK(&sbi->sb_work, flush_superblock);
if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block, sbi->s_flags = ctx->mount_flags;
&blocksize,&sbi->s_prefix, sbi->s_mode = ctx->mode;
sbi->s_volume, &mount_flags)) { sbi->s_uid = ctx->uid;
pr_err("Error parsing options\n"); sbi->s_gid = ctx->gid;
return -EINVAL; sbi->s_reserved = ctx->reserved;
} sbi->s_prefix = ctx->prefix;
/* N.B. after this point s_prefix must be released */ ctx->prefix = NULL;
memcpy(sbi->s_volume, ctx->volume, 32);
sbi->s_flags = mount_flags; /* N.B. after this point s_prefix must be released */
sbi->s_mode = i;
sbi->s_uid = uid;
sbi->s_gid = gid;
sbi->s_reserved= reserved;
/* Get the size of the device in 512-byte blocks. /* Get the size of the device in 512-byte blocks.
* If we later see that the partition uses bigger * If we later see that the partition uses bigger
@ -396,15 +362,16 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent)
i = bdev_logical_block_size(sb->s_bdev); i = bdev_logical_block_size(sb->s_bdev);
j = PAGE_SIZE; j = PAGE_SIZE;
blocksize = ctx->blocksize;
if (blocksize > 0) { if (blocksize > 0) {
i = j = blocksize; i = j = blocksize;
size = size / (blocksize / 512); size = size / (blocksize / 512);
} }
for (blocksize = i; blocksize <= j; blocksize <<= 1, size >>= 1) { for (blocksize = i; blocksize <= j; blocksize <<= 1, size >>= 1) {
sbi->s_root_block = root_block; sbi->s_root_block = ctx->root_block;
if (root_block < 0) if (ctx->root_block < 0)
sbi->s_root_block = (reserved + size - 1) / 2; sbi->s_root_block = (ctx->reserved + size - 1) / 2;
pr_debug("setting blocksize to %d\n", blocksize); pr_debug("setting blocksize to %d\n", blocksize);
affs_set_blocksize(sb, blocksize); affs_set_blocksize(sb, blocksize);
sbi->s_partition_size = size; sbi->s_partition_size = size;
@ -424,7 +391,7 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent)
"size=%d, reserved=%d\n", "size=%d, reserved=%d\n",
sb->s_id, sb->s_id,
sbi->s_root_block + num_bm, sbi->s_root_block + num_bm,
blocksize, size, reserved); ctx->blocksize, size, ctx->reserved);
root_bh = affs_bread(sb, sbi->s_root_block + num_bm); root_bh = affs_bread(sb, sbi->s_root_block + num_bm);
if (!root_bh) if (!root_bh)
continue; continue;
@ -447,7 +414,7 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent)
got_root: got_root:
/* Keep super block in cache */ /* Keep super block in cache */
sbi->s_root_bh = root_bh; sbi->s_root_bh = root_bh;
root_block = sbi->s_root_block; ctx->root_block = sbi->s_root_block;
/* Find out which kind of FS we have */ /* Find out which kind of FS we have */
boot_bh = sb_bread(sb, 0); boot_bh = sb_bread(sb, 0);
@ -506,7 +473,7 @@ got_root:
return -EINVAL; return -EINVAL;
} }
if (affs_test_opt(mount_flags, SF_VERBOSE)) { if (affs_test_opt(ctx->mount_flags, SF_VERBOSE)) {
u8 len = AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0]; u8 len = AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0];
pr_notice("Mounting volume \"%.*s\": Type=%.3s\\%c, Blocksize=%d\n", pr_notice("Mounting volume \"%.*s\": Type=%.3s\\%c, Blocksize=%d\n",
len > 31 ? 31 : len, len > 31 ? 31 : len,
@ -528,7 +495,7 @@ got_root:
/* set up enough so that it can read an inode */ /* set up enough so that it can read an inode */
root_inode = affs_iget(sb, root_block); root_inode = affs_iget(sb, ctx->root_block);
if (IS_ERR(root_inode)) if (IS_ERR(root_inode))
return PTR_ERR(root_inode); return PTR_ERR(root_inode);
@ -548,56 +515,43 @@ got_root:
return 0; return 0;
} }
static int static int affs_reconfigure(struct fs_context *fc)
affs_remount(struct super_block *sb, int *flags, char *data)
{ {
struct super_block *sb = fc->root->d_sb;
struct affs_context *ctx = fc->fs_private;
struct affs_sb_info *sbi = AFFS_SB(sb); struct affs_sb_info *sbi = AFFS_SB(sb);
int blocksize;
kuid_t uid;
kgid_t gid;
int mode;
int reserved;
int root_block;
unsigned long mount_flags;
int res = 0; int res = 0;
char volume[32];
char *prefix = NULL;
pr_debug("%s(flags=0x%x,opts=\"%s\")\n", __func__, *flags, data);
sync_filesystem(sb); sync_filesystem(sb);
*flags |= SB_NODIRATIME; fc->sb_flags |= SB_NODIRATIME;
memcpy(volume, sbi->s_volume, 32);
if (!parse_options(data, &uid, &gid, &mode, &reserved, &root_block,
&blocksize, &prefix, volume,
&mount_flags)) {
kfree(prefix);
return -EINVAL;
}
flush_delayed_work(&sbi->sb_work); flush_delayed_work(&sbi->sb_work);
sbi->s_flags = mount_flags; /*
sbi->s_mode = mode; * NB: Historically, only mount_flags, mode, uid, gic, prefix,
sbi->s_uid = uid; * and volume are accepted during remount.
sbi->s_gid = gid; */
sbi->s_flags = ctx->mount_flags;
sbi->s_mode = ctx->mode;
sbi->s_uid = ctx->uid;
sbi->s_gid = ctx->gid;
/* protect against readers */ /* protect against readers */
spin_lock(&sbi->symlink_lock); spin_lock(&sbi->symlink_lock);
if (prefix) { if (ctx->prefix) {
kfree(sbi->s_prefix); kfree(sbi->s_prefix);
sbi->s_prefix = prefix; sbi->s_prefix = ctx->prefix;
ctx->prefix = NULL;
} }
memcpy(sbi->s_volume, volume, 32); memcpy(sbi->s_volume, ctx->volume, 32);
spin_unlock(&sbi->symlink_lock); spin_unlock(&sbi->symlink_lock);
if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb))
return 0; return 0;
if (*flags & SB_RDONLY) if (fc->sb_flags & SB_RDONLY)
affs_free_bitmap(sb); affs_free_bitmap(sb);
else else
res = affs_init_bitmap(sb, flags); res = affs_init_bitmap(sb, &fc->sb_flags);
return res; return res;
} }
@ -624,10 +578,9 @@ affs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0; return 0;
} }
static struct dentry *affs_mount(struct file_system_type *fs_type, static int affs_get_tree(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
return mount_bdev(fs_type, flags, dev_name, data, affs_fill_super); return get_tree_bdev(fc, affs_fill_super);
} }
static void affs_kill_sb(struct super_block *sb) static void affs_kill_sb(struct super_block *sb)
@ -643,12 +596,61 @@ static void affs_kill_sb(struct super_block *sb)
} }
} }
static void affs_free_fc(struct fs_context *fc)
{
struct affs_context *ctx = fc->fs_private;
kfree(ctx->prefix);
kfree(ctx);
}
static const struct fs_context_operations affs_context_ops = {
.parse_param = affs_parse_param,
.get_tree = affs_get_tree,
.reconfigure = affs_reconfigure,
.free = affs_free_fc,
};
static int affs_init_fs_context(struct fs_context *fc)
{
struct affs_context *ctx;
ctx = kzalloc(sizeof(struct affs_context), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
struct super_block *sb = fc->root->d_sb;
struct affs_sb_info *sbi = AFFS_SB(sb);
/*
* NB: historically, no options other than volume were
* preserved across a remount unless they were explicitly
* passed in.
*/
memcpy(ctx->volume, sbi->s_volume, 32);
} else {
ctx->uid = current_uid();
ctx->gid = current_gid();
ctx->reserved = 2;
ctx->root_block = -1;
ctx->blocksize = -1;
ctx->volume[0] = ':';
}
fc->ops = &affs_context_ops;
fc->fs_private = ctx;
return 0;
}
static struct file_system_type affs_fs_type = { static struct file_system_type affs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "affs", .name = "affs",
.mount = affs_mount,
.kill_sb = affs_kill_sb, .kill_sb = affs_kill_sb,
.fs_flags = FS_REQUIRES_DEV, .fs_flags = FS_REQUIRES_DEV,
.init_fs_context = affs_init_fs_context,
.parameters = affs_param_spec,
}; };
MODULE_ALIAS_FS("affs"); MODULE_ALIAS_FS("affs");

View File

@ -11,12 +11,13 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/nls.h> #include <linux/nls.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/parser.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/cred.h> #include <linux/cred.h>
@ -54,22 +55,20 @@ static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,
static int befs_nls2utf(struct super_block *sb, const char *in, int in_len, static int befs_nls2utf(struct super_block *sb, const char *in, int in_len,
char **out, int *out_len); char **out, int *out_len);
static void befs_put_super(struct super_block *); static void befs_put_super(struct super_block *);
static int befs_remount(struct super_block *, int *, char *);
static int befs_statfs(struct dentry *, struct kstatfs *); static int befs_statfs(struct dentry *, struct kstatfs *);
static int befs_show_options(struct seq_file *, struct dentry *); static int befs_show_options(struct seq_file *, struct dentry *);
static int parse_options(char *, struct befs_mount_options *);
static struct dentry *befs_fh_to_dentry(struct super_block *sb, static struct dentry *befs_fh_to_dentry(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type); struct fid *fid, int fh_len, int fh_type);
static struct dentry *befs_fh_to_parent(struct super_block *sb, static struct dentry *befs_fh_to_parent(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type); struct fid *fid, int fh_len, int fh_type);
static struct dentry *befs_get_parent(struct dentry *child); static struct dentry *befs_get_parent(struct dentry *child);
static void befs_free_fc(struct fs_context *fc);
static const struct super_operations befs_sops = { static const struct super_operations befs_sops = {
.alloc_inode = befs_alloc_inode, /* allocate a new inode */ .alloc_inode = befs_alloc_inode, /* allocate a new inode */
.free_inode = befs_free_inode, /* deallocate an inode */ .free_inode = befs_free_inode, /* deallocate an inode */
.put_super = befs_put_super, /* uninit super */ .put_super = befs_put_super, /* uninit super */
.statfs = befs_statfs, /* statfs */ .statfs = befs_statfs, /* statfs */
.remount_fs = befs_remount,
.show_options = befs_show_options, .show_options = befs_show_options,
}; };
@ -672,93 +671,54 @@ static struct dentry *befs_get_parent(struct dentry *child)
} }
enum { enum {
Opt_uid, Opt_gid, Opt_charset, Opt_debug, Opt_err, Opt_uid, Opt_gid, Opt_charset, Opt_debug,
}; };
static const match_table_t befs_tokens = { static const struct fs_parameter_spec befs_param_spec[] = {
{Opt_uid, "uid=%d"}, fsparam_uid ("uid", Opt_uid),
{Opt_gid, "gid=%d"}, fsparam_gid ("gid", Opt_gid),
{Opt_charset, "iocharset=%s"}, fsparam_string ("iocharset", Opt_charset),
{Opt_debug, "debug"}, fsparam_flag ("debug", Opt_debug),
{Opt_err, NULL} {}
}; };
static int static int
parse_options(char *options, struct befs_mount_options *opts) befs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{ {
char *p; struct befs_mount_options *opts = fc->fs_private;
substring_t args[MAX_OPT_ARGS];
int option;
kuid_t uid;
kgid_t gid;
/* Initialize options */
opts->uid = GLOBAL_ROOT_UID;
opts->gid = GLOBAL_ROOT_GID;
opts->use_uid = 0;
opts->use_gid = 0;
opts->iocharset = NULL;
opts->debug = 0;
if (!options)
return 1;
while ((p = strsep(&options, ",")) != NULL) {
int token; int token;
struct fs_parse_result result;
if (!*p) /* befs ignores all options on remount */
continue; if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
return 0;
token = fs_parse(fc, befs_param_spec, param, &result);
if (token < 0)
return token;
token = match_token(p, befs_tokens, args);
switch (token) { switch (token) {
case Opt_uid: case Opt_uid:
if (match_int(&args[0], &option)) opts->uid = result.uid;
return 0;
uid = INVALID_UID;
if (option >= 0)
uid = make_kuid(current_user_ns(), option);
if (!uid_valid(uid)) {
pr_err("Invalid uid %d, "
"using default\n", option);
break;
}
opts->uid = uid;
opts->use_uid = 1; opts->use_uid = 1;
break; break;
case Opt_gid: case Opt_gid:
if (match_int(&args[0], &option)) opts->gid = result.gid;
return 0;
gid = INVALID_GID;
if (option >= 0)
gid = make_kgid(current_user_ns(), option);
if (!gid_valid(gid)) {
pr_err("Invalid gid %d, "
"using default\n", option);
break;
}
opts->gid = gid;
opts->use_gid = 1; opts->use_gid = 1;
break; break;
case Opt_charset: case Opt_charset:
kfree(opts->iocharset); kfree(opts->iocharset);
opts->iocharset = match_strdup(&args[0]); opts->iocharset = param->string;
if (!opts->iocharset) { param->string = NULL;
pr_err("allocation failure for "
"iocharset string\n");
return 0;
}
break; break;
case Opt_debug: case Opt_debug:
opts->debug = 1; opts->debug = 1;
break; break;
default: default:
pr_err("Unrecognized mount option \"%s\" " return -EINVAL;
"or missing value\n", p); }
return 0; return 0;
} }
}
return 1;
}
static int befs_show_options(struct seq_file *m, struct dentry *root) static int befs_show_options(struct seq_file *m, struct dentry *root)
{ {
@ -793,6 +753,21 @@ befs_put_super(struct super_block *sb)
sb->s_fs_info = NULL; sb->s_fs_info = NULL;
} }
/*
* Copy the parsed options into the sbi mount_options member
*/
static void
befs_set_options(struct befs_sb_info *sbi, struct befs_mount_options *opts)
{
sbi->mount_opts.uid = opts->uid;
sbi->mount_opts.gid = opts->gid;
sbi->mount_opts.use_uid = opts->use_uid;
sbi->mount_opts.use_gid = opts->use_gid;
sbi->mount_opts.debug = opts->debug;
sbi->mount_opts.iocharset = opts->iocharset;
opts->iocharset = NULL;
}
/* Allocate private field of the superblock, fill it. /* Allocate private field of the superblock, fill it.
* *
* Finish filling the public superblock fields * Finish filling the public superblock fields
@ -800,7 +775,7 @@ befs_put_super(struct super_block *sb)
* Load a set of NLS translations if needed. * Load a set of NLS translations if needed.
*/ */
static int static int
befs_fill_super(struct super_block *sb, void *data, int silent) befs_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
struct buffer_head *bh; struct buffer_head *bh;
struct befs_sb_info *befs_sb; struct befs_sb_info *befs_sb;
@ -810,6 +785,8 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
const unsigned long sb_block = 0; const unsigned long sb_block = 0;
const off_t x86_sb_off = 512; const off_t x86_sb_off = 512;
int blocksize; int blocksize;
struct befs_mount_options *parsed_opts = fc->fs_private;
int silent = fc->sb_flags & SB_SILENT;
sb->s_fs_info = kzalloc(sizeof(*befs_sb), GFP_KERNEL); sb->s_fs_info = kzalloc(sizeof(*befs_sb), GFP_KERNEL);
if (sb->s_fs_info == NULL) if (sb->s_fs_info == NULL)
@ -817,11 +794,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
befs_sb = BEFS_SB(sb); befs_sb = BEFS_SB(sb);
if (!parse_options((char *) data, &befs_sb->mount_opts)) { befs_set_options(befs_sb, parsed_opts);
if (!silent)
befs_error(sb, "cannot parse mount options");
goto unacquire_priv_sbp;
}
befs_debug(sb, "---> %s", __func__); befs_debug(sb, "---> %s", __func__);
@ -934,10 +907,10 @@ unacquire_none:
} }
static int static int
befs_remount(struct super_block *sb, int *flags, char *data) befs_reconfigure(struct fs_context *fc)
{ {
sync_filesystem(sb); sync_filesystem(fc->root->d_sb);
if (!(*flags & SB_RDONLY)) if (!(fc->sb_flags & SB_RDONLY))
return -EINVAL; return -EINVAL;
return 0; return 0;
} }
@ -965,19 +938,51 @@ befs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0; return 0;
} }
static struct dentry * static int befs_get_tree(struct fs_context *fc)
befs_mount(struct file_system_type *fs_type, int flags, const char *dev_name,
void *data)
{ {
return mount_bdev(fs_type, flags, dev_name, data, befs_fill_super); return get_tree_bdev(fc, befs_fill_super);
}
static const struct fs_context_operations befs_context_ops = {
.parse_param = befs_parse_param,
.get_tree = befs_get_tree,
.reconfigure = befs_reconfigure,
.free = befs_free_fc,
};
static int befs_init_fs_context(struct fs_context *fc)
{
struct befs_mount_options *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return -ENOMEM;
/* Initialize options */
opts->uid = GLOBAL_ROOT_UID;
opts->gid = GLOBAL_ROOT_GID;
fc->fs_private = opts;
fc->ops = &befs_context_ops;
return 0;
}
static void befs_free_fc(struct fs_context *fc)
{
struct befs_mount_options *opts = fc->fs_private;
kfree(opts->iocharset);
kfree(fc->fs_private);
} }
static struct file_system_type befs_fs_type = { static struct file_system_type befs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "befs", .name = "befs",
.mount = befs_mount,
.kill_sb = kill_block_super, .kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV, .fs_flags = FS_REQUIRES_DEV,
.init_fs_context = befs_init_fs_context,
.parameters = befs_param_spec,
}; };
MODULE_ALIAS_FS("befs"); MODULE_ALIAS_FS("befs");

View File

@ -15,10 +15,11 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/nls.h> #include <linux/nls.h>
#include <linux/parser.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vfs.h> #include <linux/vfs.h>
@ -111,21 +112,24 @@ static int hfs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0; return 0;
} }
static int hfs_remount(struct super_block *sb, int *flags, char *data) static int hfs_reconfigure(struct fs_context *fc)
{ {
struct super_block *sb = fc->root->d_sb;
sync_filesystem(sb); sync_filesystem(sb);
*flags |= SB_NODIRATIME; fc->sb_flags |= SB_NODIRATIME;
if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb))
return 0; return 0;
if (!(*flags & SB_RDONLY)) {
if (!(fc->sb_flags & SB_RDONLY)) {
if (!(HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) { if (!(HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_UNMNT))) {
pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. leaving read-only.\n"); pr_warn("filesystem was not cleanly unmounted, running fsck.hfs is recommended. leaving read-only.\n");
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
*flags |= SB_RDONLY; fc->sb_flags |= SB_RDONLY;
} else if (HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_SLOCK)) { } else if (HFS_SB(sb)->mdb->drAtrb & cpu_to_be16(HFS_SB_ATTRIB_SLOCK)) {
pr_warn("filesystem is marked locked, leaving read-only.\n"); pr_warn("filesystem is marked locked, leaving read-only.\n");
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
*flags |= SB_RDONLY; fc->sb_flags |= SB_RDONLY;
} }
} }
return 0; return 0;
@ -180,7 +184,6 @@ static const struct super_operations hfs_super_operations = {
.put_super = hfs_put_super, .put_super = hfs_put_super,
.sync_fs = hfs_sync_fs, .sync_fs = hfs_sync_fs,
.statfs = hfs_statfs, .statfs = hfs_statfs,
.remount_fs = hfs_remount,
.show_options = hfs_show_options, .show_options = hfs_show_options,
}; };
@ -188,131 +191,79 @@ enum {
opt_uid, opt_gid, opt_umask, opt_file_umask, opt_dir_umask, opt_uid, opt_gid, opt_umask, opt_file_umask, opt_dir_umask,
opt_part, opt_session, opt_type, opt_creator, opt_quiet, opt_part, opt_session, opt_type, opt_creator, opt_quiet,
opt_codepage, opt_iocharset, opt_codepage, opt_iocharset,
opt_err
}; };
static const match_table_t tokens = { static const struct fs_parameter_spec hfs_param_spec[] = {
{ opt_uid, "uid=%u" }, fsparam_u32 ("uid", opt_uid),
{ opt_gid, "gid=%u" }, fsparam_u32 ("gid", opt_gid),
{ opt_umask, "umask=%o" }, fsparam_u32oct ("umask", opt_umask),
{ opt_file_umask, "file_umask=%o" }, fsparam_u32oct ("file_umask", opt_file_umask),
{ opt_dir_umask, "dir_umask=%o" }, fsparam_u32oct ("dir_umask", opt_dir_umask),
{ opt_part, "part=%u" }, fsparam_u32 ("part", opt_part),
{ opt_session, "session=%u" }, fsparam_u32 ("session", opt_session),
{ opt_type, "type=%s" }, fsparam_string ("type", opt_type),
{ opt_creator, "creator=%s" }, fsparam_string ("creator", opt_creator),
{ opt_quiet, "quiet" }, fsparam_flag ("quiet", opt_quiet),
{ opt_codepage, "codepage=%s" }, fsparam_string ("codepage", opt_codepage),
{ opt_iocharset, "iocharset=%s" }, fsparam_string ("iocharset", opt_iocharset),
{ opt_err, NULL } {}
}; };
static inline int match_fourchar(substring_t *arg, u32 *result)
{
if (arg->to - arg->from != 4)
return -EINVAL;
memcpy(result, arg->from, 4);
return 0;
}
/* /*
* parse_options() * hfs_parse_param()
* *
* adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger * This function is called by the vfs to parse the mount options.
* This function is called by hfs_read_super() to parse the mount options.
*/ */
static int parse_options(char *options, struct hfs_sb_info *hsb) static int hfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{ {
char *p; struct hfs_sb_info *hsb = fc->s_fs_info;
substring_t args[MAX_OPT_ARGS]; struct fs_parse_result result;
int tmp, token; int opt;
/* initialize the sb with defaults */ /* hfs does not honor any fs-specific options on remount */
hsb->s_uid = current_uid(); if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
hsb->s_gid = current_gid(); return 0;
hsb->s_file_umask = 0133;
hsb->s_dir_umask = 0022;
hsb->s_type = hsb->s_creator = cpu_to_be32(0x3f3f3f3f); /* == '????' */
hsb->s_quiet = 0;
hsb->part = -1;
hsb->session = -1;
if (!options) opt = fs_parse(fc, hfs_param_spec, param, &result);
return 1; if (opt < 0)
return opt;
while ((p = strsep(&options, ",")) != NULL) { switch (opt) {
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case opt_uid: case opt_uid:
if (match_int(&args[0], &tmp)) { hsb->s_uid = result.uid;
pr_err("uid requires an argument\n");
return 0;
}
hsb->s_uid = make_kuid(current_user_ns(), (uid_t)tmp);
if (!uid_valid(hsb->s_uid)) {
pr_err("invalid uid %d\n", tmp);
return 0;
}
break; break;
case opt_gid: case opt_gid:
if (match_int(&args[0], &tmp)) { hsb->s_gid = result.gid;
pr_err("gid requires an argument\n");
return 0;
}
hsb->s_gid = make_kgid(current_user_ns(), (gid_t)tmp);
if (!gid_valid(hsb->s_gid)) {
pr_err("invalid gid %d\n", tmp);
return 0;
}
break; break;
case opt_umask: case opt_umask:
if (match_octal(&args[0], &tmp)) { hsb->s_file_umask = (umode_t)result.uint_32;
pr_err("umask requires a value\n"); hsb->s_dir_umask = (umode_t)result.uint_32;
return 0;
}
hsb->s_file_umask = (umode_t)tmp;
hsb->s_dir_umask = (umode_t)tmp;
break; break;
case opt_file_umask: case opt_file_umask:
if (match_octal(&args[0], &tmp)) { hsb->s_file_umask = (umode_t)result.uint_32;
pr_err("file_umask requires a value\n");
return 0;
}
hsb->s_file_umask = (umode_t)tmp;
break; break;
case opt_dir_umask: case opt_dir_umask:
if (match_octal(&args[0], &tmp)) { hsb->s_dir_umask = (umode_t)result.uint_32;
pr_err("dir_umask requires a value\n");
return 0;
}
hsb->s_dir_umask = (umode_t)tmp;
break; break;
case opt_part: case opt_part:
if (match_int(&args[0], &hsb->part)) { hsb->part = result.uint_32;
pr_err("part requires an argument\n");
return 0;
}
break; break;
case opt_session: case opt_session:
if (match_int(&args[0], &hsb->session)) { hsb->session = result.uint_32;
pr_err("session requires an argument\n");
return 0;
}
break; break;
case opt_type: case opt_type:
if (match_fourchar(&args[0], &hsb->s_type)) { if (strlen(param->string) != 4) {
pr_err("type requires a 4 character value\n"); pr_err("type requires a 4 character value\n");
return 0; return -EINVAL;
} }
memcpy(&hsb->s_type, param->string, 4);
break; break;
case opt_creator: case opt_creator:
if (match_fourchar(&args[0], &hsb->s_creator)) { if (strlen(param->string) != 4) {
pr_err("creator requires a 4 character value\n"); pr_err("creator requires a 4 character value\n");
return 0; return -EINVAL;
} }
memcpy(&hsb->s_creator, param->string, 4);
break; break;
case opt_quiet: case opt_quiet:
hsb->s_quiet = 1; hsb->s_quiet = 1;
@ -320,50 +271,33 @@ static int parse_options(char *options, struct hfs_sb_info *hsb)
case opt_codepage: case opt_codepage:
if (hsb->nls_disk) { if (hsb->nls_disk) {
pr_err("unable to change codepage\n"); pr_err("unable to change codepage\n");
return 0; return -EINVAL;
} }
p = match_strdup(&args[0]); hsb->nls_disk = load_nls(param->string);
if (p)
hsb->nls_disk = load_nls(p);
if (!hsb->nls_disk) { if (!hsb->nls_disk) {
pr_err("unable to load codepage \"%s\"\n", p); pr_err("unable to load codepage \"%s\"\n",
kfree(p); param->string);
return 0; return -EINVAL;
} }
kfree(p);
break; break;
case opt_iocharset: case opt_iocharset:
if (hsb->nls_io) { if (hsb->nls_io) {
pr_err("unable to change iocharset\n"); pr_err("unable to change iocharset\n");
return 0; return -EINVAL;
} }
p = match_strdup(&args[0]); hsb->nls_io = load_nls(param->string);
if (p)
hsb->nls_io = load_nls(p);
if (!hsb->nls_io) { if (!hsb->nls_io) {
pr_err("unable to load iocharset \"%s\"\n", p); pr_err("unable to load iocharset \"%s\"\n",
kfree(p); param->string);
return 0; return -EINVAL;
} }
kfree(p);
break; break;
default: default:
return 0; return -EINVAL;
}
} }
if (hsb->nls_disk && !hsb->nls_io) {
hsb->nls_io = load_nls_default();
if (!hsb->nls_io) {
pr_err("unable to load default iocharset\n");
return 0; return 0;
} }
}
hsb->s_dir_umask &= 0777;
hsb->s_file_umask &= 0577;
return 1;
}
/* /*
* hfs_read_super() * hfs_read_super()
@ -376,29 +310,25 @@ static int parse_options(char *options, struct hfs_sb_info *hsb)
* hfs_btree_init() to get the necessary data about the extents and * hfs_btree_init() to get the necessary data about the extents and
* catalog B-trees and, finally, reading the root inode into memory. * catalog B-trees and, finally, reading the root inode into memory.
*/ */
static int hfs_fill_super(struct super_block *sb, void *data, int silent) static int hfs_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
struct hfs_sb_info *sbi; struct hfs_sb_info *sbi = HFS_SB(sb);
struct hfs_find_data fd; struct hfs_find_data fd;
hfs_cat_rec rec; hfs_cat_rec rec;
struct inode *root_inode; struct inode *root_inode;
int silent = fc->sb_flags & SB_SILENT;
int res; int res;
sbi = kzalloc(sizeof(struct hfs_sb_info), GFP_KERNEL); /* load_nls_default does not fail */
if (!sbi) if (sbi->nls_disk && !sbi->nls_io)
return -ENOMEM; sbi->nls_io = load_nls_default();
sbi->s_dir_umask &= 0777;
sbi->s_file_umask &= 0577;
sbi->sb = sb;
sb->s_fs_info = sbi;
spin_lock_init(&sbi->work_lock); spin_lock_init(&sbi->work_lock);
INIT_DELAYED_WORK(&sbi->mdb_work, flush_mdb); INIT_DELAYED_WORK(&sbi->mdb_work, flush_mdb);
res = -EINVAL; sbi->sb = sb;
if (!parse_options((char *)data, sbi)) {
pr_err("unable to parse mount options\n");
goto bail;
}
sb->s_op = &hfs_super_operations; sb->s_op = &hfs_super_operations;
sb->s_xattr = hfs_xattr_handlers; sb->s_xattr = hfs_xattr_handlers;
sb->s_flags |= SB_NODIRATIME; sb->s_flags |= SB_NODIRATIME;
@ -451,18 +381,56 @@ bail:
return res; return res;
} }
static struct dentry *hfs_mount(struct file_system_type *fs_type, static int hfs_get_tree(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
return mount_bdev(fs_type, flags, dev_name, data, hfs_fill_super); return get_tree_bdev(fc, hfs_fill_super);
}
static void hfs_free_fc(struct fs_context *fc)
{
kfree(fc->s_fs_info);
}
static const struct fs_context_operations hfs_context_ops = {
.parse_param = hfs_parse_param,
.get_tree = hfs_get_tree,
.reconfigure = hfs_reconfigure,
.free = hfs_free_fc,
};
static int hfs_init_fs_context(struct fs_context *fc)
{
struct hfs_sb_info *hsb;
hsb = kzalloc(sizeof(struct hfs_sb_info), GFP_KERNEL);
if (!hsb)
return -ENOMEM;
fc->s_fs_info = hsb;
fc->ops = &hfs_context_ops;
if (fc->purpose != FS_CONTEXT_FOR_RECONFIGURE) {
/* initialize options with defaults */
hsb->s_uid = current_uid();
hsb->s_gid = current_gid();
hsb->s_file_umask = 0133;
hsb->s_dir_umask = 0022;
hsb->s_type = cpu_to_be32(0x3f3f3f3f); /* == '????' */
hsb->s_creator = cpu_to_be32(0x3f3f3f3f); /* == '????' */
hsb->s_quiet = 0;
hsb->part = -1;
hsb->session = -1;
}
return 0;
} }
static struct file_system_type hfs_fs_type = { static struct file_system_type hfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "hfs", .name = "hfs",
.mount = hfs_mount,
.kill_sb = kill_block_super, .kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV, .fs_flags = FS_REQUIRES_DEV,
.init_fs_context = hfs_init_fs_context,
}; };
MODULE_ALIAS_FS("hfs"); MODULE_ALIAS_FS("hfs");

View File

@ -21,6 +21,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/fs_context.h>
#include "hfsplus_raw.h" #include "hfsplus_raw.h"
#define DBG_BNODE_REFS 0x00000001 #define DBG_BNODE_REFS 0x00000001
@ -496,8 +497,7 @@ long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
/* options.c */ /* options.c */
void hfsplus_fill_defaults(struct hfsplus_sb_info *opts); void hfsplus_fill_defaults(struct hfsplus_sb_info *opts);
int hfsplus_parse_options_remount(char *input, int *force); int hfsplus_parse_param(struct fs_context *fc, struct fs_parameter *param);
int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi);
int hfsplus_show_options(struct seq_file *seq, struct dentry *root); int hfsplus_show_options(struct seq_file *seq, struct dentry *root);
/* part_tbl.c */ /* part_tbl.c */

View File

@ -12,7 +12,8 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/parser.h> #include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/nls.h> #include <linux/nls.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
@ -23,26 +24,23 @@ enum {
opt_creator, opt_type, opt_creator, opt_type,
opt_umask, opt_uid, opt_gid, opt_umask, opt_uid, opt_gid,
opt_part, opt_session, opt_nls, opt_part, opt_session, opt_nls,
opt_nodecompose, opt_decompose, opt_decompose, opt_barrier,
opt_barrier, opt_nobarrier, opt_force,
opt_force, opt_err
}; };
static const match_table_t tokens = { static const struct fs_parameter_spec hfs_param_spec[] = {
{ opt_creator, "creator=%s" }, fsparam_string ("creator", opt_creator),
{ opt_type, "type=%s" }, fsparam_string ("type", opt_type),
{ opt_umask, "umask=%o" }, fsparam_u32oct ("umask", opt_umask),
{ opt_uid, "uid=%u" }, fsparam_u32 ("uid", opt_uid),
{ opt_gid, "gid=%u" }, fsparam_u32 ("gid", opt_gid),
{ opt_part, "part=%u" }, fsparam_u32 ("part", opt_part),
{ opt_session, "session=%u" }, fsparam_u32 ("session", opt_session),
{ opt_nls, "nls=%s" }, fsparam_string ("nls", opt_nls),
{ opt_decompose, "decompose" }, fsparam_flag_no ("decompose", opt_decompose),
{ opt_nodecompose, "nodecompose" }, fsparam_flag_no ("barrier", opt_barrier),
{ opt_barrier, "barrier" }, fsparam_flag ("force", opt_force),
{ opt_nobarrier, "nobarrier" }, {}
{ opt_force, "force" },
{ opt_err, NULL }
}; };
/* Initialize an options object to reasonable defaults */ /* Initialize an options object to reasonable defaults */
@ -60,164 +58,91 @@ void hfsplus_fill_defaults(struct hfsplus_sb_info *opts)
opts->session = -1; opts->session = -1;
} }
/* convert a "four byte character" to a 32 bit int with error checks */ /* Parse options from mount. Returns nonzero errno on failure */
static inline int match_fourchar(substring_t *arg, u32 *result) int hfsplus_parse_param(struct fs_context *fc, struct fs_parameter *param)
{ {
if (arg->to - arg->from != 4) struct hfsplus_sb_info *sbi = fc->s_fs_info;
return -EINVAL; struct fs_parse_result result;
memcpy(result, arg->from, 4); int opt;
/*
* Only the force option is examined during remount, all others
* are ignored.
*/
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE &&
strncmp(param->key, "force", 5))
return 0; return 0;
}
int hfsplus_parse_options_remount(char *input, int *force) opt = fs_parse(fc, hfs_param_spec, param, &result);
{ if (opt < 0)
char *p; return opt;
substring_t args[MAX_OPT_ARGS];
int token;
if (!input) switch (opt) {
return 1;
while ((p = strsep(&input, ",")) != NULL) {
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case opt_force:
*force = 1;
break;
default:
break;
}
}
return 1;
}
/* Parse options from mount. Returns 0 on failure */
/* input is the options passed to mount() as a string */
int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int tmp, token;
if (!input)
goto done;
while ((p = strsep(&input, ",")) != NULL) {
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case opt_creator: case opt_creator:
if (match_fourchar(&args[0], &sbi->creator)) { if (strlen(param->string) != 4) {
pr_err("creator requires a 4 character value\n"); pr_err("creator requires a 4 character value\n");
return 0; return -EINVAL;
} }
memcpy(&sbi->creator, param->string, 4);
break; break;
case opt_type: case opt_type:
if (match_fourchar(&args[0], &sbi->type)) { if (strlen(param->string) != 4) {
pr_err("type requires a 4 character value\n"); pr_err("type requires a 4 character value\n");
return 0; return -EINVAL;
} }
memcpy(&sbi->type, param->string, 4);
break; break;
case opt_umask: case opt_umask:
if (match_octal(&args[0], &tmp)) { sbi->umask = (umode_t)result.uint_32;
pr_err("umask requires a value\n");
return 0;
}
sbi->umask = (umode_t)tmp;
break; break;
case opt_uid: case opt_uid:
if (match_int(&args[0], &tmp)) { sbi->uid = result.uid;
pr_err("uid requires an argument\n");
return 0;
}
sbi->uid = make_kuid(current_user_ns(), (uid_t)tmp);
if (!uid_valid(sbi->uid)) {
pr_err("invalid uid specified\n");
return 0;
} else {
set_bit(HFSPLUS_SB_UID, &sbi->flags); set_bit(HFSPLUS_SB_UID, &sbi->flags);
}
break; break;
case opt_gid: case opt_gid:
if (match_int(&args[0], &tmp)) { sbi->gid = result.gid;
pr_err("gid requires an argument\n");
return 0;
}
sbi->gid = make_kgid(current_user_ns(), (gid_t)tmp);
if (!gid_valid(sbi->gid)) {
pr_err("invalid gid specified\n");
return 0;
} else {
set_bit(HFSPLUS_SB_GID, &sbi->flags); set_bit(HFSPLUS_SB_GID, &sbi->flags);
}
break; break;
case opt_part: case opt_part:
if (match_int(&args[0], &sbi->part)) { sbi->part = result.uint_32;
pr_err("part requires an argument\n");
return 0;
}
break; break;
case opt_session: case opt_session:
if (match_int(&args[0], &sbi->session)) { sbi->session = result.uint_32;
pr_err("session requires an argument\n");
return 0;
}
break; break;
case opt_nls: case opt_nls:
if (sbi->nls) { if (sbi->nls) {
pr_err("unable to change nls mapping\n"); pr_err("unable to change nls mapping\n");
return 0; return -EINVAL;
} }
p = match_strdup(&args[0]); sbi->nls = load_nls(param->string);
if (p)
sbi->nls = load_nls(p);
if (!sbi->nls) { if (!sbi->nls) {
pr_err("unable to load nls mapping \"%s\"\n", pr_err("unable to load nls mapping \"%s\"\n",
p); param->string);
kfree(p); return -EINVAL;
return 0;
} }
kfree(p);
break; break;
case opt_decompose: case opt_decompose:
if (result.negated)
set_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
else
clear_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags); clear_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
break; break;
case opt_nodecompose:
set_bit(HFSPLUS_SB_NODECOMPOSE, &sbi->flags);
break;
case opt_barrier: case opt_barrier:
clear_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags); if (result.negated)
break;
case opt_nobarrier:
set_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags); set_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags);
else
clear_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags);
break; break;
case opt_force: case opt_force:
set_bit(HFSPLUS_SB_FORCE, &sbi->flags); set_bit(HFSPLUS_SB_FORCE, &sbi->flags);
break; break;
default: default:
return 0; return -EINVAL;
}
} }
done:
if (!sbi->nls) {
/* try utf8 first, as this is the old default behaviour */
sbi->nls = load_nls("utf8");
if (!sbi->nls)
sbi->nls = load_nls_default();
if (!sbi->nls)
return 0; return 0;
} }
return 1;
}
int hfsplus_show_options(struct seq_file *seq, struct dentry *root) int hfsplus_show_options(struct seq_file *seq, struct dentry *root)
{ {
struct hfsplus_sb_info *sbi = HFSPLUS_SB(root->d_sb); struct hfsplus_sb_info *sbi = HFSPLUS_SB(root->d_sb);

View File

@ -14,6 +14,7 @@
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/nls.h> #include <linux/nls.h>
@ -332,34 +333,33 @@ static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0; return 0;
} }
static int hfsplus_remount(struct super_block *sb, int *flags, char *data) static int hfsplus_reconfigure(struct fs_context *fc)
{ {
sync_filesystem(sb); struct super_block *sb = fc->root->d_sb;
if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
return 0;
if (!(*flags & SB_RDONLY)) {
struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr;
int force = 0;
if (!hfsplus_parse_options_remount(data, &force)) sync_filesystem(sb);
return -EINVAL; if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb))
return 0;
if (!(fc->sb_flags & SB_RDONLY)) {
struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
struct hfsplus_vh *vhdr = sbi->s_vhdr;
if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
pr_warn("filesystem was not cleanly unmounted, running fsck.hfsplus is recommended. leaving read-only.\n"); pr_warn("filesystem was not cleanly unmounted, running fsck.hfsplus is recommended. leaving read-only.\n");
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
*flags |= SB_RDONLY; fc->sb_flags |= SB_RDONLY;
} else if (force) { } else if (test_bit(HFSPLUS_SB_FORCE, &sbi->flags)) {
/* nothing */ /* nothing */
} else if (vhdr->attributes & } else if (vhdr->attributes &
cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
pr_warn("filesystem is marked locked, leaving read-only.\n"); pr_warn("filesystem is marked locked, leaving read-only.\n");
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
*flags |= SB_RDONLY; fc->sb_flags |= SB_RDONLY;
} else if (vhdr->attributes & } else if (vhdr->attributes &
cpu_to_be32(HFSPLUS_VOL_JOURNALED)) { cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
pr_warn("filesystem is marked journaled, leaving read-only.\n"); pr_warn("filesystem is marked journaled, leaving read-only.\n");
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
*flags |= SB_RDONLY; fc->sb_flags |= SB_RDONLY;
} }
} }
return 0; return 0;
@ -373,38 +373,33 @@ static const struct super_operations hfsplus_sops = {
.put_super = hfsplus_put_super, .put_super = hfsplus_put_super,
.sync_fs = hfsplus_sync_fs, .sync_fs = hfsplus_sync_fs,
.statfs = hfsplus_statfs, .statfs = hfsplus_statfs,
.remount_fs = hfsplus_remount,
.show_options = hfsplus_show_options, .show_options = hfsplus_show_options,
}; };
static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
struct hfsplus_vh *vhdr; struct hfsplus_vh *vhdr;
struct hfsplus_sb_info *sbi; struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
hfsplus_cat_entry entry; hfsplus_cat_entry entry;
struct hfs_find_data fd; struct hfs_find_data fd;
struct inode *root, *inode; struct inode *root, *inode;
struct qstr str; struct qstr str;
struct nls_table *nls = NULL; struct nls_table *nls;
u64 last_fs_block, last_fs_page; u64 last_fs_block, last_fs_page;
int silent = fc->sb_flags & SB_SILENT;
int err; int err;
err = -ENOMEM;
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
goto out;
sb->s_fs_info = sbi;
mutex_init(&sbi->alloc_mutex); mutex_init(&sbi->alloc_mutex);
mutex_init(&sbi->vh_mutex); mutex_init(&sbi->vh_mutex);
spin_lock_init(&sbi->work_lock); spin_lock_init(&sbi->work_lock);
INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs); INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs);
hfsplus_fill_defaults(sbi);
err = -EINVAL; err = -EINVAL;
if (!hfsplus_parse_options(data, sbi)) { if (!sbi->nls) {
pr_err("unable to parse mount options\n"); /* try utf8 first, as this is the old default behaviour */
goto out_unload_nls; sbi->nls = load_nls("utf8");
if (!sbi->nls)
sbi->nls = load_nls_default();
} }
/* temporarily use utf8 to correctly find the hidden dir below */ /* temporarily use utf8 to correctly find the hidden dir below */
@ -616,7 +611,6 @@ out_unload_nls:
unload_nls(sbi->nls); unload_nls(sbi->nls);
unload_nls(nls); unload_nls(nls);
kfree(sbi); kfree(sbi);
out:
return err; return err;
} }
@ -641,18 +635,46 @@ static void hfsplus_free_inode(struct inode *inode)
#define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info) #define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info)
static struct dentry *hfsplus_mount(struct file_system_type *fs_type, static int hfsplus_get_tree(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
return mount_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super); return get_tree_bdev(fc, hfsplus_fill_super);
}
static void hfsplus_free_fc(struct fs_context *fc)
{
kfree(fc->s_fs_info);
}
static const struct fs_context_operations hfsplus_context_ops = {
.parse_param = hfsplus_parse_param,
.get_tree = hfsplus_get_tree,
.reconfigure = hfsplus_reconfigure,
.free = hfsplus_free_fc,
};
static int hfsplus_init_fs_context(struct fs_context *fc)
{
struct hfsplus_sb_info *sbi;
sbi = kzalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
if (fc->purpose != FS_CONTEXT_FOR_RECONFIGURE)
hfsplus_fill_defaults(sbi);
fc->s_fs_info = sbi;
fc->ops = &hfsplus_context_ops;
return 0;
} }
static struct file_system_type hfsplus_fs_type = { static struct file_system_type hfsplus_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "hfsplus", .name = "hfsplus",
.mount = hfsplus_mount,
.kill_sb = kill_block_super, .kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV, .fs_flags = FS_REQUIRES_DEV,
.init_fs_context = hfsplus_init_fs_context,
}; };
MODULE_ALIAS_FS("hfsplus"); MODULE_ALIAS_FS("hfsplus");