diff options
Diffstat (limited to 'fs/ext4/ialloc.c')
| -rw-r--r-- | fs/ext4/ialloc.c | 113 | 
1 files changed, 75 insertions, 38 deletions
| diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 619bfc1fda8c..1b8024d26f65 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -64,7 +64,7 @@ void ext4_mark_bitmap_end(int start_bit, int end_bit, char *bitmap)  }  /* Initializes an uninitialized inode bitmap */ -static unsigned ext4_init_inode_bitmap(struct super_block *sb, +static int ext4_init_inode_bitmap(struct super_block *sb,  				       struct buffer_head *bh,  				       ext4_group_t block_group,  				       struct ext4_group_desc *gdp) @@ -89,7 +89,7 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,  					   count);  		}  		set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state); -		return 0; +		return -EFSBADCRC;  	}  	memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8); @@ -99,7 +99,7 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb,  				   EXT4_INODES_PER_GROUP(sb) / 8);  	ext4_group_desc_csum_set(sb, block_group, gdp); -	return EXT4_INODES_PER_GROUP(sb); +	return 0;  }  void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate) @@ -112,6 +112,42 @@ void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate)  	put_bh(bh);  } +static int ext4_validate_inode_bitmap(struct super_block *sb, +				      struct ext4_group_desc *desc, +				      ext4_group_t block_group, +				      struct buffer_head *bh) +{ +	ext4_fsblk_t	blk; +	struct ext4_group_info *grp = ext4_get_group_info(sb, block_group); +	struct ext4_sb_info *sbi = EXT4_SB(sb); + +	if (buffer_verified(bh)) +		return 0; +	if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) +		return -EFSCORRUPTED; + +	ext4_lock_group(sb, block_group); +	blk = ext4_inode_bitmap(sb, desc); +	if (!ext4_inode_bitmap_csum_verify(sb, block_group, desc, bh, +					   EXT4_INODES_PER_GROUP(sb) / 8)) { +		ext4_unlock_group(sb, block_group); +		ext4_error(sb, "Corrupt inode bitmap - block_group = %u, " +			   "inode_bitmap = %llu", block_group, blk); +		grp = ext4_get_group_info(sb, block_group); +		if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) { +			int count; +			count = ext4_free_inodes_count(sb, desc); +			percpu_counter_sub(&sbi->s_freeinodes_counter, +					   count); +		} +		set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state); +		return -EFSBADCRC; +	} +	set_buffer_verified(bh); +	ext4_unlock_group(sb, block_group); +	return 0; +} +  /*   * Read the inode allocation bitmap for a given block_group, reading   * into the specified slot in the superblock's bitmap cache. @@ -124,12 +160,11 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  	struct ext4_group_desc *desc;  	struct buffer_head *bh = NULL;  	ext4_fsblk_t bitmap_blk; -	struct ext4_group_info *grp; -	struct ext4_sb_info *sbi = EXT4_SB(sb); +	int err;  	desc = ext4_get_group_desc(sb, block_group, NULL);  	if (!desc) -		return NULL; +		return ERR_PTR(-EFSCORRUPTED);  	bitmap_blk = ext4_inode_bitmap(sb, desc);  	bh = sb_getblk(sb, bitmap_blk); @@ -137,7 +172,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  		ext4_error(sb, "Cannot read inode bitmap - "  			    "block_group = %u, inode_bitmap = %llu",  			    block_group, bitmap_blk); -		return NULL; +		return ERR_PTR(-EIO);  	}  	if (bitmap_uptodate(bh))  		goto verify; @@ -150,12 +185,14 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  	ext4_lock_group(sb, block_group);  	if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { -		ext4_init_inode_bitmap(sb, bh, block_group, desc); +		err = 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); +		if (err) +			goto out;  		return bh;  	}  	ext4_unlock_group(sb, block_group); @@ -182,31 +219,17 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)  		ext4_error(sb, "Cannot read inode bitmap - "  			   "block_group = %u, inode_bitmap = %llu",  			   block_group, bitmap_blk); -		return NULL; +		return ERR_PTR(-EIO);  	}  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); -		grp = ext4_get_group_info(sb, block_group); -		if (!EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) { -			int count; -			count = ext4_free_inodes_count(sb, desc); -			percpu_counter_sub(&sbi->s_freeinodes_counter, -					   count); -		} -		set_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &grp->bb_state); -		return NULL; -	} -	ext4_unlock_group(sb, block_group); -	set_buffer_verified(bh); +	err = ext4_validate_inode_bitmap(sb, desc, block_group, bh); +	if (err) +		goto out;  	return bh; +out: +	put_bh(bh); +	return ERR_PTR(err);  }  /* @@ -286,8 +309,15 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)  	bitmap_bh = ext4_read_inode_bitmap(sb, block_group);  	/* Don't bother if the inode bitmap is corrupt. */  	grp = ext4_get_group_info(sb, block_group); -	if (unlikely(EXT4_MB_GRP_IBITMAP_CORRUPT(grp)) || !bitmap_bh) +	if (IS_ERR(bitmap_bh)) { +		fatal = PTR_ERR(bitmap_bh); +		bitmap_bh = NULL; +		goto error_return; +	} +	if (unlikely(EXT4_MB_GRP_IBITMAP_CORRUPT(grp))) { +		fatal = -EFSCORRUPTED;  		goto error_return; +	}  	BUFFER_TRACE(bitmap_bh, "get_write_access");  	fatal = ext4_journal_get_write_access(handle, bitmap_bh); @@ -826,7 +856,9 @@ got_group:  		brelse(inode_bitmap_bh);  		inode_bitmap_bh = ext4_read_inode_bitmap(sb, group);  		/* Skip groups with suspicious inode tables */ -		if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp) || !inode_bitmap_bh) { +		if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp) || +		    IS_ERR(inode_bitmap_bh)) { +			inode_bitmap_bh = NULL;  			if (++group == ngroups)  				group = 0;  			continue; @@ -902,8 +934,8 @@ got:  		struct buffer_head *block_bitmap_bh;  		block_bitmap_bh = ext4_read_block_bitmap(sb, group); -		if (!block_bitmap_bh) { -			err = -EIO; +		if (IS_ERR(block_bitmap_bh)) { +			err = PTR_ERR(block_bitmap_bh);  			goto out;  		}  		BUFFER_TRACE(block_bitmap_bh, "get block bitmap access"); @@ -1045,7 +1077,7 @@ got:  	ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;  	ei->i_inline_off = 0; -	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_INLINE_DATA)) +	if (ext4_has_feature_inline_data(sb))  		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);  	ret = inode;  	err = dquot_alloc_inode(inode); @@ -1060,7 +1092,7 @@ got:  	if (err)  		goto fail_free_drop; -	if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) { +	if (ext4_has_feature_extents(sb)) {  		/* set extent flag only for directory, file and normal symlink*/  		if (S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) {  			ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS); @@ -1116,14 +1148,17 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)  	/* Error cases - e2fsck has already cleaned up for us */  	if (ino > max_ino) {  		ext4_warning(sb, "bad orphan ino %lu!  e2fsck was run?", ino); +		err = -EFSCORRUPTED;  		goto error;  	}  	block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);  	bit = (ino - 1) % EXT4_INODES_PER_GROUP(sb);  	bitmap_bh = ext4_read_inode_bitmap(sb, block_group); -	if (!bitmap_bh) { -		ext4_warning(sb, "inode bitmap error for orphan %lu", ino); +	if (IS_ERR(bitmap_bh)) { +		err = PTR_ERR(bitmap_bh); +		ext4_warning(sb, "inode bitmap error %ld for orphan %lu", +			     ino, err);  		goto error;  	} @@ -1198,8 +1233,10 @@ unsigned long ext4_count_free_inodes(struct super_block *sb)  		desc_count += ext4_free_inodes_count(sb, gdp);  		brelse(bitmap_bh);  		bitmap_bh = ext4_read_inode_bitmap(sb, i); -		if (!bitmap_bh) +		if (IS_ERR(bitmap_bh)) { +			bitmap_bh = NULL;  			continue; +		}  		x = ext4_count_free(bitmap_bh->b_data,  				    EXT4_INODES_PER_GROUP(sb) / 8); |