diff options
| author | Mark Tinguely <tinguely@sgi.com> | 2013-06-17 16:35:57 -0400 |
|---|---|---|
| committer | Ben Myers <bpm@sgi.com> | 2013-06-19 15:14:43 -0400 |
| commit | 725eb1eb2ae88c200466fec34bcf1fbce4b8eca3 (patch) | |
| tree | 7460ee9819514f53f842f9df3e29485bf9daf878 | |
| parent | 1ebdf3611c8968e7202c47c2dcb2d36986c44cb0 (diff) | |
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>
| -rw-r--r-- | fs/xfs/xfs_symlink.c | 48 | ||||
| -rw-r--r-- | fs/xfs/xfs_symlink.h | 2 | ||||
| -rw-r--r-- | fs/xfs/xfs_trace.h | 1 | ||||
| -rw-r--r-- | fs/xfs/xfs_vnodeops.c | 15 |
4 files changed, 51 insertions, 15 deletions
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 195a403e1522..738c04be2019 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c | |||
| @@ -585,7 +585,7 @@ xfs_symlink( | |||
| 585 | /* | 585 | /* |
| 586 | * Free a symlink that has blocks associated with it. | 586 | * Free a symlink that has blocks associated with it. |
| 587 | */ | 587 | */ |
| 588 | int | 588 | STATIC int |
| 589 | xfs_inactive_symlink_rmt( | 589 | xfs_inactive_symlink_rmt( |
| 590 | xfs_inode_t *ip, | 590 | xfs_inode_t *ip, |
| 591 | xfs_trans_t **tpp) | 591 | xfs_trans_t **tpp) |
| @@ -606,7 +606,7 @@ xfs_inactive_symlink_rmt( | |||
| 606 | 606 | ||
| 607 | tp = *tpp; | 607 | tp = *tpp; |
| 608 | mp = ip->i_mount; | 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 | * We're freeing a symlink that has some | 611 | * We're freeing a symlink that has some |
| 612 | * blocks allocated to it. Free the | 612 | * blocks allocated to it. Free the |
| @@ -720,3 +720,47 @@ xfs_inactive_symlink_rmt( | |||
| 720 | error0: | 720 | error0: |
| 721 | return error; | 721 | return error; |
| 722 | } | 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); | ||
| 766 | } | ||
diff --git a/fs/xfs/xfs_symlink.h b/fs/xfs/xfs_symlink.h index b39398d2097c..374394880c01 100644 --- a/fs/xfs/xfs_symlink.h +++ b/fs/xfs/xfs_symlink.h | |||
| @@ -60,7 +60,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops; | |||
| 60 | int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name, | 60 | int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name, |
| 61 | const char *target_path, umode_t mode, struct xfs_inode **ipp); | 61 | const char *target_path, umode_t mode, struct xfs_inode **ipp); |
| 62 | int xfs_readlink(struct xfs_inode *ip, char *link); | 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 | #endif /* __KERNEL__ */ | 65 | #endif /* __KERNEL__ */ |
| 66 | #endif /* __XFS_SYMLINK_H */ | 66 | #endif /* __XFS_SYMLINK_H */ |
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index aa4db3307d36..e31867270077 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h | |||
| @@ -571,6 +571,7 @@ DEFINE_INODE_EVENT(xfs_iget_miss); | |||
| 571 | DEFINE_INODE_EVENT(xfs_getattr); | 571 | DEFINE_INODE_EVENT(xfs_getattr); |
| 572 | DEFINE_INODE_EVENT(xfs_setattr); | 572 | DEFINE_INODE_EVENT(xfs_setattr); |
| 573 | DEFINE_INODE_EVENT(xfs_readlink); | 573 | DEFINE_INODE_EVENT(xfs_readlink); |
| 574 | DEFINE_INODE_EVENT(xfs_inactive_symlink); | ||
| 574 | DEFINE_INODE_EVENT(xfs_alloc_file_space); | 575 | DEFINE_INODE_EVENT(xfs_alloc_file_space); |
| 575 | DEFINE_INODE_EVENT(xfs_free_file_space); | 576 | DEFINE_INODE_EVENT(xfs_free_file_space); |
| 576 | DEFINE_INODE_EVENT(xfs_readdir); | 577 | DEFINE_INODE_EVENT(xfs_readdir); |
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 0176bb21f09a..42c0ef288aeb 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c | |||
| @@ -322,18 +322,9 @@ xfs_inactive( | |||
| 322 | xfs_trans_ijoin(tp, ip, 0); | 322 | xfs_trans_ijoin(tp, ip, 0); |
| 323 | 323 | ||
| 324 | if (S_ISLNK(ip->i_d.di_mode)) { | 324 | if (S_ISLNK(ip->i_d.di_mode)) { |
| 325 | /* | 325 | error = xfs_inactive_symlink(ip, &tp); |
| 326 | * Zero length symlinks _can_ exist. | 326 | if (error) |
| 327 | */ | 327 | goto out_cancel; |
| 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 | } | ||
| 337 | } else if (truncate) { | 328 | } else if (truncate) { |
| 338 | ip->i_d.di_size = 0; | 329 | ip->i_d.di_size = 0; |
| 339 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); | 330 | xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); |
