#include "xfs_attr.h"
 #include "xfs_dir2_priv.h"
 #include "xfs_dir2.h"
+#include "xfs_symlink_remote.h"
 
 struct kmem_cache      *xfs_exchmaps_intent_cache;
 
        return xfs_dir2_block_to_sf(&args, bp, size, &sfh);
 }
 
+/* Convert inode2's remote symlink target back to shortform, if possible. */
+STATIC int
+xfs_exchmaps_link_to_sf(
+       struct xfs_trans                *tp,
+       struct xfs_exchmaps_intent      *xmi)
+{
+       struct xfs_inode                *ip = xmi->xmi_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_inode_data_fork_size(ip))
+               return 0;
+
+       /* Read the current symlink target into a buffer. */
+       buf = kmalloc(ip->i_disk_size + 1,
+                       GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL);
+       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:
+       kfree(buf);
+       return error;
+}
+
 /* Clear the reflink flag after an exchange. */
 static inline void
 xfs_exchmaps_clear_reflink(
                        error = xfs_exchmaps_attr_to_sf(tp, xmi);
                else if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode))
                        error = xfs_exchmaps_dir_to_sf(tp, xmi);
+               else if (S_ISLNK(VFS_I(xmi->xmi_ip2)->i_mode))
+                       error = xfs_exchmaps_link_to_sf(tp, xmi);
                xmi->xmi_flags &= ~__XFS_EXCHMAPS_INO2_SHORTFORM;
                if (error)
                        return error;
                        xmi->xmi_flags |= XFS_EXCHMAPS_CLEAR_INO2_REFLINK;
        }
 
-       if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode))
+       if (S_ISDIR(VFS_I(xmi->xmi_ip2)->i_mode) ||
+           S_ISLNK(VFS_I(xmi->xmi_ip2)->i_mode))
                xmi->xmi_flags |= __XFS_EXCHMAPS_INO2_SHORTFORM;
 
        return xmi;
 
        ASSERT(pathlen == 0);
        return 0;
 }
+
+/* Remove all the blocks from a symlink and invalidate buffers. */
+int
+xfs_symlink_remote_truncate(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip)
+{
+       struct xfs_bmbt_irec    mval[XFS_SYMLINK_MAPS];
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_buf          *bp;
+       int                     nmaps = XFS_SYMLINK_MAPS;
+       int                     done = 0;
+       int                     i;
+       int                     error;
+
+       /* Read mappings and invalidate buffers. */
+       error = xfs_bmapi_read(ip, 0, XFS_MAX_FILEOFF, mval, &nmaps, 0);
+       if (error)
+               return error;
+
+       for (i = 0; i < nmaps; i++) {
+               if (!xfs_bmap_is_real_extent(&mval[i]))
+                       break;
+
+               error = xfs_trans_get_buf(tp, mp->m_ddev_targp,
+                               XFS_FSB_TO_DADDR(mp, mval[i].br_startblock),
+                               XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0,
+                               &bp);
+               if (error)
+                       return error;
+
+               xfs_trans_binval(tp, bp);
+       }
+
+       /* Unmap the remote blocks. */
+       error = xfs_bunmapi(tp, ip, 0, XFS_MAX_FILEOFF, 0, nmaps, &done);
+       if (error)
+               return error;
+       if (!done) {
+               ASSERT(done);
+               xfs_inode_mark_sick(ip, XFS_SICK_INO_SYMLINK);
+               return -EFSCORRUPTED;
+       }
+
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+       return 0;
+}
 
 int xfs_symlink_write_target(struct xfs_trans *tp, struct xfs_inode *ip,
                const char *target_path, int pathlen, xfs_fsblock_t fs_blocks,
                uint resblks);
+int xfs_symlink_remote_truncate(struct xfs_trans *tp, struct xfs_inode *ip);
 
 #endif /* __XFS_SYMLINK_REMOTE_H */
 
  */
 STATIC int
 xfs_inactive_symlink_rmt(
-       struct xfs_inode *ip)
+       struct xfs_inode        *ip)
 {
-       struct xfs_buf  *bp;
-       int             done;
-       int             error;
-       int             i;
-       xfs_mount_t     *mp;
-       xfs_bmbt_irec_t mval[XFS_SYMLINK_MAPS];
-       int             nmaps;
-       int             size;
-       xfs_trans_t     *tp;
-
-       mp = ip->i_mount;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       int                     error;
+
        ASSERT(!xfs_need_iread_extents(&ip->i_df));
        /*
         * We're freeing a symlink that has some
         * locked for the second transaction.  In the error paths we need it
         * held so the cancel won't rele it, see below.
         */
-       size = (int)ip->i_disk_size;
        ip->i_disk_size = 0;
        VFS_I(ip)->i_mode = (VFS_I(ip)->i_mode & ~S_IFMT) | S_IFREG;
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       /*
-        * Find the block(s) so we can inval and unmap them.
-        */
-       done = 0;
-       nmaps = ARRAY_SIZE(mval);
-       error = xfs_bmapi_read(ip, 0, xfs_symlink_blocks(mp, size),
-                               mval, &nmaps, 0);
-       if (error)
-               goto error_trans_cancel;
-       /*
-        * Invalidate the block(s). No validation is done.
-        */
-       for (i = 0; i < nmaps; i++) {
-               error = xfs_trans_get_buf(tp, mp->m_ddev_targp,
-                               XFS_FSB_TO_DADDR(mp, mval[i].br_startblock),
-                               XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0,
-                               &bp);
-               if (error)
-                       goto error_trans_cancel;
-               xfs_trans_binval(tp, bp);
-       }
-       /*
-        * Unmap the dead block(s) to the dfops.
-        */
-       error = xfs_bunmapi(tp, ip, 0, size, 0, nmaps, &done);
+
+       error = xfs_symlink_remote_truncate(tp, ip);
        if (error)
                goto error_trans_cancel;
-       ASSERT(done);
 
-       /*
-        * Commit the transaction. This first logs the EFI and the inode, then
-        * rolls and commits the transaction that frees the extents.
-        */
-       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
        error = xfs_trans_commit(tp);
        if (error) {
                ASSERT(xfs_is_shutdown(mp));