diff options
Diffstat (limited to 'fs/xfs/xfs_vnodeops.c')
| -rw-r--r-- | fs/xfs/xfs_vnodeops.c | 285 | 
1 files changed, 83 insertions, 202 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index b6a82d817a8..2a5c637344b 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -146,11 +146,6 @@ xfs_readlink(  }  /* - * Flags for xfs_free_eofblocks - */ -#define XFS_FREE_EOF_TRYLOCK	(1<<0) - -/*   * This is called by xfs_inactive to free any blocks beyond eof   * when the link count isn't zero and by xfs_dm_punch_hole() when   * punching a hole to EOF. @@ -159,7 +154,7 @@ STATIC int  xfs_free_eofblocks(  	xfs_mount_t	*mp,  	xfs_inode_t	*ip, -	int		flags) +	bool		need_iolock)  {  	xfs_trans_t	*tp;  	int		error; @@ -174,7 +169,7 @@ xfs_free_eofblocks(  	 * of the file.  If not, then there is nothing to do.  	 */  	end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_ISIZE(ip)); -	last_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp)); +	last_fsb = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);  	if (last_fsb <= end_fsb)  		return 0;  	map_len = last_fsb - end_fsb; @@ -201,13 +196,11 @@ xfs_free_eofblocks(  		 */  		tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE); -		if (flags & XFS_FREE_EOF_TRYLOCK) { +		if (need_iolock) {  			if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {  				xfs_trans_cancel(tp, 0);  				return 0;  			} -		} else { -			xfs_ilock(ip, XFS_IOLOCK_EXCL);  		}  		error = xfs_trans_reserve(tp, 0, @@ -217,7 +210,8 @@ xfs_free_eofblocks(  		if (error) {  			ASSERT(XFS_FORCED_SHUTDOWN(mp));  			xfs_trans_cancel(tp, 0); -			xfs_iunlock(ip, XFS_IOLOCK_EXCL); +			if (need_iolock) +				xfs_iunlock(ip, XFS_IOLOCK_EXCL);  			return error;  		} @@ -244,7 +238,10 @@ xfs_free_eofblocks(  			error = xfs_trans_commit(tp,  						XFS_TRANS_RELEASE_LOG_RES);  		} -		xfs_iunlock(ip, XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL); + +		xfs_iunlock(ip, XFS_ILOCK_EXCL); +		if (need_iolock) +			xfs_iunlock(ip, XFS_IOLOCK_EXCL);  	}  	return error;  } @@ -282,23 +279,15 @@ xfs_inactive_symlink_rmt(  	 * free them all in one bunmapi call.  	 */  	ASSERT(ip->i_d.di_nextents > 0 && ip->i_d.di_nextents <= 2); -	if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, -			XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT))) { -		ASSERT(XFS_FORCED_SHUTDOWN(mp)); -		xfs_trans_cancel(tp, 0); -		*tpp = NULL; -		return error; -	} +  	/*  	 * Lock the inode, fix the size, and join it to the transaction.  	 * Hold it so in the normal path, we still have it locked for  	 * the second transaction.  In the error paths we need it  	 * held so the cancel won't rele it, see below.  	 */ -	xfs_ilock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);  	size = (int)ip->i_d.di_size;  	ip->i_d.di_size = 0; -	xfs_trans_ijoin(tp, ip, 0);  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);  	/*  	 * Find the block(s) so we can inval and unmap them. @@ -385,114 +374,14 @@ xfs_inactive_symlink_rmt(  		ASSERT(XFS_FORCED_SHUTDOWN(mp));  		goto error0;  	} -	/* -	 * Return with the inode locked but not joined to the transaction. -	 */ + +	xfs_trans_ijoin(tp, ip, 0);  	*tpp = tp;  	return 0;   error1:  	xfs_bmap_cancel(&free_list);   error0: -	/* -	 * Have to come here with the inode locked and either -	 * (held and in the transaction) or (not in the transaction). -	 * If the inode isn't held then cancel would iput it, but -	 * that's wrong since this is inactive and the vnode ref -	 * count is 0 already. -	 * Cancel won't do anything to the inode if held, but it still -	 * needs to be locked until the cancel is done, if it was -	 * joined to the transaction. -	 */ -	xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); -	xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); -	*tpp = NULL; -	return error; - -} - -STATIC int -xfs_inactive_symlink_local( -	xfs_inode_t	*ip, -	xfs_trans_t	**tpp) -{ -	int		error; - -	ASSERT(ip->i_d.di_size <= XFS_IFORK_DSIZE(ip)); -	/* -	 * We're freeing a symlink which fit into -	 * the inode.  Just free the memory used -	 * to hold the old symlink. -	 */ -	error = xfs_trans_reserve(*tpp, 0, -				  XFS_ITRUNCATE_LOG_RES(ip->i_mount), -				  0, XFS_TRANS_PERM_LOG_RES, -				  XFS_ITRUNCATE_LOG_COUNT); - -	if (error) { -		xfs_trans_cancel(*tpp, 0); -		*tpp = NULL; -		return error; -	} -	xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - -	/* -	 * Zero length symlinks _can_ exist. -	 */ -	if (ip->i_df.if_bytes > 0) { -		xfs_idata_realloc(ip, -				  -(ip->i_df.if_bytes), -				  XFS_DATA_FORK); -		ASSERT(ip->i_df.if_bytes == 0); -	} -	return 0; -} - -STATIC int -xfs_inactive_attrs( -	xfs_inode_t	*ip, -	xfs_trans_t	**tpp) -{ -	xfs_trans_t	*tp; -	int		error; -	xfs_mount_t	*mp; - -	ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); -	tp = *tpp; -	mp = ip->i_mount; -	ASSERT(ip->i_d.di_forkoff != 0); -	error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); -	xfs_iunlock(ip, XFS_ILOCK_EXCL); -	if (error) -		goto error_unlock; - -	error = xfs_attr_inactive(ip); -	if (error) -		goto error_unlock; - -	tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE); -	error = xfs_trans_reserve(tp, 0, -				  XFS_IFREE_LOG_RES(mp), -				  0, XFS_TRANS_PERM_LOG_RES, -				  XFS_INACTIVE_LOG_COUNT); -	if (error) -		goto error_cancel; - -	xfs_ilock(ip, XFS_ILOCK_EXCL); -	xfs_trans_ijoin(tp, ip, 0); -	xfs_idestroy_fork(ip, XFS_ATTR_FORK); - -	ASSERT(ip->i_d.di_anextents == 0); - -	*tpp = tp; -	return 0; - -error_cancel: -	ASSERT(XFS_FORCED_SHUTDOWN(mp)); -	xfs_trans_cancel(tp, 0); -error_unlock: -	*tpp = NULL; -	xfs_iunlock(ip, XFS_IOLOCK_EXCL);  	return error;  } @@ -574,8 +463,7 @@ xfs_release(  		if (xfs_iflags_test(ip, XFS_IDIRTY_RELEASE))  			return 0; -		error = xfs_free_eofblocks(mp, ip, -					   XFS_FREE_EOF_TRYLOCK); +		error = xfs_free_eofblocks(mp, ip, true);  		if (error)  			return error; @@ -604,7 +492,7 @@ xfs_inactive(  	xfs_trans_t	*tp;  	xfs_mount_t	*mp;  	int		error; -	int		truncate; +	int		truncate = 0;  	/*  	 * If the inode is already free, then there can be nothing @@ -616,17 +504,6 @@ xfs_inactive(  		return VN_INACTIVE_CACHE;  	} -	/* -	 * Only do a truncate if it's a regular file with -	 * some actual space in it.  It's OK to look at the -	 * inode's fields without the lock because we're the -	 * only one with a reference to the inode. -	 */ -	truncate = ((ip->i_d.di_nlink == 0) && -	    ((ip->i_d.di_size != 0) || XFS_ISIZE(ip) != 0 || -	     (ip->i_d.di_nextents > 0) || (ip->i_delayed_blks > 0)) && -	    S_ISREG(ip->i_d.di_mode)); -  	mp = ip->i_mount;  	error = 0; @@ -643,99 +520,100 @@ xfs_inactive(  		    (!(ip->i_d.di_flags &  				(XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) ||  		     ip->i_delayed_blks != 0))) { -			error = xfs_free_eofblocks(mp, ip, 0); +			error = xfs_free_eofblocks(mp, ip, false);  			if (error)  				return VN_INACTIVE_CACHE;  		}  		goto out;  	} -	ASSERT(ip->i_d.di_nlink == 0); +	if (S_ISREG(ip->i_d.di_mode) && +	    (ip->i_d.di_size != 0 || XFS_ISIZE(ip) != 0 || +	     ip->i_d.di_nextents > 0 || ip->i_delayed_blks > 0)) +		truncate = 1;  	error = xfs_qm_dqattach(ip, 0);  	if (error)  		return VN_INACTIVE_CACHE;  	tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE); -	if (truncate) { -		xfs_ilock(ip, XFS_IOLOCK_EXCL); - -		error = xfs_trans_reserve(tp, 0, -					  XFS_ITRUNCATE_LOG_RES(mp), -					  0, XFS_TRANS_PERM_LOG_RES, -					  XFS_ITRUNCATE_LOG_COUNT); -		if (error) { -			/* Don't call itruncate_cleanup */ -			ASSERT(XFS_FORCED_SHUTDOWN(mp)); -			xfs_trans_cancel(tp, 0); -			xfs_iunlock(ip, XFS_IOLOCK_EXCL); -			return VN_INACTIVE_CACHE; -		} +	error = xfs_trans_reserve(tp, 0, +			(truncate || S_ISLNK(ip->i_d.di_mode)) ? +				XFS_ITRUNCATE_LOG_RES(mp) : +				XFS_IFREE_LOG_RES(mp), +			0, +			XFS_TRANS_PERM_LOG_RES, +			XFS_ITRUNCATE_LOG_COUNT); +	if (error) { +		ASSERT(XFS_FORCED_SHUTDOWN(mp)); +		xfs_trans_cancel(tp, 0); +		return VN_INACTIVE_CACHE; +	} -		xfs_ilock(ip, XFS_ILOCK_EXCL); -		xfs_trans_ijoin(tp, ip, 0); +	xfs_ilock(ip, XFS_ILOCK_EXCL); +	xfs_trans_ijoin(tp, ip, 0); +	if (S_ISLNK(ip->i_d.di_mode)) { +		/* +		 * Zero length symlinks _can_ exist. +		 */ +		if (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) { +			error = xfs_inactive_symlink_rmt(ip, &tp); +			if (error) +				goto out_cancel; +		} else if (ip->i_df.if_bytes > 0) { +			xfs_idata_realloc(ip, -(ip->i_df.if_bytes), +					  XFS_DATA_FORK); +			ASSERT(ip->i_df.if_bytes == 0); +		} +	} else if (truncate) {  		ip->i_d.di_size = 0;  		xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);  		error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0); -		if (error) { -			xfs_trans_cancel(tp, -				XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); -			xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); -			return VN_INACTIVE_CACHE; -		} +		if (error) +			goto out_cancel;  		ASSERT(ip->i_d.di_nextents == 0); -	} else if (S_ISLNK(ip->i_d.di_mode)) { +	} -		/* -		 * If we get an error while cleaning up a -		 * symlink we bail out. -		 */ -		error = (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) ? -			xfs_inactive_symlink_rmt(ip, &tp) : -			xfs_inactive_symlink_local(ip, &tp); +	/* +	 * If there are attributes associated with the file then blow them away +	 * now.  The code calls a routine that recursively deconstructs the +	 * attribute fork.  We need to just commit the current transaction +	 * because we can't use it for xfs_attr_inactive(). +	 */ +	if (ip->i_d.di_anextents > 0) { +		ASSERT(ip->i_d.di_forkoff != 0); -		if (error) { -			ASSERT(tp == NULL); -			return VN_INACTIVE_CACHE; -		} +		error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); +		if (error) +			goto out_unlock; -		xfs_trans_ijoin(tp, ip, 0); -	} else { +		xfs_iunlock(ip, XFS_ILOCK_EXCL); + +		error = xfs_attr_inactive(ip); +		if (error) +			goto out; + +		tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);  		error = xfs_trans_reserve(tp, 0,  					  XFS_IFREE_LOG_RES(mp),  					  0, XFS_TRANS_PERM_LOG_RES,  					  XFS_INACTIVE_LOG_COUNT);  		if (error) { -			ASSERT(XFS_FORCED_SHUTDOWN(mp));  			xfs_trans_cancel(tp, 0); -			return VN_INACTIVE_CACHE; +			goto out;  		} -		xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); +		xfs_ilock(ip, XFS_ILOCK_EXCL);  		xfs_trans_ijoin(tp, ip, 0);  	} -	/* -	 * If there are attributes associated with the file -	 * then blow them away now.  The code calls a routine -	 * that recursively deconstructs the attribute fork. -	 * We need to just commit the current transaction -	 * because we can't use it for xfs_attr_inactive(). -	 */ -	if (ip->i_d.di_anextents > 0) { -		error = xfs_inactive_attrs(ip, &tp); -		/* -		 * If we got an error, the transaction is already -		 * cancelled, and the inode is unlocked. Just get out. -		 */ -		 if (error) -			 return VN_INACTIVE_CACHE; -	} else if (ip->i_afp) { +	if (ip->i_afp)  		xfs_idestroy_fork(ip, XFS_ATTR_FORK); -	} + +	ASSERT(ip->i_d.di_anextents == 0);  	/*  	 * Free the inode. @@ -779,10 +657,13 @@ xfs_inactive(  	 * Release the dquots held by inode, if any.  	 */  	xfs_qm_dqdetach(ip); -	xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); - - out: +out_unlock: +	xfs_iunlock(ip, XFS_ILOCK_EXCL); +out:  	return VN_INACTIVE_CACHE; +out_cancel: +	xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); +	goto out_unlock;  }  /* @@ -2262,10 +2143,10 @@ xfs_change_file_space(  	llen = bf->l_len > 0 ? bf->l_len - 1 : bf->l_len; -	if (   (bf->l_start < 0) -	    || (bf->l_start > XFS_MAXIOFFSET(mp)) -	    || (bf->l_start + llen < 0) -	    || (bf->l_start + llen > XFS_MAXIOFFSET(mp))) +	if (bf->l_start < 0 || +	    bf->l_start > mp->m_super->s_maxbytes || +	    bf->l_start + llen < 0 || +	    bf->l_start + llen > mp->m_super->s_maxbytes)  		return XFS_ERROR(EINVAL);  	bf->l_whence = 0;  | 
