diff options
Diffstat (limited to 'fs/ext4/inode.c')
| -rw-r--r-- | fs/ext4/inode.c | 75 | 
1 files changed, 52 insertions, 23 deletions
| diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 1dc09ed5d403..94c7d2d828a6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -386,10 +386,11 @@ static int __check_block_validity(struct inode *inode, const char *func,  				unsigned int line,  				struct ext4_map_blocks *map)  { -	if (ext4_has_feature_journal(inode->i_sb) && -	    (inode->i_ino == -	     le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) +	journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; + +	if (journal && inode == journal->j_inode)  		return 0; +  	if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) {  		ext4_error_inode(inode, func, line, map->m_pblk,  				 "lblock %lu mapped to illegal pblock %llu " @@ -4724,22 +4725,43 @@ 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) - +static int check_igot_inode(struct inode *inode, ext4_iget_flags flags, +			    const char *function, unsigned int line)  { +	const char *err_str; +  	if (flags & EXT4_IGET_EA_INODE) { -		if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) -			return "missing EA_INODE flag"; +		if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { +			err_str = "missing EA_INODE flag"; +			goto error; +		}  		if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || -		    EXT4_I(inode)->i_file_acl) -			return "ea_inode with extended attributes"; +		    EXT4_I(inode)->i_file_acl) { +			err_str = "ea_inode with extended attributes"; +			goto error; +		}  	} else { -		if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) -			return "unexpected EA_INODE flag"; +		if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { +			/* +			 * open_by_handle_at() could provide an old inode number +			 * that has since been reused for an ea_inode; this does +			 * not indicate filesystem corruption +			 */ +			if (flags & EXT4_IGET_HANDLE) +				return -ESTALE; +			err_str = "unexpected EA_INODE flag"; +			goto error; +		} +	} +	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) { +		err_str = "unexpected bad inode w/o EXT4_IGET_BAD"; +		goto error;  	} -	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) -		return "unexpected bad inode w/o EXT4_IGET_BAD"; -	return NULL; +	return 0; + +error: +	ext4_error_inode(inode, function, line, 0, err_str); +	return -EFSCORRUPTED;  }  struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, @@ -4751,7 +4773,6 @@ 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; @@ -4780,10 +4801,10 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,  	if (!inode)  		return ERR_PTR(-ENOMEM);  	if (!(inode->i_state & I_NEW)) { -		if ((err_str = check_igot_inode(inode, flags)) != NULL) { -			ext4_error_inode(inode, function, line, 0, err_str); +		ret = check_igot_inode(inode, flags, function, line); +		if (ret) {  			iput(inode); -			return ERR_PTR(-EFSCORRUPTED); +			return ERR_PTR(ret);  		}  		return inode;  	} @@ -5065,13 +5086,21 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,  		ret = -EFSCORRUPTED;  		goto bad_inode;  	} -	if ((err_str = check_igot_inode(inode, flags)) != NULL) { -		ext4_error_inode(inode, function, line, 0, err_str); -		ret = -EFSCORRUPTED; -		goto bad_inode; +	ret = check_igot_inode(inode, flags, function, line); +	/* +	 * -ESTALE here means there is nothing inherently wrong with the inode, +	 * it's just not an inode we can return for an fhandle lookup. +	 */ +	if (ret == -ESTALE) { +		brelse(iloc.bh); +		unlock_new_inode(inode); +		iput(inode); +		return ERR_PTR(-ESTALE);  	} - +	if (ret) +		goto bad_inode;  	brelse(iloc.bh); +  	unlock_new_inode(inode);  	return inode; | 
