|
@@ -405,6 +405,9 @@ static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn)
|
|
|
|
|
|
static void ext4_handle_error(struct super_block *sb)
|
|
|
{
|
|
|
+ if (test_opt(sb, WARN_ON_ERROR))
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
+
|
|
|
if (sb_rdonly(sb))
|
|
|
return;
|
|
|
|
|
@@ -740,6 +743,9 @@ __acquires(bitlock)
|
|
|
va_end(args);
|
|
|
}
|
|
|
|
|
|
+ if (test_opt(sb, WARN_ON_ERROR))
|
|
|
+ WARN_ON_ONCE(1);
|
|
|
+
|
|
|
if (test_opt(sb, ERRORS_CONT)) {
|
|
|
ext4_commit_super(sb, 0);
|
|
|
return;
|
|
@@ -1371,7 +1377,8 @@ enum {
|
|
|
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
|
|
|
Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
|
|
|
Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_i_version, Opt_dax,
|
|
|
- Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit,
|
|
|
+ Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_warn_on_error,
|
|
|
+ Opt_nowarn_on_error, Opt_mblk_io_submit,
|
|
|
Opt_lazytime, Opt_nolazytime, Opt_debug_want_extra_isize,
|
|
|
Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity,
|
|
|
Opt_inode_readahead_blks, Opt_journal_ioprio,
|
|
@@ -1438,6 +1445,8 @@ static const match_table_t tokens = {
|
|
|
{Opt_dax, "dax"},
|
|
|
{Opt_stripe, "stripe=%u"},
|
|
|
{Opt_delalloc, "delalloc"},
|
|
|
+ {Opt_warn_on_error, "warn_on_error"},
|
|
|
+ {Opt_nowarn_on_error, "nowarn_on_error"},
|
|
|
{Opt_lazytime, "lazytime"},
|
|
|
{Opt_nolazytime, "nolazytime"},
|
|
|
{Opt_debug_want_extra_isize, "debug_want_extra_isize=%u"},
|
|
@@ -1602,6 +1611,8 @@ static const struct mount_opts {
|
|
|
MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT},
|
|
|
{Opt_nodelalloc, EXT4_MOUNT_DELALLOC,
|
|
|
MOPT_EXT4_ONLY | MOPT_CLEAR},
|
|
|
+ {Opt_warn_on_error, EXT4_MOUNT_WARN_ON_ERROR, MOPT_SET},
|
|
|
+ {Opt_nowarn_on_error, EXT4_MOUNT_WARN_ON_ERROR, MOPT_CLEAR},
|
|
|
{Opt_nojournal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM,
|
|
|
MOPT_EXT4_ONLY | MOPT_CLEAR},
|
|
|
{Opt_journal_checksum, EXT4_MOUNT_JOURNAL_CHECKSUM,
|
|
@@ -2331,6 +2342,7 @@ static int ext4_check_descriptors(struct super_block *sb,
|
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
ext4_fsblk_t first_block = le32_to_cpu(sbi->s_es->s_first_data_block);
|
|
|
ext4_fsblk_t last_block;
|
|
|
+ ext4_fsblk_t last_bg_block = sb_block + ext4_bg_num_gdb(sb, 0) + 1;
|
|
|
ext4_fsblk_t block_bitmap;
|
|
|
ext4_fsblk_t inode_bitmap;
|
|
|
ext4_fsblk_t inode_table;
|
|
@@ -2363,6 +2375,14 @@ static int ext4_check_descriptors(struct super_block *sb,
|
|
|
if (!sb_rdonly(sb))
|
|
|
return 0;
|
|
|
}
|
|
|
+ if (block_bitmap >= sb_block + 1 &&
|
|
|
+ block_bitmap <= last_bg_block) {
|
|
|
+ ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
|
|
+ "Block bitmap for group %u overlaps "
|
|
|
+ "block group descriptors", i);
|
|
|
+ if (!sb_rdonly(sb))
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
if (block_bitmap < first_block || block_bitmap > last_block) {
|
|
|
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
|
|
"Block bitmap for group %u not in group "
|
|
@@ -2377,6 +2397,14 @@ static int ext4_check_descriptors(struct super_block *sb,
|
|
|
if (!sb_rdonly(sb))
|
|
|
return 0;
|
|
|
}
|
|
|
+ if (inode_bitmap >= sb_block + 1 &&
|
|
|
+ inode_bitmap <= last_bg_block) {
|
|
|
+ ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
|
|
+ "Inode bitmap for group %u overlaps "
|
|
|
+ "block group descriptors", i);
|
|
|
+ if (!sb_rdonly(sb))
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
if (inode_bitmap < first_block || inode_bitmap > last_block) {
|
|
|
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
|
|
"Inode bitmap for group %u not in group "
|
|
@@ -2391,6 +2419,14 @@ static int ext4_check_descriptors(struct super_block *sb,
|
|
|
if (!sb_rdonly(sb))
|
|
|
return 0;
|
|
|
}
|
|
|
+ if (inode_table >= sb_block + 1 &&
|
|
|
+ inode_table <= last_bg_block) {
|
|
|
+ ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
|
|
+ "Inode table for group %u overlaps "
|
|
|
+ "block group descriptors", i);
|
|
|
+ if (!sb_rdonly(sb))
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
if (inode_table < first_block ||
|
|
|
inode_table + sbi->s_itb_per_group - 1 > last_block) {
|
|
|
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
|
|
@@ -3097,13 +3133,22 @@ static ext4_group_t ext4_has_uninit_itable(struct super_block *sb)
|
|
|
ext4_group_t group, ngroups = EXT4_SB(sb)->s_groups_count;
|
|
|
struct ext4_group_desc *gdp = NULL;
|
|
|
|
|
|
+ if (!ext4_has_group_desc_csum(sb))
|
|
|
+ return ngroups;
|
|
|
+
|
|
|
for (group = 0; group < ngroups; group++) {
|
|
|
gdp = ext4_get_group_desc(sb, group, NULL);
|
|
|
if (!gdp)
|
|
|
continue;
|
|
|
|
|
|
- if (!(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED)))
|
|
|
+ if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED))
|
|
|
+ continue;
|
|
|
+ if (group != 0)
|
|
|
break;
|
|
|
+ ext4_error(sb, "Inode table for bg 0 marked as "
|
|
|
+ "needing zeroing");
|
|
|
+ if (sb_rdonly(sb))
|
|
|
+ return ngroups;
|
|
|
}
|
|
|
|
|
|
return group;
|
|
@@ -3742,6 +3787,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
le32_to_cpu(es->s_log_block_size));
|
|
|
goto failed_mount;
|
|
|
}
|
|
|
+ if (le32_to_cpu(es->s_log_cluster_size) >
|
|
|
+ (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
|
|
|
+ ext4_msg(sb, KERN_ERR,
|
|
|
+ "Invalid log cluster size: %u",
|
|
|
+ le32_to_cpu(es->s_log_cluster_size));
|
|
|
+ goto failed_mount;
|
|
|
+ }
|
|
|
|
|
|
if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {
|
|
|
ext4_msg(sb, KERN_ERR,
|
|
@@ -3806,6 +3858,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
} else {
|
|
|
sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
|
|
|
sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
|
|
|
+ if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) {
|
|
|
+ ext4_msg(sb, KERN_ERR, "invalid first ino: %u",
|
|
|
+ sbi->s_first_ino);
|
|
|
+ goto failed_mount;
|
|
|
+ }
|
|
|
if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) ||
|
|
|
(!is_power_of_2(sbi->s_inode_size)) ||
|
|
|
(sbi->s_inode_size > blocksize)) {
|
|
@@ -3882,13 +3939,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
"block size (%d)", clustersize, blocksize);
|
|
|
goto failed_mount;
|
|
|
}
|
|
|
- if (le32_to_cpu(es->s_log_cluster_size) >
|
|
|
- (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
|
|
|
- ext4_msg(sb, KERN_ERR,
|
|
|
- "Invalid log cluster size: %u",
|
|
|
- le32_to_cpu(es->s_log_cluster_size));
|
|
|
- goto failed_mount;
|
|
|
- }
|
|
|
sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -
|
|
|
le32_to_cpu(es->s_log_block_size);
|
|
|
sbi->s_clusters_per_group =
|
|
@@ -3909,10 +3959,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
}
|
|
|
} else {
|
|
|
if (clustersize != blocksize) {
|
|
|
- ext4_warning(sb, "fragment/cluster size (%d) != "
|
|
|
- "block size (%d)", clustersize,
|
|
|
- blocksize);
|
|
|
- clustersize = blocksize;
|
|
|
+ ext4_msg(sb, KERN_ERR,
|
|
|
+ "fragment/cluster size (%d) != "
|
|
|
+ "block size (%d)", clustersize, blocksize);
|
|
|
+ goto failed_mount;
|
|
|
}
|
|
|
if (sbi->s_blocks_per_group > blocksize * 8) {
|
|
|
ext4_msg(sb, KERN_ERR,
|
|
@@ -3966,6 +4016,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
ext4_blocks_count(es));
|
|
|
goto failed_mount;
|
|
|
}
|
|
|
+ if ((es->s_first_data_block == 0) && (es->s_log_block_size == 0) &&
|
|
|
+ (sbi->s_cluster_ratio == 1)) {
|
|
|
+ ext4_msg(sb, KERN_WARNING, "bad geometry: first data "
|
|
|
+ "block is 0 with a 1k block and cluster size");
|
|
|
+ goto failed_mount;
|
|
|
+ }
|
|
|
+
|
|
|
blocks_count = (ext4_blocks_count(es) -
|
|
|
le32_to_cpu(es->s_first_data_block) +
|
|
|
EXT4_BLOCKS_PER_GROUP(sb) - 1);
|
|
@@ -4001,6 +4058,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
ret = -ENOMEM;
|
|
|
goto failed_mount;
|
|
|
}
|
|
|
+ if (((u64)sbi->s_groups_count * sbi->s_inodes_per_group) !=
|
|
|
+ le32_to_cpu(es->s_inodes_count)) {
|
|
|
+ ext4_msg(sb, KERN_ERR, "inodes count not valid: %u vs %llu",
|
|
|
+ le32_to_cpu(es->s_inodes_count),
|
|
|
+ ((u64)sbi->s_groups_count * sbi->s_inodes_per_group));
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto failed_mount;
|
|
|
+ }
|
|
|
|
|
|
bgl_lock_init(sbi->s_blockgroup_lock);
|
|
|
|
|
@@ -4736,6 +4801,14 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
|
|
|
|
|
if (!sbh || block_device_ejected(sb))
|
|
|
return error;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The superblock bh should be mapped, but it might not be if the
|
|
|
+ * device was hot-removed. Not much we can do but fail the I/O.
|
|
|
+ */
|
|
|
+ if (!buffer_mapped(sbh))
|
|
|
+ return error;
|
|
|
+
|
|
|
/*
|
|
|
* If the file system is mounted read-only, don't update the
|
|
|
* superblock write time. This avoids updating the superblock
|