diff options
Diffstat (limited to 'fs/ext4/ialloc.c')
| -rw-r--r-- | fs/ext4/ialloc.c | 81 | 
1 files changed, 62 insertions, 19 deletions
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 9f9acac6c43..d48e8b14928 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -70,24 +70,27 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,  				       ext4_group_t block_group,  				       struct ext4_group_desc *gdp)  { -	struct ext4_sb_info *sbi = EXT4_SB(sb); -  	J_ASSERT_BH(bh, buffer_locked(bh));  	/* If checksum is bad mark all blocks and inodes use to prevent  	 * allocation, essentially implementing a per-group read-only flag. */ -	if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { +	if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) {  		ext4_error(sb, "Checksum bad for group %u", block_group);  		ext4_free_group_clusters_set(sb, gdp, 0);  		ext4_free_inodes_set(sb, gdp, 0);  		ext4_itable_unused_set(sb, gdp, 0);  		memset(bh->b_data, 0xff, sb->s_blocksize); +		ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh, +					   EXT4_INODES_PER_GROUP(sb) / 8);  		return 0;  	}  	memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8);  	ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8,  			bh->b_data); +	ext4_inode_bitmap_csum_set(sb, block_group, gdp, bh, +				   EXT4_INODES_PER_GROUP(sb) / 8); +	ext4_group_desc_csum_set(sb, block_group, gdp);  	return EXT4_INODES_PER_GROUP(sb);  } @@ -128,12 +131,12 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  		return NULL;  	}  	if (bitmap_uptodate(bh)) -		return bh; +		goto verify;  	lock_buffer(bh);  	if (bitmap_uptodate(bh)) {  		unlock_buffer(bh); -		return bh; +		goto verify;  	}  	ext4_lock_group(sb, block_group); @@ -141,6 +144,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  		ext4_init_inode_bitmap(sb, bh, block_group, desc);  		set_bitmap_uptodate(bh);  		set_buffer_uptodate(bh); +		set_buffer_verified(bh);  		ext4_unlock_group(sb, block_group);  		unlock_buffer(bh);  		return bh; @@ -154,7 +158,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  		 */  		set_bitmap_uptodate(bh);  		unlock_buffer(bh); -		return bh; +		goto verify;  	}  	/*  	 * submit the buffer_head for reading @@ -171,6 +175,20 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  			   block_group, bitmap_blk);  		return NULL;  	} + +verify: +	ext4_lock_group(sb, block_group); +	if (!buffer_verified(bh) && +	    !ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh, +					   EXT4_INODES_PER_GROUP(sb) / 8)) { +		ext4_unlock_group(sb, block_group); +		put_bh(bh); +		ext4_error(sb, "Corrupt inode bitmap - block_group = %u, " +			   "inode_bitmap = %llu", block_group, bitmap_blk); +		return NULL; +	} +	ext4_unlock_group(sb, block_group); +	set_buffer_verified(bh);  	return bh;  } @@ -276,7 +294,9 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)  		ext4_used_dirs_set(sb, gdp, count);  		percpu_counter_dec(&sbi->s_dirs_counter);  	} -	gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp); +	ext4_inode_bitmap_csum_set(sb, block_group, gdp, bitmap_bh, +				   EXT4_INODES_PER_GROUP(sb) / 8); +	ext4_group_desc_csum_set(sb, block_group, gdp);  	ext4_unlock_group(sb, block_group);  	percpu_counter_inc(&sbi->s_freeinodes_counter); @@ -488,10 +508,12 @@ fallback_retry:  	for (i = 0; i < ngroups; i++) {  		grp = (parent_group + i) % ngroups;  		desc = ext4_get_group_desc(sb, grp, NULL); -		grp_free = ext4_free_inodes_count(sb, desc); -		if (desc && grp_free && grp_free >= avefreei) { -			*group = grp; -			return 0; +		if (desc) { +			grp_free = ext4_free_inodes_count(sb, desc); +			if (grp_free && grp_free >= avefreei) { +				*group = grp; +				return 0; +			}  		}  	} @@ -709,7 +731,7 @@ repeat_in_this_group:  got:  	/* We may have to initialize the block bitmap if it isn't already */ -	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && +	if (ext4_has_group_desc_csum(sb) &&  	    gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {  		struct buffer_head *block_bitmap_bh; @@ -731,8 +753,11 @@ got:  			gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);  			ext4_free_group_clusters_set(sb, gdp,  				ext4_free_clusters_after_init(sb, group, gdp)); -			gdp->bg_checksum = ext4_group_desc_csum(sbi, group, -								gdp); +			ext4_block_bitmap_csum_set(sb, group, gdp, +						   block_bitmap_bh, +						   EXT4_BLOCKS_PER_GROUP(sb) / +						   8); +			ext4_group_desc_csum_set(sb, group, gdp);  		}  		ext4_unlock_group(sb, group); @@ -751,7 +776,7 @@ got:  		goto fail;  	/* Update the relevant bg descriptor fields */ -	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { +	if (ext4_has_group_desc_csum(sb)) {  		int free;  		struct ext4_group_info *grp = ext4_get_group_info(sb, group); @@ -772,7 +797,10 @@ got:  			ext4_itable_unused_set(sb, gdp,  					(EXT4_INODES_PER_GROUP(sb) - ino));  		up_read(&grp->alloc_sem); +	} else { +		ext4_lock_group(sb, group);  	} +  	ext4_free_inodes_set(sb, gdp, ext4_free_inodes_count(sb, gdp) - 1);  	if (S_ISDIR(mode)) {  		ext4_used_dirs_set(sb, gdp, ext4_used_dirs_count(sb, gdp) + 1); @@ -782,10 +810,12 @@ got:  			atomic_inc(&sbi->s_flex_groups[f].used_dirs);  		}  	} -	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { -		gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); -		ext4_unlock_group(sb, group); +	if (ext4_has_group_desc_csum(sb)) { +		ext4_inode_bitmap_csum_set(sb, group, gdp, inode_bitmap_bh, +					   EXT4_INODES_PER_GROUP(sb) / 8); +		ext4_group_desc_csum_set(sb, group, gdp);  	} +	ext4_unlock_group(sb, group);  	BUFFER_TRACE(inode_bitmap_bh, "call ext4_handle_dirty_metadata");  	err = ext4_handle_dirty_metadata(handle, NULL, inode_bitmap_bh); @@ -850,6 +880,19 @@ got:  	inode->i_generation = sbi->s_next_generation++;  	spin_unlock(&sbi->s_next_gen_lock); +	/* Precompute checksum seed for inode metadata */ +	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, +			EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) { +		__u32 csum; +		struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); +		__le32 inum = cpu_to_le32(inode->i_ino); +		__le32 gen = cpu_to_le32(inode->i_generation); +		csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum, +				   sizeof(inum)); +		ei->i_csum_seed = ext4_chksum(sbi, csum, (__u8 *)&gen, +					      sizeof(gen)); +	} +  	ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */  	ext4_set_inode_state(inode, EXT4_STATE_NEW); @@ -1140,7 +1183,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group,  skip_zeroout:  	ext4_lock_group(sb, group);  	gdp->bg_flags |= cpu_to_le16(EXT4_BG_INODE_ZEROED); -	gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); +	ext4_group_desc_csum_set(sb, group, gdp);  	ext4_unlock_group(sb, group);  	BUFFER_TRACE(group_desc_bh,  |