Commit 725eb1eb2ae88c200466fec34bcf1fbce4b8eca3
Committed by
Ben Myers
1 parent
1ebdf3611c
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
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 */ |
fs/xfs/xfs_trace.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); |