Commit 26092bf5 authored by Theodore Ts'o's avatar Theodore Ts'o

ext4: use a table-driven handler for mount options

By using a table-drive approach, we shave about 100 lines of code from
ext4, and make the code a bit more regular and factored out.  This
will also make it possible in a future patch to use this table for
displaying the mount options that were specified in /proc/mounts.
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent 72578c33
...@@ -1509,371 +1509,269 @@ static int clear_qf_name(struct super_block *sb, int qtype) ...@@ -1509,371 +1509,269 @@ static int clear_qf_name(struct super_block *sb, int qtype)
} }
#endif #endif
static int parse_options(char *options, struct super_block *sb, #define MOPT_SET 0x0001
unsigned long *journal_devnum, #define MOPT_CLEAR 0x0002
unsigned int *journal_ioprio, #define MOPT_NOSUPPORT 0x0004
int is_remount) #define MOPT_EXPLICIT 0x0008
{ #define MOPT_CLEAR_ERR 0x0010
struct ext4_sb_info *sbi = EXT4_SB(sb); #define MOPT_GTE0 0x0020
char *p;
substring_t args[MAX_OPT_ARGS];
int data_opt = 0;
int option;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
int qfmt; #define MOPT_Q 0
#define MOPT_QFMT 0x0040
#else
#define MOPT_Q MOPT_NOSUPPORT
#define MOPT_QFMT MOPT_NOSUPPORT
#endif #endif
#define MOPT_DATAJ 0x0080
if (!options) static const struct mount_opts {
return 1;
while ((p = strsep(&options, ",")) != NULL) {
int token; int token;
if (!*p) int mount_opt;
continue; int flags;
} ext4_mount_opts[] = {
/* {Opt_minix_df, EXT4_MOUNT_MINIX_DF, MOPT_SET},
* Initialize args struct so we know whether arg was {Opt_bsd_df, EXT4_MOUNT_MINIX_DF, MOPT_CLEAR},
* found; some options take optional arguments. {Opt_grpid, EXT4_MOUNT_GRPID, MOPT_SET},
*/ {Opt_nogrpid, EXT4_MOUNT_GRPID, MOPT_CLEAR},
args[0].to = args[0].from = NULL; {Opt_mblk_io_submit, EXT4_MOUNT_MBLK_IO_SUBMIT, MOPT_SET},
token = match_token(p, tokens, args); {Opt_nomblk_io_submit, EXT4_MOUNT_MBLK_IO_SUBMIT, MOPT_CLEAR},
switch (token) { {Opt_block_validity, EXT4_MOUNT_BLOCK_VALIDITY, MOPT_SET},
case Opt_bsd_df: {Opt_noblock_validity, EXT4_MOUNT_BLOCK_VALIDITY, MOPT_CLEAR},
clear_opt(sb, MINIX_DF); {Opt_dioread_nolock, EXT4_MOUNT_DIOREAD_NOLOCK, MOPT_SET},
break; {Opt_dioread_lock, EXT4_MOUNT_DIOREAD_NOLOCK, MOPT_CLEAR},
case Opt_minix_df: {Opt_discard, EXT4_MOUNT_DISCARD, MOPT_SET},
set_opt(sb, MINIX_DF); {Opt_nodiscard, EXT4_MOUNT_DISCARD, MOPT_CLEAR},
break; {Opt_delalloc, EXT4_MOUNT_DELALLOC, MOPT_SET | MOPT_EXPLICIT},
case Opt_grpid: {Opt_nodelalloc, EXT4_MOUNT_DELALLOC, MOPT_CLEAR | MOPT_EXPLICIT},
set_opt(sb, GRPID); {Opt_journal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM, MOPT_SET},
break; {Opt_journal_async_commit, (EXT4_MOUNT_JOURNAL_ASYNC_COMMIT |
case Opt_nogrpid: EXT4_MOUNT_JOURNAL_CHECKSUM), MOPT_SET},
clear_opt(sb, GRPID); {Opt_noload, EXT4_MOUNT_NOLOAD, MOPT_SET},
break; {Opt_err_panic, EXT4_MOUNT_ERRORS_PANIC, MOPT_SET | MOPT_CLEAR_ERR},
case Opt_resuid: {Opt_err_ro, EXT4_MOUNT_ERRORS_RO, MOPT_SET | MOPT_CLEAR_ERR},
if (match_int(&args[0], &option)) {Opt_err_cont, EXT4_MOUNT_ERRORS_CONT, MOPT_SET | MOPT_CLEAR_ERR},
return 0; {Opt_data_err_abort, EXT4_MOUNT_DATA_ERR_ABORT, MOPT_SET},
sbi->s_resuid = option; {Opt_data_err_ignore, EXT4_MOUNT_DATA_ERR_ABORT, MOPT_CLEAR},
break; {Opt_barrier, EXT4_MOUNT_BARRIER, MOPT_SET},
case Opt_resgid: {Opt_nobarrier, EXT4_MOUNT_BARRIER, MOPT_CLEAR},
if (match_int(&args[0], &option)) {Opt_noauto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_SET},
return 0; {Opt_auto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_CLEAR},
sbi->s_resgid = option; {Opt_noinit_itable, EXT4_MOUNT_INIT_INODE_TABLE, MOPT_CLEAR},
break; {Opt_commit, 0, MOPT_GTE0},
case Opt_sb: {Opt_max_batch_time, 0, MOPT_GTE0},
/* handled by get_sb_block() instead of here */ {Opt_min_batch_time, 0, MOPT_GTE0},
/* *sb_block = match_int(&args[0]); */ {Opt_inode_readahead_blks, 0, MOPT_GTE0},
break; {Opt_init_itable, 0, MOPT_GTE0},
case Opt_err_panic: {Opt_stripe, 0, MOPT_GTE0},
clear_opt(sb, ERRORS_MASK); {Opt_data_journal, EXT4_MOUNT_JOURNAL_DATA, MOPT_DATAJ},
set_opt(sb, ERRORS_PANIC); {Opt_data_ordered, EXT4_MOUNT_ORDERED_DATA, MOPT_DATAJ},
break; {Opt_data_writeback, EXT4_MOUNT_WRITEBACK_DATA, MOPT_DATAJ},
case Opt_err_ro:
clear_opt(sb, ERRORS_MASK);
set_opt(sb, ERRORS_RO);
break;
case Opt_err_cont:
clear_opt(sb, ERRORS_MASK);
set_opt(sb, ERRORS_CONT);
break;
case Opt_nouid32:
set_opt(sb, NO_UID32);
break;
case Opt_debug:
set_opt(sb, DEBUG);
break;
case Opt_removed:
ext4_msg(sb, KERN_WARNING,
"Ignoring deprecated %s option", p);
break;
#ifdef CONFIG_EXT4_FS_XATTR #ifdef CONFIG_EXT4_FS_XATTR
case Opt_user_xattr: {Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET},
set_opt(sb, XATTR_USER); {Opt_nouser_xattr, EXT4_MOUNT_XATTR_USER, MOPT_CLEAR},
break;
case Opt_nouser_xattr:
clear_opt(sb, XATTR_USER);
break;
#else #else
case Opt_user_xattr: {Opt_user_xattr, 0, MOPT_NOSUPPORT},
case Opt_nouser_xattr: {Opt_nouser_xattr, 0, MOPT_NOSUPPORT},
ext4_msg(sb, KERN_ERR, "(no)user_xattr options not supported");
break;
#endif #endif
#ifdef CONFIG_EXT4_FS_POSIX_ACL #ifdef CONFIG_EXT4_FS_POSIX_ACL
case Opt_acl: {Opt_acl, EXT4_MOUNT_POSIX_ACL, MOPT_SET},
set_opt(sb, POSIX_ACL); {Opt_noacl, EXT4_MOUNT_POSIX_ACL, MOPT_CLEAR},
break;
case Opt_noacl:
clear_opt(sb, POSIX_ACL);
break;
#else #else
case Opt_acl: {Opt_acl, 0, MOPT_NOSUPPORT},
case Opt_noacl: {Opt_noacl, 0, MOPT_NOSUPPORT},
ext4_msg(sb, KERN_ERR, "(no)acl options not supported");
break;
#endif #endif
{Opt_nouid32, EXT4_MOUNT_NO_UID32, MOPT_SET},
{Opt_debug, EXT4_MOUNT_DEBUG, MOPT_SET},
{Opt_quota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA, MOPT_SET | MOPT_Q},
{Opt_usrquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA,
MOPT_SET | MOPT_Q},
{Opt_grpquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_GRPQUOTA,
MOPT_SET | MOPT_Q},
{Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
EXT4_MOUNT_GRPQUOTA), MOPT_CLEAR | MOPT_Q},
{Opt_usrjquota, 0, MOPT_Q},
{Opt_grpjquota, 0, MOPT_Q},
{Opt_offusrjquota, 0, MOPT_Q},
{Opt_offgrpjquota, 0, MOPT_Q},
{Opt_jqfmt_vfsold, QFMT_VFS_OLD, MOPT_QFMT},
{Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
{Opt_err, 0, 0}
};
static int handle_mount_opt(struct super_block *sb, char *opt, int token,
substring_t *args, unsigned long *journal_devnum,
unsigned int *journal_ioprio, int is_remount)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
const struct mount_opts *m;
int arg = 0;
if (args->from && match_int(args, &arg))
return -1;
switch (token) {
case Opt_sb:
return 1; /* handled by get_sb_block() */
case Opt_removed:
ext4_msg(sb, KERN_WARNING,
"Ignoring removed %s option", opt);
return 1;
case Opt_resuid:
sbi->s_resuid = arg;
return 1;
case Opt_resgid:
sbi->s_resgid = arg;
return 1;
case Opt_abort:
sbi->s_mount_flags |= EXT4_MF_FS_ABORTED;
return 1;
case Opt_i_version:
sb->s_flags |= MS_I_VERSION;
return 1;
case Opt_journal_dev: case Opt_journal_dev:
if (is_remount) { if (is_remount) {
ext4_msg(sb, KERN_ERR, ext4_msg(sb, KERN_ERR,
"Cannot specify journal on remount"); "Cannot specify journal on remount");
return 0; return -1;
} }
if (match_int(&args[0], &option)) *journal_devnum = arg;
return 0; return 1;
*journal_devnum = option; case Opt_journal_ioprio:
break; if (arg < 0 || arg > 7)
case Opt_journal_checksum: return -1;
set_opt(sb, JOURNAL_CHECKSUM); *journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
break; return 1;
case Opt_journal_async_commit: }
set_opt(sb, JOURNAL_ASYNC_COMMIT);
set_opt(sb, JOURNAL_CHECKSUM); for (m = ext4_mount_opts; m->token != Opt_err; m++) {
break; if (token != m->token)
case Opt_noload: continue;
set_opt(sb, NOLOAD); if (args->from && (m->flags & MOPT_GTE0) && (arg < 0))
break; return -1;
case Opt_commit: if (m->flags & MOPT_EXPLICIT)
if (match_int(&args[0], &option)) set_opt2(sb, EXPLICIT_DELALLOC);
return 0; if (m->flags & MOPT_CLEAR_ERR)
if (option < 0) clear_opt(sb, ERRORS_MASK);
return 0; if (token == Opt_noquota && sb_any_quota_loaded(sb)) {
if (option == 0) ext4_msg(sb, KERN_ERR, "Cannot change quota "
option = JBD2_DEFAULT_MAX_COMMIT_AGE; "options when quota turned on");
sbi->s_commit_interval = HZ * option; return -1;
break; }
case Opt_max_batch_time:
if (match_int(&args[0], &option)) if (m->flags & MOPT_NOSUPPORT) {
return 0; ext4_msg(sb, KERN_ERR, "%s option not supported", opt);
if (option < 0) } else if (token == Opt_commit) {
return 0; if (arg == 0)
if (option == 0) arg = JBD2_DEFAULT_MAX_COMMIT_AGE;
option = EXT4_DEF_MAX_BATCH_TIME; sbi->s_commit_interval = HZ * arg;
sbi->s_max_batch_time = option; } else if (token == Opt_max_batch_time) {
break; if (arg == 0)
case Opt_min_batch_time: arg = EXT4_DEF_MAX_BATCH_TIME;
if (match_int(&args[0], &option)) sbi->s_max_batch_time = arg;
return 0; } else if (token == Opt_min_batch_time) {
if (option < 0) sbi->s_min_batch_time = arg;
return 0; } else if (token == Opt_inode_readahead_blks) {
sbi->s_min_batch_time = option; if (arg > (1 << 30))
break; return -1;
case Opt_data_journal: if (arg && !is_power_of_2(arg)) {
data_opt = EXT4_MOUNT_JOURNAL_DATA; ext4_msg(sb, KERN_ERR,
goto datacheck; "EXT4-fs: inode_readahead_blks"
case Opt_data_ordered: " must be a power of 2");
data_opt = EXT4_MOUNT_ORDERED_DATA; return -1;
goto datacheck; }
case Opt_data_writeback: sbi->s_inode_readahead_blks = arg;
data_opt = EXT4_MOUNT_WRITEBACK_DATA; } else if (token == Opt_init_itable) {
datacheck: set_opt(sb, INIT_INODE_TABLE);
if (!args->from)
arg = EXT4_DEF_LI_WAIT_MULT;
sbi->s_li_wait_mult = arg;
} else if (token == Opt_stripe) {
sbi->s_stripe = arg;
} else if (m->flags & MOPT_DATAJ) {
if (is_remount) { if (is_remount) {
if (!sbi->s_journal) if (!sbi->s_journal)
ext4_msg(sb, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option"); ext4_msg(sb, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
else if (test_opt(sb, DATA_FLAGS) != data_opt) { else if (test_opt(sb, DATA_FLAGS) !=
m->mount_opt) {
ext4_msg(sb, KERN_ERR, ext4_msg(sb, KERN_ERR,
"Cannot change data mode on remount"); "Cannot change data mode on remount");
return 0; return -1;
} }
} else { } else {
clear_opt(sb, DATA_FLAGS); clear_opt(sb, DATA_FLAGS);
sbi->s_mount_opt |= data_opt; sbi->s_mount_opt |= m->mount_opt;
} }
break;
case Opt_data_err_abort:
set_opt(sb, DATA_ERR_ABORT);
break;
case Opt_data_err_ignore:
clear_opt(sb, DATA_ERR_ABORT);
break;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
case Opt_usrjquota: } else if (token == Opt_usrjquota) {
if (!set_qf_name(sb, USRQUOTA, &args[0])) if (!set_qf_name(sb, USRQUOTA, &args[0]))
return 0; return -1;
break; } else if (token == Opt_grpjquota) {
case Opt_grpjquota:
if (!set_qf_name(sb, GRPQUOTA, &args[0])) if (!set_qf_name(sb, GRPQUOTA, &args[0]))
return 0; return -1;
break; } else if (token == Opt_offusrjquota) {
case Opt_offusrjquota:
if (!clear_qf_name(sb, USRQUOTA)) if (!clear_qf_name(sb, USRQUOTA))
return 0; return -1;
break; } else if (token == Opt_offgrpjquota) {
case Opt_offgrpjquota:
if (!clear_qf_name(sb, GRPQUOTA)) if (!clear_qf_name(sb, GRPQUOTA))
return 0; return -1;
break; } else if (m->flags & MOPT_QFMT) {
case Opt_jqfmt_vfsold:
qfmt = QFMT_VFS_OLD;
goto set_qf_format;
case Opt_jqfmt_vfsv0:
qfmt = QFMT_VFS_V0;
goto set_qf_format;
case Opt_jqfmt_vfsv1:
qfmt = QFMT_VFS_V1;
set_qf_format:
if (sb_any_quota_loaded(sb) && if (sb_any_quota_loaded(sb) &&
sbi->s_jquota_fmt != qfmt) { sbi->s_jquota_fmt != m->mount_opt) {
ext4_msg(sb, KERN_ERR, "Cannot change " ext4_msg(sb, KERN_ERR, "Cannot "
"journaled quota options when " "change journaled quota options "
"quota turned on"); "when quota turned on");
return 0; return -1;
}
sbi->s_jquota_fmt = qfmt;
break;
case Opt_quota:
case Opt_usrquota:
set_opt(sb, QUOTA);
set_opt(sb, USRQUOTA);
break;
case Opt_grpquota:
set_opt(sb, QUOTA);
set_opt(sb, GRPQUOTA);
break;
case Opt_noquota:
if (sb_any_quota_loaded(sb)) {
ext4_msg(sb, KERN_ERR, "Cannot change quota "
"options when quota turned on");
return 0;
} }
clear_opt(sb, QUOTA); sbi->s_jquota_fmt = m->mount_opt;
clear_opt(sb, USRQUOTA);
clear_opt(sb, GRPQUOTA);
break;
#else
case Opt_quota:
case Opt_usrquota:
case Opt_grpquota:
ext4_msg(sb, KERN_ERR,
"quota options not supported");
break;
case Opt_usrjquota:
case Opt_grpjquota:
case Opt_offusrjquota:
case Opt_offgrpjquota:
case Opt_jqfmt_vfsold:
case Opt_jqfmt_vfsv0:
case Opt_jqfmt_vfsv1:
ext4_msg(sb, KERN_ERR,
"journaled quota options not supported");
break;
case Opt_noquota:
break;
#endif #endif
case Opt_abort: } else {
sbi->s_mount_flags |= EXT4_MF_FS_ABORTED; if (!args->from)
break; arg = 1;
case Opt_nobarrier: if (m->flags & MOPT_CLEAR)
clear_opt(sb, BARRIER); arg = !arg;
break; else if (unlikely(!(m->flags & MOPT_SET))) {
case Opt_barrier: ext4_msg(sb, KERN_WARNING,
if (args[0].from) { "buggy handling of option %s", opt);
if (match_int(&args[0], &option)) WARN_ON(1);
return 0; return -1;
} else
option = 1; /* No argument, default to 1 */
if (option)
set_opt(sb, BARRIER);
else
clear_opt(sb, BARRIER);
break;
case Opt_i_version:
sb->s_flags |= MS_I_VERSION;
break;
case Opt_nodelalloc:
clear_opt(sb, DELALLOC);
clear_opt2(sb, EXPLICIT_DELALLOC);
break;
case Opt_mblk_io_submit:
set_opt(sb, MBLK_IO_SUBMIT);
break;
case Opt_nomblk_io_submit:
clear_opt(sb, MBLK_IO_SUBMIT);
break;
case Opt_stripe:
if (match_int(&args[0], &option))
return 0;
if (option < 0)
return 0;
sbi->s_stripe = option;
break;
case Opt_delalloc:
set_opt(sb, DELALLOC);
set_opt2(sb, EXPLICIT_DELALLOC);
break;
case Opt_block_validity:
set_opt(sb, BLOCK_VALIDITY);
break;
case Opt_noblock_validity:
clear_opt(sb, BLOCK_VALIDITY);
break;
case Opt_inode_readahead_blks:
if (match_int(&args[0], &option))
return 0;
if (option < 0 || option > (1 << 30))
return 0;
if (option && !is_power_of_2(option)) {
ext4_msg(sb, KERN_ERR,
"EXT4-fs: inode_readahead_blks"
" must be a power of 2");
return 0;
} }
sbi->s_inode_readahead_blks = option; if (arg != 0)
break; sbi->s_mount_opt |= m->mount_opt;
case Opt_journal_ioprio:
if (match_int(&args[0], &option))
return 0;
if (option < 0 || option > 7)
break;
*journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE,
option);
break;
case Opt_noauto_da_alloc:
set_opt(sb, NO_AUTO_DA_ALLOC);
break;
case Opt_auto_da_alloc:
if (args[0].from) {
if (match_int(&args[0], &option))
return 0;
} else
option = 1; /* No argument, default to 1 */
if (option)
clear_opt(sb, NO_AUTO_DA_ALLOC);
else else
set_opt(sb,NO_AUTO_DA_ALLOC); sbi->s_mount_opt &= ~m->mount_opt;
break; }
case Opt_discard: return 1;
set_opt(sb, DISCARD);
break;
case Opt_nodiscard:
clear_opt(sb, DISCARD);
break;
case Opt_dioread_nolock:
set_opt(sb, DIOREAD_NOLOCK);
break;
case Opt_dioread_lock:
clear_opt(sb, DIOREAD_NOLOCK);
break;
case Opt_init_itable:
set_opt(sb, INIT_INODE_TABLE);
if (args[0].from) {
if (match_int(&args[0], &option))
return 0;
} else
option = EXT4_DEF_LI_WAIT_MULT;
if (option < 0)
return 0;
sbi->s_li_wait_mult = option;
break;
case Opt_noinit_itable:
clear_opt(sb, INIT_INODE_TABLE);
break;
default:
ext4_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" "
"or missing value", p);
return 0;
} }
ext4_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" "
"or missing value", opt);
return -1;
}
static int parse_options(char *options, struct super_block *sb,
unsigned long *journal_devnum,
unsigned int *journal_ioprio,
int is_remount)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
char *p;
substring_t args[MAX_OPT_ARGS];
int token;
if (!options)
return 1;
while ((p = strsep(&options, ",")) != NULL) {
if (!*p)
continue;
/*
* Initialize args struct so we know whether arg was
* found; some options take optional arguments.
*/
args[0].to = args[0].from = 0;
token = match_token(p, tokens, args);
if (handle_mount_opt(sb, p, token, args, journal_devnum,
journal_ioprio, is_remount) < 0)
return 0;
} }
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) { if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment