diff options
Diffstat (limited to 'fs/ext4/inode.c')
| -rw-r--r-- | fs/ext4/inode.c | 54 | 
1 files changed, 40 insertions, 14 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0d5ba922e411..02de439bf1f0 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2783,11 +2783,12 @@ static int ext4_writepages(struct address_space *mapping,  		.can_map = 1,  	};  	int ret; +	int alloc_ctx;  	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))  		return -EIO; -	percpu_down_read(&EXT4_SB(sb)->s_writepages_rwsem); +	alloc_ctx = ext4_writepages_down_read(sb);  	ret = ext4_do_writepages(&mpd);  	/*  	 * For data=journal writeback we could have come across pages marked @@ -2796,7 +2797,7 @@ static int ext4_writepages(struct address_space *mapping,  	 */  	if (!ret && mpd.journalled_more_data)  		ret = ext4_do_writepages(&mpd); -	percpu_up_read(&EXT4_SB(sb)->s_writepages_rwsem); +	ext4_writepages_up_read(sb, alloc_ctx);  	return ret;  } @@ -2824,17 +2825,18 @@ static int ext4_dax_writepages(struct address_space *mapping,  	long nr_to_write = wbc->nr_to_write;  	struct inode *inode = mapping->host;  	struct ext4_sb_info *sbi = EXT4_SB(mapping->host->i_sb); +	int alloc_ctx;  	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))  		return -EIO; -	percpu_down_read(&sbi->s_writepages_rwsem); +	alloc_ctx = ext4_writepages_down_read(inode->i_sb);  	trace_ext4_writepages(inode, wbc);  	ret = dax_writeback_mapping_range(mapping, sbi->s_daxdev, wbc);  	trace_ext4_writepages_result(inode, wbc, ret,  				     nr_to_write - wbc->nr_to_write); -	percpu_up_read(&sbi->s_writepages_rwsem); +	ext4_writepages_up_read(inode->i_sb, alloc_ctx);  	return ret;  } @@ -3375,7 +3377,7 @@ static int ext4_iomap_overwrite_begin(struct inode *inode, loff_t offset,  	 */  	flags &= ~IOMAP_WRITE;  	ret = ext4_iomap_begin(inode, offset, length, flags, iomap, srcmap); -	WARN_ON_ONCE(iomap->type != IOMAP_MAPPED); +	WARN_ON_ONCE(!ret && iomap->type != IOMAP_MAPPED);  	return ret;  } @@ -4639,6 +4641,24 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val)  		inode_set_iversion_queried(inode, val);  } +static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags) + +{ +	if (flags & EXT4_IGET_EA_INODE) { +		if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) +			return "missing EA_INODE flag"; +		if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || +		    EXT4_I(inode)->i_file_acl) +			return "ea_inode with extended attributes"; +	} else { +		if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) +			return "unexpected EA_INODE flag"; +	} +	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) +		return "unexpected bad inode w/o EXT4_IGET_BAD"; +	return NULL; +} +  struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,  			  ext4_iget_flags flags, const char *function,  			  unsigned int line) @@ -4648,6 +4668,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,  	struct ext4_inode_info *ei;  	struct ext4_super_block *es = EXT4_SB(sb)->s_es;  	struct inode *inode; +	const char *err_str;  	journal_t *journal = EXT4_SB(sb)->s_journal;  	long ret;  	loff_t size; @@ -4675,8 +4696,14 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,  	inode = iget_locked(sb, ino);  	if (!inode)  		return ERR_PTR(-ENOMEM); -	if (!(inode->i_state & I_NEW)) +	if (!(inode->i_state & I_NEW)) { +		if ((err_str = check_igot_inode(inode, flags)) != NULL) { +			ext4_error_inode(inode, function, line, 0, err_str); +			iput(inode); +			return ERR_PTR(-EFSCORRUPTED); +		}  		return inode; +	}  	ei = EXT4_I(inode);  	iloc.bh = NULL; @@ -4942,10 +4969,9 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,  	if (IS_CASEFOLDED(inode) && !ext4_has_feature_casefold(inode->i_sb))  		ext4_error_inode(inode, function, line, 0,  				 "casefold flag without casefold feature"); -	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) { -		ext4_error_inode(inode, function, line, 0, -				 "bad inode without EXT4_IGET_BAD flag"); -		ret = -EUCLEAN; +	if ((err_str = check_igot_inode(inode, flags)) != NULL) { +		ext4_error_inode(inode, function, line, 0, err_str); +		ret = -EFSCORRUPTED;  		goto bad_inode;  	} @@ -5928,7 +5954,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)  	journal_t *journal;  	handle_t *handle;  	int err; -	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); +	int alloc_ctx;  	/*  	 * We have to be very careful here: changing a data block's @@ -5966,7 +5992,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)  		}  	} -	percpu_down_write(&sbi->s_writepages_rwsem); +	alloc_ctx = ext4_writepages_down_write(inode->i_sb);  	jbd2_journal_lock_updates(journal);  	/* @@ -5983,7 +6009,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)  		err = jbd2_journal_flush(journal, 0);  		if (err < 0) {  			jbd2_journal_unlock_updates(journal); -			percpu_up_write(&sbi->s_writepages_rwsem); +			ext4_writepages_up_write(inode->i_sb, alloc_ctx);  			return err;  		}  		ext4_clear_inode_flag(inode, EXT4_INODE_JOURNAL_DATA); @@ -5991,7 +6017,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)  	ext4_set_aops(inode);  	jbd2_journal_unlock_updates(journal); -	percpu_up_write(&sbi->s_writepages_rwsem); +	ext4_writepages_up_write(inode->i_sb, alloc_ctx);  	if (val)  		filemap_invalidate_unlock(inode->i_mapping);  | 
