Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 725eb1eb authored by Mark Tinguely's avatar Mark Tinguely Committed by Ben Myers
Browse files

xfs: fix the symbolic link assert in xfs_ifree



Adding an extended attribute to a symbolic link can force that
link to an remote extent. xfs_inactive() incorrectly assumes
that any symbolic link small enough to be in the inode core
is incore, resulting in the remote extent to not be removed.
xfs_ifree() will assert on presence of this leaked remote extent.

Signed-off-by: default avatarMark Tinguely <tinguely@sgi.com>
Reviewed-by: default avatarBen Myers <bpm@sgi.com>
Signed-off-by: default avatarBen Myers <bpm@sgi.com>
parent 1ebdf361
Loading
Loading
Loading
Loading
+46 −2
Original line number Diff line number Diff line
@@ -585,7 +585,7 @@ xfs_symlink(
/*
 * Free a symlink that has blocks associated with it.
 */
int
STATIC int
xfs_inactive_symlink_rmt(
	xfs_inode_t	*ip,
	xfs_trans_t	**tpp)
@@ -606,7 +606,7 @@ xfs_inactive_symlink_rmt(

	tp = *tpp;
	mp = ip->i_mount;
	ASSERT(ip->i_d.di_size > XFS_IFORK_DSIZE(ip));
	ASSERT(ip->i_df.if_flags & XFS_IFEXTENTS);
	/*
	 * We're freeing a symlink that has some
	 * blocks allocated to it.  Free the
@@ -720,3 +720,47 @@ xfs_inactive_symlink_rmt(
 error0:
	return error;
}

/*
 * xfs_inactive_symlink - free a symlink
 */
int
xfs_inactive_symlink(
	struct xfs_inode	*ip,
	struct xfs_trans	**tp)
{
	struct xfs_mount	*mp = ip->i_mount;
	int			pathlen;

	trace_xfs_inactive_symlink(ip);

	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));

	if (XFS_FORCED_SHUTDOWN(mp))
		return XFS_ERROR(EIO);

	/*
	 * Zero length symlinks _can_ exist.
	 */
	pathlen = (int)ip->i_d.di_size;
	if (!pathlen)
		return 0;

	if (pathlen < 0 || pathlen > MAXPATHLEN) {
		xfs_alert(mp, "%s: inode (0x%llx) bad symlink length (%d)",
			 __func__, (unsigned long long)ip->i_ino, pathlen);
		ASSERT(0);
		return XFS_ERROR(EFSCORRUPTED);
	}

	if (ip->i_df.if_flags & XFS_IFINLINE) {
		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;
	}

	/* remove the remote symlink */
	return xfs_inactive_symlink_rmt(ip, tp);
}
+1 −1
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name,
		const char *target_path, umode_t mode, struct xfs_inode **ipp);
int xfs_readlink(struct xfs_inode *ip, char *link);
int xfs_inactive_symlink_rmt(struct xfs_inode *ip, struct xfs_trans **tpp);
int xfs_inactive_symlink(struct xfs_inode *ip, struct xfs_trans **tpp);

#endif /* __KERNEL__ */
#endif /* __XFS_SYMLINK_H */
+1 −0
Original line number Diff line number Diff line
@@ -571,6 +571,7 @@ DEFINE_INODE_EVENT(xfs_iget_miss);
DEFINE_INODE_EVENT(xfs_getattr);
DEFINE_INODE_EVENT(xfs_setattr);
DEFINE_INODE_EVENT(xfs_readlink);
DEFINE_INODE_EVENT(xfs_inactive_symlink);
DEFINE_INODE_EVENT(xfs_alloc_file_space);
DEFINE_INODE_EVENT(xfs_free_file_space);
DEFINE_INODE_EVENT(xfs_readdir);
+3 −12
Original line number Diff line number Diff line
@@ -322,18 +322,9 @@ xfs_inactive(
	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);
		error = xfs_inactive_symlink(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);