xfs: fix the symbolic link assert in xfs_ifree
PATCH v3 1/1 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.
--- fs/xfs/xfs_symlink.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/xfs/xfs_symlink.h | 2 +- fs/xfs/xfs_trace.h | 2 ++ fs/xfs/xfs_vnodeops.c | 15 +++------------ 4 files changed, 53 insertions(+), 15 deletions(-)
fs/xfs/xfs_symlink.c +47 -2
@@ -585,7 +585,7 @@ xfs_symlink(/* * Free a symlink that has blocks associated with it. */ -int +STATIC intxfs_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,48 @@ 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; +xfs_fsize_t pathlen; + +trace_xfs_readlink (ip); + +ASSERT (xfs_isilocked (ip,XFS_ILOCK_EXCL )); + +if (XFS_FORCED_SHUTDOWN (mp)) +return XFS_ERROR (EIO ); + +/* +* Zero length symlinks _can_ exist. +*/ + pathlen = ip->i_d.di_size; +if (!pathlen) +return 0 ; + +if (pathlen <0 || pathlen >MAXPATHLEN ) { +xfs_alert (mp,"%s: inode (%llu) bad symlink length (%lld)" , + __func__, (unsigned long long ) ip->i_ino, + (long long ) 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)); +}
fs/xfs/xfs_symlink.h +1 -1
@@ -60,7 +60,7 @@ extern const struct xfs_buf_ops xfs_symlint 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 */
fs/xfs/xfs_trace.h +2 -0
@@ -594,6 +594,8 @@ DEFINE_INODE_EVENT(xfs_inode_set_eofblocDEFINE_INODE_EVENT (xfs_inode_clear_eofblocks_tag);DEFINE_INODE_EVENT (xfs_inode_free_eofblocks_invalid); +DEFINE_INODE_EVENT (xfs_symlink_inactive); + DECLARE_EVENT_CLASS(xfs_iref_class, TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip), TP_ARGS(ip, caller_ip),
fs/xfs/xfs_vnodeops.c +3 -12
@@ -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); -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 ); - } + error =xfs_inactive_symlink (ip, &tp); +if (error) + goto out_cancel; }else if (truncate) { ip->i_d.di_size =0 ;xfs_trans_log_inode (tp, ip,XFS_ILOG_CORE );
_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
Quoted 2 lines · Mark Tinguely
race_xfs_readlink(ip);
+
_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
Quoted 4 lines · Mark Tinguely
Quoted 2 lines · Mark Tinguely
race_xfs_readlink(ip);
+
Augh, one of those days - oops I got the wrong trace in this version of the patch... should be trace_xfs_symlink_inactive(ip) added in xfs_trace.h
_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
Quoted 7 lines · Mark Tinguely
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 <[email protected]>
.....
fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 733
Quoted 10 lines
fs/xfs/xfs_symlink.c +10
-0+/* +* 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; +xfs_fsize_t pathlen;
int will do here - it can't be more than MAXPATHLEN = 1024, and this gets rid of the casts in the error print.
Review
Quoted 2 lines
(quoted diff) +2
-0+ + trace_xfs_readlink(ip);
trace_xfs_inactive_symlink, as you mentioned ;)
Quoted 6 lines
fs/xfs/xfs_symlink.c +6
-0+ +ASSERT (xfs_isilocked (ip,XFS_ILOCK_EXCL )); + +if (XFS_FORCED_SHUTDOWN (mp)) +return XFS_ERROR (EIO ); +
fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 751
Quoted 10 lines
fs/xfs/xfs_symlink.c +10
-0+/* +* Zero length symlinks _can_ exist. +*/ + pathlen = ip->i_d.di_size; +if (!pathlen) +return 0 ; + +if (pathlen <0 || pathlen >MAXPATHLEN ) { + xfs_alert(mp, "%s: inode (%llu) bad symlink length (%lld)", + __func__, (unsigned long long) ip->i_ino,
^ whitespace
fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 752
Quoted 1 line
fs/xfs/xfs_symlink.c +1
-0+ (long long) pathlen);
Can we change this to a hex-based inode number (much easier to read) and the pathlen cast can go away if you use an int. i.e.:
xfs_alert(mp, "%s: inode (0x%llx) bad symlink length (%d)", __func__, (unsigned long long)ip->i_ino, pathlen);
Quoted 4 lines
fs/xfs/xfs_symlink.c +4
-0+ASSERT (0 ); +return XFS_ERROR (EFSCORRUPTED ); + } +
fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 766
Quoted 10 lines
fs/xfs/xfs_symlink.c +10
-0+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));
And no () around xfs_inactive_symlink_rmt().
Quoted 19 lines
+}
Index:b/fs/xfs/xfs_symlink.h
===================================================================fs/xfs/xfs_symlink.h +1
-1@@ -60,7 +60,7 @@ extern const struct xfs_buf_ops xfs_symlint 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 */ Index:
b/fs/xfs/xfs_trace.h
===================================================================
fs/xfs/xfs_trace.h DEFINE_INODE_EVENT(xfs_inode_set_eofbloc · line 598
Quoted 5 lines
fs/xfs/xfs_trace.h +2
-0@@ -594,6 +594,8 @@ DEFINE_INODE_EVENT(xfs_inode_set_eofblocDEFINE_INODE_EVENT (xfs_inode_clear_eofblocks_tag);DEFINE_INODE_EVENT (xfs_inode_free_eofblocks_invalid); +DEFINE_INODE_EVENT (xfs_symlink_inactive); +
No need for the extra whitespace, just put it hard up against the other DEFINE_INODE_EVENT()s. Might be better to put it directly after the readlink trace definition.
Cheers,
Dave.
Signature · 7 lines
Dave Chinner [email protected] _______________________________________________ xfs mailing list [email protected] http://oss.sgi.com/mailman/listinfo/xfs
Quoted 23 lines · Dave Chinner
Quoted 7 lines · Mark Tinguely
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<[email protected]>.....
fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 733
Quoted 10 lines
fs/xfs/xfs_symlink.c +10
-0+/* +* 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; +xfs_fsize_t pathlen;int will do here - it can't be more than MAXPATHLEN = 1024, and this gets rid of the casts in the error print.
Quoted 4 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ + trace_xfs_readlink(ip);
trace_xfs_inactive_symlink, as you mentioned ;)
sigh, and I reverted the inactive and symlink in the trace below. sigh again.
Quoted 18 lines
Quoted 7 lines
fs/xfs/xfs_symlink.c +6
-0+ +ASSERT (xfs_isilocked (ip,XFS_ILOCK_EXCL )); + +if (XFS_FORCED_SHUTDOWN (mp)) +return XFS_ERROR (EIO ); +fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 751
Quoted 10 lines
fs/xfs/xfs_symlink.c +10
-0+/* +* Zero length symlinks _can_ exist. +*/ + pathlen = ip->i_d.di_size; +if (!pathlen) +return 0 ; + +if (pathlen<0 || pathlen>MAXPATHLEN ) { + xfs_alert(mp, "%s: inode (%llu) bad symlink length (%lld)", + __func__, (unsigned long long) ip->i_ino,^ whitespace
Quoted 9 lines
fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 752
Quoted 1 line
fs/xfs/xfs_symlink.c +1
-0+ (long long) pathlen);Can we change this to a hex-based inode number (much easier to read) and the pathlen cast can go away if you use an int. i.e.:
xfs_alert(mp, "%s: inode (0x%llx) bad symlink length (%d)", __func__, (unsigned long long)ip->i_ino, pathlen);
Both of the items above were stole directly from xfs_readlink(). Thought they should be consistent(ly wrong :) ).
Quoted 47 lines
Quoted 4 lines
fs/xfs/xfs_symlink.c +4
-0+ASSERT (0 ); +return XFS_ERROR (EFSCORRUPTED ); + } +fs/xfs/xfs_symlink.c xfs_inactive_symlink_rmt( · line 766
Quoted 10 lines
fs/xfs/xfs_symlink.c +10
-0+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));And no () around
xfs_inactive_symlink_rmt().Quoted 19 lines
+}
Index:b/fs/xfs/xfs_symlink.h
===================================================================fs/xfs/xfs_symlink.h +1
-1@@ -60,7 +60,7 @@ extern const struct xfs_buf_ops xfs_symlint 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 */ Index:
b/fs/xfs/xfs_trace.h
===================================================================fs/xfs/xfs_trace.h DEFINE_INODE_EVENT(xfs_inode_set_eofbloc · line 598
Quoted 5 lines
fs/xfs/xfs_trace.h +2
-0@@ -594,6 +594,8 @@ DEFINE_INODE_EVENT(xfs_inode_set_eofblocDEFINE_INODE_EVENT (xfs_inode_clear_eofblocks_tag);DEFINE_INODE_EVENT (xfs_inode_free_eofblocks_invalid); +DEFINE_INODE_EVENT (xfs_symlink_inactive); +No need for the extra whitespace, just put it hard up against the other DEFINE_INODE_EVENT()s. Might be better to put it directly after the readlink trace definition.
Quoted 3 lines
Cheers,
Dave.
Thanks.
--Mark.
_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs