Commit 725eb1eb2ae88c200466fec34bcf1fbce4b8eca3

Authored by Mark Tinguely
Committed by Ben Myers
1 parent 1ebdf3611c

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: Mark Tinguely <tinguely@sgi.com>
Reviewed-by: Ben Myers <bpm@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>

Showing 4 changed files with 51 additions and 15 deletions Side-by-side Diff

fs/xfs/xfs_symlink.c
... ... @@ -585,7 +585,7 @@
585 585 /*
586 586 * Free a symlink that has blocks associated with it.
587 587 */
588   -int
  588 +STATIC int
589 589 xfs_inactive_symlink_rmt(
590 590 xfs_inode_t *ip,
591 591 xfs_trans_t **tpp)
... ... @@ -606,7 +606,7 @@
606 606  
607 607 tp = *tpp;
608 608 mp = ip->i_mount;
609   - ASSERT(ip->i_d.di_size > XFS_IFORK_DSIZE(ip));
  609 + ASSERT(ip->i_df.if_flags & XFS_IFEXTENTS);
610 610 /*
611 611 * We're freeing a symlink that has some
612 612 * blocks allocated to it. Free the
... ... @@ -719,5 +719,49 @@
719 719 xfs_bmap_cancel(&free_list);
720 720 error0:
721 721 return error;
  722 +}
  723 +
  724 +/*
  725 + * xfs_inactive_symlink - free a symlink
  726 + */
  727 +int
  728 +xfs_inactive_symlink(
  729 + struct xfs_inode *ip,
  730 + struct xfs_trans **tp)
  731 +{
  732 + struct xfs_mount *mp = ip->i_mount;
  733 + int pathlen;
  734 +
  735 + trace_xfs_inactive_symlink(ip);
  736 +
  737 + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
  738 +
  739 + if (XFS_FORCED_SHUTDOWN(mp))
  740 + return XFS_ERROR(EIO);
  741 +
  742 + /*
  743 + * Zero length symlinks _can_ exist.
  744 + */
  745 + pathlen = (int)ip->i_d.di_size;
  746 + if (!pathlen)
  747 + return 0;
  748 +
  749 + if (pathlen < 0 || pathlen > MAXPATHLEN) {
  750 + xfs_alert(mp, "%s: inode (0x%llx) bad symlink length (%d)",
  751 + __func__, (unsigned long long)ip->i_ino, pathlen);
  752 + ASSERT(0);
  753 + return XFS_ERROR(EFSCORRUPTED);
  754 + }
  755 +
  756 + if (ip->i_df.if_flags & XFS_IFINLINE) {
  757 + if (ip->i_df.if_bytes > 0)
  758 + xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
  759 + XFS_DATA_FORK);
  760 + ASSERT(ip->i_df.if_bytes == 0);
  761 + return 0;
  762 + }
  763 +
  764 + /* remove the remote symlink */
  765 + return xfs_inactive_symlink_rmt(ip, tp);
722 766 }
fs/xfs/xfs_symlink.h
... ... @@ -60,7 +60,7 @@
60 60 int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name,
61 61 const char *target_path, umode_t mode, struct xfs_inode **ipp);
62 62 int xfs_readlink(struct xfs_inode *ip, char *link);
63   -int xfs_inactive_symlink_rmt(struct xfs_inode *ip, struct xfs_trans **tpp);
  63 +int xfs_inactive_symlink(struct xfs_inode *ip, struct xfs_trans **tpp);
64 64  
65 65 #endif /* __KERNEL__ */
66 66 #endif /* __XFS_SYMLINK_H */
... ... @@ -571,6 +571,7 @@
571 571 DEFINE_INODE_EVENT(xfs_getattr);
572 572 DEFINE_INODE_EVENT(xfs_setattr);
573 573 DEFINE_INODE_EVENT(xfs_readlink);
  574 +DEFINE_INODE_EVENT(xfs_inactive_symlink);
574 575 DEFINE_INODE_EVENT(xfs_alloc_file_space);
575 576 DEFINE_INODE_EVENT(xfs_free_file_space);
576 577 DEFINE_INODE_EVENT(xfs_readdir);
fs/xfs/xfs_vnodeops.c
... ... @@ -322,18 +322,9 @@
322 322 xfs_trans_ijoin(tp, ip, 0);
323 323  
324 324 if (S_ISLNK(ip->i_d.di_mode)) {
325   - /*
326   - * Zero length symlinks _can_ exist.
327   - */
328   - if (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) {
329   - error = xfs_inactive_symlink_rmt(ip, &tp);
330   - if (error)
331   - goto out_cancel;
332   - } else if (ip->i_df.if_bytes > 0) {
333   - xfs_idata_realloc(ip, -(ip->i_df.if_bytes),
334   - XFS_DATA_FORK);
335   - ASSERT(ip->i_df.if_bytes == 0);
336   - }
  325 + error = xfs_inactive_symlink(ip, &tp);
  326 + if (error)
  327 + goto out_cancel;
337 328 } else if (truncate) {
338 329 ip->i_d.di_size = 0;
339 330 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);