diff options
author | Darrick J. Wong <djwong@kernel.org> | 2021-12-04 18:04:11 -0800 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2021-12-15 17:29:32 -0800 |
commit | 851df07d9599471df909c4ff0e3cf33f4b9619f0 (patch) | |
tree | 390175cf6ee12df70340e9e1d23742882a01bce3 /fs/xfs/libxfs/xfs_swapext.c | |
parent | e659750c94e5a1c7b9398396e47ee00749d8973e (diff) |
xfs: convert symlink repair to use swapextrepair-symlink-swapext_2021-12-15
Convert the symlink repair code to use extent swapping. This means we
can eliminate the problem of symlinks with crosslinked blocks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs/xfs/libxfs/xfs_swapext.c')
-rw-r--r-- | fs/xfs/libxfs/xfs_swapext.c | 48 |
1 files changed, 47 insertions, 1 deletions
diff --git a/fs/xfs/libxfs/xfs_swapext.c b/fs/xfs/libxfs/xfs_swapext.c index 7a1489e02d40..3dcf8f4ac817 100644 --- a/fs/xfs/libxfs/xfs_swapext.c +++ b/fs/xfs/libxfs/xfs_swapext.c @@ -28,6 +28,7 @@ #include "xfs_attr.h" #include "xfs_dir2_priv.h" #include "xfs_dir2.h" +#include "xfs_symlink_remote.h" struct kmem_cache *xfs_swapext_intent_cache; @@ -606,6 +607,48 @@ xfs_swapext_dir_to_sf( return xfs_dir2_block_to_sf(&args, bp, size, &sfh); } +/* Convert inode2's remote symlink target back to shortform, if possible. */ +STATIC int +xfs_swapext_link_to_sf( + struct xfs_trans *tp, + struct xfs_swapext_intent *sxi) +{ + struct xfs_inode *ip = sxi->sxi_ip2; + struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + char *buf; + int error; + + if (ifp->if_format == XFS_DINODE_FMT_LOCAL || + ip->i_disk_size > XFS_IFORK_DSIZE(ip)) + return 0; + + /* Read the current symlink target into a buffer. */ + buf = kmem_alloc(ip->i_disk_size + 1, KM_NOFS); + if (!buf) { + ASSERT(0); + return -ENOMEM; + } + + error = xfs_symlink_remote_read(ip, buf); + if (error) + goto free; + + /* Remove the blocks. */ + error = xfs_symlink_remote_truncate(tp, ip); + if (error) + goto free; + + /* Convert fork to local format and log our changes. */ + xfs_idestroy_fork(ifp); + ifp->if_bytes = 0; + ifp->if_format = XFS_DINODE_FMT_LOCAL; + xfs_init_local_fork(ip, XFS_DATA_FORK, buf, ip->i_disk_size); + xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE); +free: + kmem_free(buf); + return error; +} + /* Finish whatever work might come after a swap operation. */ static int xfs_swapext_postop_work( @@ -619,6 +662,8 @@ xfs_swapext_postop_work( error = xfs_swapext_attr_to_sf(tp, sxi); else if (S_ISDIR(VFS_I(sxi->sxi_ip2)->i_mode)) error = xfs_swapext_dir_to_sf(tp, sxi); + else if (S_ISLNK(VFS_I(sxi->sxi_ip2)->i_mode)) + error = xfs_swapext_link_to_sf(tp, sxi); sxi->sxi_flags &= ~XFS_SWAP_EXT_FILE2_CVT_SF; if (error) return error; @@ -1107,7 +1152,8 @@ xfs_swapext( if (req->req_flags & XFS_SWAP_REQ_FILE2_CVT_SF) ASSERT(req->whichfork == XFS_ATTR_FORK || (req->whichfork == XFS_DATA_FORK && - S_ISDIR(VFS_I(req->ip2)->i_mode))); + (S_ISDIR(VFS_I(req->ip2)->i_mode) || + S_ISLNK(VFS_I(req->ip2)->i_mode)))); if (req->blockcount == 0) return 0; |