aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/super.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4/super.c')
-rw-r--r--fs/ext4/super.c541
1 files changed, 368 insertions, 173 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 68395631d222..1e4908f5f593 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -91,8 +91,7 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb,
static int ext4_validate_options(struct fs_context *fc);
static int ext4_check_opt_consistency(struct fs_context *fc,
struct super_block *sb);
-static void ext4_apply_quota_options(struct fs_context *fc,
- struct super_block *sb);
+static int ext4_apply_options(struct fs_context *fc, struct super_block *sb);
/*
* Lock ordering
@@ -2149,57 +2148,74 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
}
#endif
-static int ext4_set_test_dummy_encryption(struct super_block *sb,
- struct fs_parameter *param,
- bool is_remount)
+static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
{
#ifdef CONFIG_FS_ENCRYPTION
struct ext4_sb_info *sbi = EXT4_SB(sb);
int err;
- /*
- * This mount option is just for testing, and it's not worthwhile to
- * implement the extra complexity (e.g. RCU protection) that would be
- * needed to allow it to be set or changed during remount. We do allow
- * it to be specified during remount, but only if there is no change.
- */
- if (is_remount && !sbi->s_dummy_enc_policy.policy) {
- ext4_msg(sb, KERN_WARNING,
- "Can't set test_dummy_encryption on remount");
- return -1;
- }
- err = fscrypt_set_test_dummy_encryption(sb, param->string,
+ err = fscrypt_set_test_dummy_encryption(sb, arg,
&sbi->s_dummy_enc_policy);
if (err) {
- if (err == -EEXIST)
- ext4_msg(sb, KERN_WARNING,
- "Can't change test_dummy_encryption on remount");
- else if (err == -EINVAL)
- ext4_msg(sb, KERN_WARNING,
- "Value of option \"%s\" is unrecognized",
- param->key);
- else
- ext4_msg(sb, KERN_WARNING,
- "Error processing option \"%s\" [%d]",
- param->key, err);
- return -1;
+ ext4_msg(sb, KERN_WARNING,
+ "Error while setting test dummy encryption [%d]", err);
+ return err;
}
ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
-#else
- ext4_msg(sb, KERN_WARNING,
- "Test dummy encryption mount option ignored");
#endif
- return 1;
+ return 0;
}
+#define EXT4_SPEC_JQUOTA (1 << 0)
+#define EXT4_SPEC_JQFMT (1 << 1)
+#define EXT4_SPEC_DATAJ (1 << 2)
+#define EXT4_SPEC_SB_BLOCK (1 << 3)
+#define EXT4_SPEC_JOURNAL_DEV (1 << 4)
+#define EXT4_SPEC_JOURNAL_IOPRIO (1 << 5)
+#define EXT4_SPEC_DUMMY_ENCRYPTION (1 << 6)
+#define EXT4_SPEC_s_want_extra_isize (1 << 7)
+#define EXT4_SPEC_s_max_batch_time (1 << 8)
+#define EXT4_SPEC_s_min_batch_time (1 << 9)
+#define EXT4_SPEC_s_inode_readahead_blks (1 << 10)
+#define EXT4_SPEC_s_li_wait_mult (1 << 11)
+#define EXT4_SPEC_s_max_dir_size_kb (1 << 12)
+#define EXT4_SPEC_s_stripe (1 << 13)
+#define EXT4_SPEC_s_resuid (1 << 14)
+#define EXT4_SPEC_s_resgid (1 << 15)
+#define EXT4_SPEC_s_commit_interval (1 << 16)
+#define EXT4_SPEC_s_fc_debug_max_replay (1 << 17)
+
struct ext4_fs_context {
char *s_qf_names[EXT4_MAXQUOTAS];
+ char *test_dummy_enc_arg;
int s_jquota_fmt; /* Format of quota to use */
+ int mb_optimize_scan;
+#ifdef CONFIG_EXT4_DEBUG
+ int s_fc_debug_max_replay;
+#endif
unsigned short qname_spec;
+ unsigned long vals_s_flags; /* Bits to set in s_flags */
+ unsigned long mask_s_flags; /* Bits changed in s_flags */
unsigned long journal_devnum;
+ unsigned long s_commit_interval;
+ unsigned long s_stripe;
+ unsigned int s_inode_readahead_blks;
+ unsigned int s_want_extra_isize;
+ unsigned int s_li_wait_mult;
+ unsigned int s_max_dir_size_kb;
unsigned int journal_ioprio;
- int mb_optimize_scan;
+ unsigned int vals_s_mount_opt;
+ unsigned int mask_s_mount_opt;
+ unsigned int vals_s_mount_opt2;
+ unsigned int mask_s_mount_opt2;
+ unsigned int vals_s_mount_flags;
+ unsigned int mask_s_mount_flags;
unsigned int opt_flags; /* MOPT flags */
+ unsigned int spec;
+ u32 s_max_batch_time;
+ u32 s_min_batch_time;
+ kuid_t s_resuid;
+ kgid_t s_resgid;
};
#ifdef CONFIG_QUOTA
@@ -2239,6 +2255,7 @@ static int note_qf_name(struct fs_context *fc, int qtype,
}
ctx->s_qf_names[qtype] = qname;
ctx->qname_spec |= 1 << qtype;
+ ctx->spec |= EXT4_SPEC_JQUOTA;
return 0;
}
@@ -2254,15 +2271,35 @@ static int unnote_qf_name(struct fs_context *fc, int qtype)
ctx->s_qf_names[qtype] = NULL;
ctx->qname_spec |= 1 << qtype;
+ ctx->spec |= EXT4_SPEC_JQUOTA;
return 0;
}
#endif
+#define EXT4_SET_CTX(name) \
+static inline void ctx_set_##name(struct ext4_fs_context *ctx, int flag)\
+{ \
+ ctx->mask_s_##name |= flag; \
+ ctx->vals_s_##name |= flag; \
+} \
+static inline void ctx_clear_##name(struct ext4_fs_context *ctx, int flag)\
+{ \
+ ctx->mask_s_##name |= flag; \
+ ctx->vals_s_##name &= ~flag; \
+} \
+static inline bool ctx_test_##name(struct ext4_fs_context *ctx, int flag)\
+{ \
+ return ((ctx->vals_s_##name & flag) != 0); \
+} \
+
+EXT4_SET_CTX(flags);
+EXT4_SET_CTX(mount_opt);
+EXT4_SET_CTX(mount_opt2);
+EXT4_SET_CTX(mount_flags);
+
static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
{
struct ext4_fs_context *ctx = fc->fs_private;
- struct ext4_sb_info *sbi = fc->s_fs_info;
- struct super_block *sb = sbi->s_sb;
struct fs_parse_result result;
const struct mount_opts *m;
int is_remount;
@@ -2300,20 +2337,20 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
param->key);
return 1;
case Opt_abort:
- ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
+ ctx_set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
return 1;
case Opt_i_version:
- sb->s_flags |= SB_I_VERSION;
+ ctx_set_flags(ctx, SB_I_VERSION);
return 1;
case Opt_lazytime:
- sb->s_flags |= SB_LAZYTIME;
+ ctx_set_flags(ctx, SB_LAZYTIME);
return 1;
case Opt_nolazytime:
- sb->s_flags &= ~SB_LAZYTIME;
+ ctx_clear_flags(ctx, SB_LAZYTIME);
return 1;
case Opt_inlinecrypt:
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
- sb->s_flags |= SB_INLINECRYPT;
+ ctx_set_flags(ctx, SB_INLINECRYPT);
#else
ext4_msg(NULL, KERN_ERR, "inline encryption not supported");
#endif
@@ -2340,21 +2377,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
if (m->flags & MOPT_EXPLICIT) {
if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
- set_opt2(sb, EXPLICIT_DELALLOC);
+ ctx_set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
} else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
- set_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM);
+ ctx_set_mount_opt2(ctx,
+ EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
} else
return -EINVAL;
}
if (m->flags & MOPT_CLEAR_ERR)
- clear_opt(sb, ERRORS_MASK);
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_ERRORS_MASK);
if (m->flags & MOPT_NOSUPPORT) {
ext4_msg(NULL, KERN_ERR, "%s option not supported",
param->key);
} else if (token == Opt_commit) {
if (result.uint_32 == 0)
- sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
+ ctx->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
else if (result.uint_32 > INT_MAX / HZ) {
ext4_msg(NULL, KERN_ERR,
"Invalid commit interval %d, "
@@ -2362,21 +2400,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
result.uint_32, INT_MAX / HZ);
return -EINVAL;
}
- sbi->s_commit_interval = HZ * result.uint_32;
+ ctx->s_commit_interval = HZ * result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_commit_interval;
} else if (token == Opt_debug_want_extra_isize) {
- if ((result.uint_32 & 1) ||
- (result.uint_32 < 4) ||
- (result.uint_32 >
- (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) {
+ if ((result.uint_32 & 1) || (result.uint_32 < 4)) {
ext4_msg(NULL, KERN_ERR,
"Invalid want_extra_isize %d", result.uint_32);
return -EINVAL;
}
- sbi->s_want_extra_isize = result.uint_32;
+ ctx->s_want_extra_isize = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_want_extra_isize;
} else if (token == Opt_max_batch_time) {
- sbi->s_max_batch_time = result.uint_32;
+ ctx->s_max_batch_time = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_max_batch_time;
} else if (token == Opt_min_batch_time) {
- sbi->s_min_batch_time = result.uint_32;
+ ctx->s_min_batch_time = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_min_batch_time;
} else if (token == Opt_inode_readahead_blks) {
if (result.uint_32 &&
(result.uint_32 > (1 << 30) ||
@@ -2386,20 +2425,25 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
"0 or a power of 2 smaller than 2^31");
return -EINVAL;
}
- sbi->s_inode_readahead_blks = result.uint_32;
+ ctx->s_inode_readahead_blks = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_inode_readahead_blks;
} else if (token == Opt_init_itable) {
- set_opt(sb, INIT_INODE_TABLE);
- sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
+ ctx_set_mount_opt(ctx, EXT4_MOUNT_INIT_INODE_TABLE);
+ ctx->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
if (param->type == fs_value_is_string)
- sbi->s_li_wait_mult = result.uint_32;
+ ctx->s_li_wait_mult = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_li_wait_mult;
} else if (token == Opt_max_dir_size_kb) {
- sbi->s_max_dir_size_kb = result.uint_32;
+ ctx->s_max_dir_size_kb = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_max_dir_size_kb;
#ifdef CONFIG_EXT4_DEBUG
} else if (token == Opt_fc_debug_max_replay) {
- sbi->s_fc_debug_max_replay = result.uint_32;
+ ctx->s_fc_debug_max_replay = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_fc_debug_max_replay;
#endif
} else if (token == Opt_stripe) {
- sbi->s_stripe = result.uint_32;
+ ctx->s_stripe = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_stripe;
} else if (token == Opt_resuid) {
uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid)) {
@@ -2407,7 +2451,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
result.uint_32);
return -EINVAL;
}
- sbi->s_resuid = uid;
+ ctx->s_resuid = uid;
+ ctx->spec |= EXT4_SPEC_s_resuid;
} else if (token == Opt_resgid) {
gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(gid)) {
@@ -2415,7 +2460,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
result.uint_32);
return -EINVAL;
}
- sbi->s_resgid = gid;
+ ctx->s_resgid = gid;
+ ctx->spec |= EXT4_SPEC_s_resgid;
} else if (token == Opt_journal_dev) {
if (is_remount) {
ext4_msg(NULL, KERN_ERR,
@@ -2423,6 +2469,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
return -EINVAL;
}
ctx->journal_devnum = result.uint_32;
+ ctx->spec |= EXT4_SPEC_JOURNAL_DEV;
} else if (token == Opt_journal_path) {
struct inode *journal_inode;
struct path path;
@@ -2443,6 +2490,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
journal_inode = d_inode(path.dentry);
ctx->journal_devnum = new_encode_dev(journal_inode->i_rdev);
+ ctx->spec |= EXT4_SPEC_JOURNAL_DEV;
path_put(&path);
} else if (token == Opt_journal_ioprio) {
if (result.uint_32 > 7) {
@@ -2452,24 +2500,37 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
}
ctx->journal_ioprio =
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
+ ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;
} else if (token == Opt_test_dummy_encryption) {
- return ext4_set_test_dummy_encryption(sb, param, is_remount);
- } else if (m->flags & MOPT_DATAJ) {
- if (is_remount) {
- if (!sbi->s_journal)
- ext4_msg(NULL, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
- else if (test_opt(sb, DATA_FLAGS) != m->mount_opt) {
- ext4_msg(NULL, KERN_ERR,
- "Cannot change data mode on remount");
- return -EINVAL;
- }
- } else {
- clear_opt(sb, DATA_FLAGS);
- sbi->s_mount_opt |= m->mount_opt;
+#ifdef CONFIG_FS_ENCRYPTION
+ if (param->type == fs_value_is_flag) {
+ ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
+ ctx->test_dummy_enc_arg = NULL;
+ return 1;
+ }
+ if (*param->string &&
+ !(!strcmp(param->string, "v1") ||
+ !strcmp(param->string, "v2"))) {
+ ext4_msg(NULL, KERN_WARNING,
+ "Value of option \"%s\" is unrecognized",
+ param->key);
+ return -EINVAL;
}
+ ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
+ ctx->test_dummy_enc_arg = kmemdup_nul(param->string, param->size,
+ GFP_KERNEL);
+#else
+ ext4_msg(NULL, KERN_WARNING,
+ "Test dummy encryption mount option ignored");
+#endif
+ } else if (m->flags & MOPT_DATAJ) {
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+ ctx_set_mount_opt(ctx, m->mount_opt);
+ ctx->spec |= EXT4_SPEC_DATAJ;
#ifdef CONFIG_QUOTA
} else if (m->flags & MOPT_QFMT) {
ctx->s_jquota_fmt = m->mount_opt;
+ ctx->spec |= EXT4_SPEC_JQFMT;
#endif
} else if (token == Opt_dax || token == Opt_dax_always ||
token == Opt_dax_inode || token == Opt_dax_never) {
@@ -2477,56 +2538,30 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
switch (token) {
case Opt_dax:
case Opt_dax_always:
- if (is_remount &&
- (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
- (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
- fail_dax_change_remount:
- ext4_msg(NULL, KERN_ERR, "can't change "
- "dax mount option while remounting");
- return -EINVAL;
- }
- if (is_remount &&
- (test_opt(sb, DATA_FLAGS) ==
- EXT4_MOUNT_JOURNAL_DATA)) {
- ext4_msg(NULL, KERN_ERR, "can't mount with "
- "both data=journal and dax");
- return -EINVAL;
- }
- ext4_msg(NULL, KERN_WARNING,
- "DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
- sbi->s_mount_opt |= EXT4_MOUNT_DAX_ALWAYS;
- sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
+ ctx_set_mount_opt(ctx, m->mount_opt);
+ ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
break;
case Opt_dax_never:
- if (is_remount &&
- (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
- (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS)))
- goto fail_dax_change_remount;
- sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
- sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
+ ctx_set_mount_opt2(ctx, m->mount_opt);
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
break;
case Opt_dax_inode:
- if (is_remount &&
- ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
- (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
- !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE)))
- goto fail_dax_change_remount;
- sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
- sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
+ ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
/* Strictly for printing options */
- sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_INODE;
+ ctx_set_mount_opt2(ctx, m->mount_opt);
break;
}
#else
ext4_msg(NULL, KERN_INFO, "dax option not supported");
- sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
- sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
+ ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
return -EINVAL;
#endif
} else if (token == Opt_data_err_abort) {
- sbi->s_mount_opt |= m->mount_opt;
+ ctx_set_mount_opt(ctx, m->mount_opt);
} else if (token == Opt_data_err_ignore) {
- sbi->s_mount_opt &= ~m->mount_opt;
+ ctx_clear_mount_opt(ctx, m->mount_opt);
} else if (token == Opt_mb_optimize_scan) {
if (result.int_32 != 0 && result.int_32 != 1) {
ext4_msg(NULL, KERN_WARNING,
@@ -2552,14 +2587,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
}
if (m->flags & MOPT_2) {
if (set != 0)
- sbi->s_mount_opt2 |= m->mount_opt;
+ ctx_set_mount_opt2(ctx, m->mount_opt);
else
- sbi->s_mount_opt2 &= ~m->mount_opt;
+ ctx_clear_mount_opt2(ctx, m->mount_opt);
} else {
if (set != 0)
- sbi->s_mount_opt |= m->mount_opt;
+ ctx_set_mount_opt(ctx, m->mount_opt);
else
- sbi->s_mount_opt &= ~m->mount_opt;
+ ctx_clear_mount_opt(ctx, m->mount_opt);
}
}
return 1;
@@ -2624,8 +2659,9 @@ static int parse_options(char *options, struct super_block *sb,
if (ret < 0)
return 0;
- if (ctx->qname_spec)
- ext4_apply_quota_options(&fc, sb);
+ ret = ext4_apply_options(&fc, sb);
+ if (ret < 0)
+ return 0;
return 1;
}
@@ -2634,20 +2670,30 @@ static void ext4_apply_quota_options(struct fs_context *fc,
struct super_block *sb)
{
#ifdef CONFIG_QUOTA
+ bool quota_feature = ext4_has_feature_quota(sb);
struct ext4_fs_context *ctx = fc->fs_private;
struct ext4_sb_info *sbi = EXT4_SB(sb);
char *qname;
int i;
- for (i = 0; i < EXT4_MAXQUOTAS; i++) {
- if (!(ctx->qname_spec & (1 << i)))
- continue;
- qname = ctx->s_qf_names[i]; /* May be NULL */
- ctx->s_qf_names[i] = NULL;
- kfree(sbi->s_qf_names[i]);
- rcu_assign_pointer(sbi->s_qf_names[i], qname);
- set_opt(sb, QUOTA);
+ if (quota_feature)
+ return;
+
+ if (ctx->spec & EXT4_SPEC_JQUOTA) {
+ for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+ if (!(ctx->qname_spec & (1 << i)))
+ continue;
+
+ qname = ctx->s_qf_names[i]; /* May be NULL */
+ ctx->s_qf_names[i] = NULL;
+ kfree(sbi->s_qf_names[i]);
+ rcu_assign_pointer(sbi->s_qf_names[i], qname);
+ set_opt(sb, QUOTA);
+ }
}
+
+ if (ctx->spec & EXT4_SPEC_JQFMT)
+ sbi->s_jquota_fmt = ctx->s_jquota_fmt;
#endif
}
@@ -2662,17 +2708,36 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
struct ext4_sb_info *sbi = EXT4_SB(sb);
bool quota_feature = ext4_has_feature_quota(sb);
bool quota_loaded = sb_any_quota_loaded(sb);
- int i;
+ bool usr_qf_name, grp_qf_name, usrquota, grpquota;
+ int quota_flags, i;
+
+ /*
+ * We do the test below only for project quotas. 'usrquota' and
+ * 'grpquota' mount options are allowed even without quota feature
+ * to support legacy quotas in quota files.
+ */
+ if (ctx_test_mount_opt(ctx, EXT4_MOUNT_PRJQUOTA) &&
+ !ext4_has_feature_project(sb)) {
+ ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
+ "Cannot enable project quota enforcement.");
+ return -EINVAL;
+ }
+
+ quota_flags = EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
+ EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA;
+ if (quota_loaded &&
+ ctx->mask_s_mount_opt & quota_flags &&
+ !ctx_test_mount_opt(ctx, quota_flags))
+ goto err_quota_change;
- if (ctx->qname_spec && quota_loaded) {
- if (quota_feature)
- goto err_feature;
+ if (ctx->spec & EXT4_SPEC_JQUOTA) {
for (i = 0; i < EXT4_MAXQUOTAS; i++) {
if (!(ctx->qname_spec & (1 << i)))
continue;
- if (!!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
+ if (quota_loaded &&
+ !!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
goto err_jquota_change;
if (sbi->s_qf_names[i] && ctx->s_qf_names[i] &&
@@ -2680,17 +2745,60 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
ctx->s_qf_names[i]) != 0)
goto err_jquota_specified;
}
+
+ if (quota_feature) {
+ ext4_msg(NULL, KERN_INFO,
+ "Journaled quota options ignored when "
+ "QUOTA feature is enabled");
+ return 0;
+ }
}
- if (ctx->s_jquota_fmt) {
+ if (ctx->spec & EXT4_SPEC_JQFMT) {
if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
- goto err_quota_change;
+ goto err_jquota_change;
if (quota_feature) {
ext4_msg(NULL, KERN_INFO, "Quota format mount options "
"ignored when QUOTA feature is enabled");
return 0;
}
}
+
+ /* Make sure we don't mix old and new quota format */
+ usr_qf_name = (get_qf_name(sb, sbi, USRQUOTA) ||
+ ctx->s_qf_names[USRQUOTA]);
+ grp_qf_name = (get_qf_name(sb, sbi, GRPQUOTA) ||
+ ctx->s_qf_names[GRPQUOTA]);
+
+ usrquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+ test_opt(sb, USRQUOTA));
+
+ grpquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) ||
+ test_opt(sb, GRPQUOTA));
+
+ if (usr_qf_name) {
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
+ usrquota = false;
+ }
+ if (grp_qf_name) {
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
+ grpquota = false;
+ }
+
+ if (usr_qf_name || grp_qf_name) {
+ if (usrquota || grpquota) {
+ ext4_msg(NULL, KERN_ERR, "old and new quota "
+ "format mixing");
+ return -EINVAL;
+ }
+
+ if (!(ctx->spec & EXT4_SPEC_JQFMT || sbi->s_jquota_fmt)) {
+ ext4_msg(NULL, KERN_ERR, "journaled quota format "
+ "not specified");
+ return -EINVAL;
+ }
+ }
+
return 0;
err_quota_change:
@@ -2705,10 +2813,6 @@ err_jquota_specified:
ext4_msg(NULL, KERN_ERR, "%s quota file already specified",
QTYPE2NAME(i));
return -EINVAL;
-err_feature:
- ext4_msg(NULL, KERN_ERR, "Journaled quota options ignored "
- "when QUOTA feature is enabled");
- return 0;
#else
return 0;
#endif
@@ -2718,6 +2822,8 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
struct super_block *sb)
{
struct ext4_fs_context *ctx = fc->fs_private;
+ struct ext4_sb_info *sbi = fc->s_fs_info;
+ int is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
if ((ctx->opt_flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
ext4_msg(NULL, KERN_ERR,
@@ -2730,57 +2836,146 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
return -EINVAL;
}
+ if (ctx->s_want_extra_isize >
+ (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE)) {
+ ext4_msg(NULL, KERN_ERR,
+ "Invalid want_extra_isize %d",
+ ctx->s_want_extra_isize);
+ return -EINVAL;
+ }
+
+ if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DIOREAD_NOLOCK)) {
+ int blocksize =
+ BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
+ if (blocksize < PAGE_SIZE)
+ ext4_msg(NULL, KERN_WARNING, "Warning: mounting with an "
+ "experimental mount option 'dioread_nolock' "
+ "for blocksize < PAGE_SIZE");
+ }
+
+#ifdef CONFIG_FS_ENCRYPTION
+ /*
+ * This mount option is just for testing, and it's not worthwhile to
+ * implement the extra complexity (e.g. RCU protection) that would be
+ * needed to allow it to be set or changed during remount. We do allow
+ * it to be specified during remount, but only if there is no change.
+ */
+ if ((ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION) &&
+ is_remount && !sbi->s_dummy_enc_policy.policy) {
+ ext4_msg(NULL, KERN_WARNING,
+ "Can't set test_dummy_encryption on remount");
+ return -1;
+ }
+#endif
+
+ if ((ctx->spec & EXT4_SPEC_DATAJ) && is_remount) {
+ if (!sbi->s_journal) {
+ ext4_msg(NULL, KERN_WARNING,
+ "Remounting file system with no journal "
+ "so ignoring journalled data option");
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+ } else if (ctx->mask_s_mount_opt & EXT4_MOUNT_DATA_FLAGS) {
+ ext4_msg(NULL, KERN_ERR, "Cannot change data mode "
+ "on remount");
+ return -EINVAL;
+ }
+ }
+
+ if (is_remount) {
+ if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
+ (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)) {
+ ext4_msg(NULL, KERN_ERR, "can't mount with "
+ "both data=journal and dax");
+ return -EINVAL;
+ }
+
+ if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
+ (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
+ (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
+fail_dax_change_remount:
+ ext4_msg(NULL, KERN_ERR, "can't change "
+ "dax mount option while remounting");
+ return -EINVAL;
+ } else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER) &&
+ (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
+ (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS))) {
+ goto fail_dax_change_remount;
+ } else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_INODE) &&
+ ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
+ (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
+ !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE))) {
+ goto fail_dax_change_remount;
+ }
+ }
+
return ext4_check_quota_consistency(fc, sb);
}
-static int ext4_validate_options(struct fs_context *fc)
+static int ext4_apply_options(struct fs_context *fc, struct super_block *sb)
{
+ struct ext4_fs_context *ctx = fc->fs_private;
struct ext4_sb_info *sbi = fc->s_fs_info;
- struct super_block *sb = sbi->s_sb;
+ int ret = 0;
+
+ sbi->s_mount_opt &= ~ctx->mask_s_mount_opt;
+ sbi->s_mount_opt |= ctx->vals_s_mount_opt;
+ sbi->s_mount_opt2 &= ~ctx->mask_s_mount_opt2;
+ sbi->s_mount_opt2 |= ctx->vals_s_mount_opt2;
+ sbi->s_mount_flags &= ~ctx->mask_s_mount_flags;
+ sbi->s_mount_flags |= ctx->vals_s_mount_flags;
+ sb->s_flags &= ~ctx->mask_s_flags;
+ sb->s_flags |= ctx->vals_s_flags;
+
+#define APPLY(X) ({ if (ctx->spec & EXT4_SPEC_##X) sbi->X = ctx->X; })
+ APPLY(s_commit_interval);
+ APPLY(s_stripe);
+ APPLY(s_max_batch_time);
+ APPLY(s_min_batch_time);
+ APPLY(s_want_extra_isize);
+ APPLY(s_inode_readahead_blks);
+ APPLY(s_max_dir_size_kb);
+ APPLY(s_li_wait_mult);
+ APPLY(s_resgid);
+ APPLY(s_resuid);
+
+#ifdef CONFIG_EXT4_DEBUG
+ APPLY(s_fc_debug_max_replay);
+#endif
+
+ ext4_apply_quota_options(fc, sb);
+
+ if (ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION)
+ ret = ext4_set_test_dummy_encryption(sb, ctx->test_dummy_enc_arg);
+
+ return ret;
+}
+
+
+static int ext4_validate_options(struct fs_context *fc)
+{
#ifdef CONFIG_QUOTA
+ struct ext4_fs_context *ctx = fc->fs_private;
char *usr_qf_name, *grp_qf_name;
- /*
- * We do the test below only for project quotas. 'usrquota' and
- * 'grpquota' mount options are allowed even without quota feature
- * to support legacy quotas in quota files.
- */
- if (test_opt(sb, PRJQUOTA) && !ext4_has_feature_project(sb)) {
- ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
- "Cannot enable project quota enforcement.");
- return -EINVAL;
- }
- usr_qf_name = get_qf_name(sb, sbi, USRQUOTA);
- grp_qf_name = get_qf_name(sb, sbi, GRPQUOTA);
+
+ usr_qf_name = ctx->s_qf_names[USRQUOTA];
+ grp_qf_name = ctx->s_qf_names[GRPQUOTA];
+
if (usr_qf_name || grp_qf_name) {
- if (test_opt(sb, USRQUOTA) && usr_qf_name)
- clear_opt(sb, USRQUOTA);
+ if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) && usr_qf_name)
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
- if (test_opt(sb, GRPQUOTA) && grp_qf_name)
- clear_opt(sb, GRPQUOTA);
+ if (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) && grp_qf_name)
+ ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
- if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+ if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+ ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA)) {
ext4_msg(NULL, KERN_ERR, "old and new quota "
- "format mixing");
- return -EINVAL;
- }
-
- if (!sbi->s_jquota_fmt) {
- ext4_msg(NULL, KERN_ERR, "journaled quota format "
- "not specified");
+ "format mixing");
return -EINVAL;
}
}
#endif
- if (test_opt(sb, DIOREAD_NOLOCK)) {
- int blocksize =
- BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
- if (blocksize < PAGE_SIZE)
- ext4_msg(NULL, KERN_WARNING,
- "Warning: mounting with an experimental "
- "option 'dioread_nolock' for "
- "blocksize < PAGE_SIZE");
- }
- return 0;
+ return 1;
}
static inline void ext4_show_quota_options(struct seq_file *seq,