diff options
Diffstat (limited to 'fs/ext4/super.c')
| -rw-r--r-- | fs/ext4/super.c | 99 | 
1 files changed, 86 insertions, 13 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 0c4c2201b3aa..ba2396a7bd04 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -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  |