12651923dSDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
22651923dSDarrick J. Wong /*
32651923dSDarrick J. Wong * Copyright (c) 2018-2024 Oracle. All Rights Reserved.
42651923dSDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org>
52651923dSDarrick J. Wong */
62651923dSDarrick J. Wong #include "xfs.h"
72651923dSDarrick J. Wong #include "xfs_fs.h"
82651923dSDarrick J. Wong #include "xfs_shared.h"
92651923dSDarrick J. Wong #include "xfs_format.h"
102651923dSDarrick J. Wong #include "xfs_trans_resv.h"
112651923dSDarrick J. Wong #include "xfs_mount.h"
122651923dSDarrick J. Wong #include "xfs_defer.h"
132651923dSDarrick J. Wong #include "xfs_btree.h"
142651923dSDarrick J. Wong #include "xfs_bit.h"
152651923dSDarrick J. Wong #include "xfs_log_format.h"
162651923dSDarrick J. Wong #include "xfs_trans.h"
172651923dSDarrick J. Wong #include "xfs_sb.h"
182651923dSDarrick J. Wong #include "xfs_inode.h"
192651923dSDarrick J. Wong #include "xfs_inode_fork.h"
202651923dSDarrick J. Wong #include "xfs_symlink.h"
212651923dSDarrick J. Wong #include "xfs_bmap.h"
222651923dSDarrick J. Wong #include "xfs_quota.h"
232651923dSDarrick J. Wong #include "xfs_da_format.h"
242651923dSDarrick J. Wong #include "xfs_da_btree.h"
252651923dSDarrick J. Wong #include "xfs_bmap_btree.h"
262651923dSDarrick J. Wong #include "xfs_trans_space.h"
272651923dSDarrick J. Wong #include "xfs_symlink_remote.h"
282651923dSDarrick J. Wong #include "xfs_exchmaps.h"
292651923dSDarrick J. Wong #include "xfs_exchrange.h"
302651923dSDarrick J. Wong #include "xfs_health.h"
312651923dSDarrick J. Wong #include "scrub/xfs_scrub.h"
322651923dSDarrick J. Wong #include "scrub/scrub.h"
332651923dSDarrick J. Wong #include "scrub/common.h"
342651923dSDarrick J. Wong #include "scrub/trace.h"
352651923dSDarrick J. Wong #include "scrub/repair.h"
362651923dSDarrick J. Wong #include "scrub/tempfile.h"
372651923dSDarrick J. Wong #include "scrub/tempexch.h"
382651923dSDarrick J. Wong #include "scrub/reap.h"
39*6f466970SDarrick J. Wong #include "scrub/health.h"
402651923dSDarrick J. Wong
412651923dSDarrick J. Wong /*
422651923dSDarrick J. Wong * Symbolic Link Repair
432651923dSDarrick J. Wong * ====================
442651923dSDarrick J. Wong *
452651923dSDarrick J. Wong * We repair symbolic links by reading whatever target data we can find, up to
462651923dSDarrick J. Wong * the first NULL byte. If the recovered target strlen matches i_size, then
472651923dSDarrick J. Wong * we rewrite the target. In all other cases, we replace the target with an
482651923dSDarrick J. Wong * overly long string that cannot possibly resolve. The new target is written
492651923dSDarrick J. Wong * into a private hidden temporary file, and then a file contents exchange
502651923dSDarrick J. Wong * commits the new symlink target to the file being repaired.
512651923dSDarrick J. Wong */
522651923dSDarrick J. Wong
532651923dSDarrick J. Wong /* Set us up to repair the symlink file. */
542651923dSDarrick J. Wong int
xrep_setup_symlink(struct xfs_scrub * sc,unsigned int * resblks)552651923dSDarrick J. Wong xrep_setup_symlink(
562651923dSDarrick J. Wong struct xfs_scrub *sc,
572651923dSDarrick J. Wong unsigned int *resblks)
582651923dSDarrick J. Wong {
592651923dSDarrick J. Wong struct xfs_mount *mp = sc->mp;
602651923dSDarrick J. Wong unsigned long long blocks;
612651923dSDarrick J. Wong int error;
622651923dSDarrick J. Wong
632651923dSDarrick J. Wong error = xrep_tempfile_create(sc, S_IFLNK);
642651923dSDarrick J. Wong if (error)
652651923dSDarrick J. Wong return error;
662651923dSDarrick J. Wong
672651923dSDarrick J. Wong /*
682651923dSDarrick J. Wong * If we're doing a repair, we reserve enough blocks to write out a
692651923dSDarrick J. Wong * completely new symlink file, plus twice as many blocks as we would
702651923dSDarrick J. Wong * need if we can only allocate one block per data fork mapping. This
712651923dSDarrick J. Wong * should cover the preallocation of the temporary file and exchanging
722651923dSDarrick J. Wong * the extent mappings.
732651923dSDarrick J. Wong *
742651923dSDarrick J. Wong * We cannot use xfs_exchmaps_estimate because we have not yet
752651923dSDarrick J. Wong * constructed the replacement symlink and therefore do not know how
762651923dSDarrick J. Wong * many extents it will use. By the time we do, we will have a dirty
772651923dSDarrick J. Wong * transaction (which we cannot drop because we cannot drop the
782651923dSDarrick J. Wong * symlink ILOCK) and cannot ask for more reservation.
792651923dSDarrick J. Wong */
802651923dSDarrick J. Wong blocks = xfs_symlink_blocks(sc->mp, XFS_SYMLINK_MAXLEN);
812651923dSDarrick J. Wong blocks += xfs_bmbt_calc_size(mp, blocks) * 2;
822651923dSDarrick J. Wong if (blocks > UINT_MAX)
832651923dSDarrick J. Wong return -EOPNOTSUPP;
842651923dSDarrick J. Wong
852651923dSDarrick J. Wong *resblks += blocks;
862651923dSDarrick J. Wong return 0;
872651923dSDarrick J. Wong }
882651923dSDarrick J. Wong
892651923dSDarrick J. Wong /*
902651923dSDarrick J. Wong * Try to salvage the pathname from remote blocks. Returns the number of bytes
912651923dSDarrick J. Wong * salvaged or a negative errno.
922651923dSDarrick J. Wong */
932651923dSDarrick J. Wong STATIC ssize_t
xrep_symlink_salvage_remote(struct xfs_scrub * sc)942651923dSDarrick J. Wong xrep_symlink_salvage_remote(
952651923dSDarrick J. Wong struct xfs_scrub *sc)
962651923dSDarrick J. Wong {
972651923dSDarrick J. Wong struct xfs_bmbt_irec mval[XFS_SYMLINK_MAPS];
982651923dSDarrick J. Wong struct xfs_inode *ip = sc->ip;
992651923dSDarrick J. Wong struct xfs_buf *bp;
1002651923dSDarrick J. Wong char *target_buf = sc->buf;
1012651923dSDarrick J. Wong xfs_failaddr_t fa;
1022651923dSDarrick J. Wong xfs_filblks_t fsblocks;
1032651923dSDarrick J. Wong xfs_daddr_t d;
1042651923dSDarrick J. Wong loff_t len;
1052651923dSDarrick J. Wong loff_t offset = 0;
1062651923dSDarrick J. Wong unsigned int byte_cnt;
1072651923dSDarrick J. Wong bool magic_ok;
1082651923dSDarrick J. Wong bool hdr_ok;
1092651923dSDarrick J. Wong int n;
1102651923dSDarrick J. Wong int nmaps = XFS_SYMLINK_MAPS;
1112651923dSDarrick J. Wong int error;
1122651923dSDarrick J. Wong
1132651923dSDarrick J. Wong /* We'll only read until the buffer is full. */
1142651923dSDarrick J. Wong len = min_t(loff_t, ip->i_disk_size, XFS_SYMLINK_MAXLEN);
1152651923dSDarrick J. Wong fsblocks = xfs_symlink_blocks(sc->mp, len);
1162651923dSDarrick J. Wong error = xfs_bmapi_read(ip, 0, fsblocks, mval, &nmaps, 0);
1172651923dSDarrick J. Wong if (error)
1182651923dSDarrick J. Wong return error;
1192651923dSDarrick J. Wong
1202651923dSDarrick J. Wong for (n = 0; n < nmaps; n++) {
1212651923dSDarrick J. Wong struct xfs_dsymlink_hdr *dsl;
1222651923dSDarrick J. Wong
1232651923dSDarrick J. Wong d = XFS_FSB_TO_DADDR(sc->mp, mval[n].br_startblock);
1242651923dSDarrick J. Wong
1252651923dSDarrick J. Wong /* Read the rmt block. We'll run the verifiers manually. */
1262651923dSDarrick J. Wong error = xfs_trans_read_buf(sc->mp, sc->tp, sc->mp->m_ddev_targp,
1272651923dSDarrick J. Wong d, XFS_FSB_TO_BB(sc->mp, mval[n].br_blockcount),
1282651923dSDarrick J. Wong 0, &bp, NULL);
1292651923dSDarrick J. Wong if (error)
1302651923dSDarrick J. Wong return error;
1312651923dSDarrick J. Wong bp->b_ops = &xfs_symlink_buf_ops;
1322651923dSDarrick J. Wong
1332651923dSDarrick J. Wong /* How many bytes do we expect to get out of this buffer? */
1342651923dSDarrick J. Wong byte_cnt = XFS_FSB_TO_B(sc->mp, mval[n].br_blockcount);
1352651923dSDarrick J. Wong byte_cnt = XFS_SYMLINK_BUF_SPACE(sc->mp, byte_cnt);
1362651923dSDarrick J. Wong byte_cnt = min_t(unsigned int, byte_cnt, len);
1372651923dSDarrick J. Wong
1382651923dSDarrick J. Wong /*
1392651923dSDarrick J. Wong * See if the verifiers accept this block. We're willing to
1402651923dSDarrick J. Wong * salvage if the if the offset/byte/ino are ok and either the
1412651923dSDarrick J. Wong * verifier passed or the magic is ok. Anything else and we
1422651923dSDarrick J. Wong * stop dead in our tracks.
1432651923dSDarrick J. Wong */
1442651923dSDarrick J. Wong fa = bp->b_ops->verify_struct(bp);
1452651923dSDarrick J. Wong dsl = bp->b_addr;
1462651923dSDarrick J. Wong magic_ok = dsl->sl_magic == cpu_to_be32(XFS_SYMLINK_MAGIC);
1472651923dSDarrick J. Wong hdr_ok = xfs_symlink_hdr_ok(ip->i_ino, offset, byte_cnt, bp);
1482651923dSDarrick J. Wong if (!hdr_ok || (fa != NULL && !magic_ok))
1492651923dSDarrick J. Wong break;
1502651923dSDarrick J. Wong
1512651923dSDarrick J. Wong memcpy(target_buf + offset, dsl + 1, byte_cnt);
1522651923dSDarrick J. Wong
1532651923dSDarrick J. Wong len -= byte_cnt;
1542651923dSDarrick J. Wong offset += byte_cnt;
1552651923dSDarrick J. Wong }
1562651923dSDarrick J. Wong return offset;
1572651923dSDarrick J. Wong }
1582651923dSDarrick J. Wong
1592651923dSDarrick J. Wong /*
1602651923dSDarrick J. Wong * Try to salvage an inline symlink's contents. Returns the number of bytes
1612651923dSDarrick J. Wong * salvaged or a negative errno.
1622651923dSDarrick J. Wong */
1632651923dSDarrick J. Wong STATIC ssize_t
xrep_symlink_salvage_inline(struct xfs_scrub * sc)1642651923dSDarrick J. Wong xrep_symlink_salvage_inline(
1652651923dSDarrick J. Wong struct xfs_scrub *sc)
1662651923dSDarrick J. Wong {
1672651923dSDarrick J. Wong struct xfs_inode *ip = sc->ip;
1682651923dSDarrick J. Wong char *target_buf = sc->buf;
1692651923dSDarrick J. Wong char *old_target;
1702651923dSDarrick J. Wong struct xfs_ifork *ifp;
1712651923dSDarrick J. Wong unsigned int nr;
1722651923dSDarrick J. Wong
1732651923dSDarrick J. Wong ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
1742651923dSDarrick J. Wong if (!ifp->if_data)
1752651923dSDarrick J. Wong return 0;
1762651923dSDarrick J. Wong
1772651923dSDarrick J. Wong /*
1782651923dSDarrick J. Wong * If inode repair zapped the link target, pretend that we didn't find
1792651923dSDarrick J. Wong * any bytes at all so that we can replace the (now totally lost) link
1802651923dSDarrick J. Wong * target with a warning message.
1812651923dSDarrick J. Wong */
1822651923dSDarrick J. Wong old_target = ifp->if_data;
1832651923dSDarrick J. Wong if (xfs_inode_has_sickness(sc->ip, XFS_SICK_INO_SYMLINK_ZAPPED) &&
1842651923dSDarrick J. Wong sc->ip->i_disk_size == 1 && old_target[0] == '?')
1852651923dSDarrick J. Wong return 0;
1862651923dSDarrick J. Wong
1872651923dSDarrick J. Wong nr = min(XFS_SYMLINK_MAXLEN, xfs_inode_data_fork_size(ip));
1882651923dSDarrick J. Wong strncpy(target_buf, ifp->if_data, nr);
1892651923dSDarrick J. Wong return nr;
1902651923dSDarrick J. Wong }
1912651923dSDarrick J. Wong
1922651923dSDarrick J. Wong #define DUMMY_TARGET \
1932651923dSDarrick J. Wong "The target of this symbolic link could not be recovered at all and " \
1942651923dSDarrick J. Wong "has been replaced with this explanatory message. To avoid " \
1952651923dSDarrick J. Wong "accidentally pointing to an existing file path, this message is " \
1962651923dSDarrick J. Wong "longer than the maximum supported file name length. That is an " \
1972651923dSDarrick J. Wong "acceptable length for a symlink target on XFS but will produce " \
1982651923dSDarrick J. Wong "File Name Too Long errors if resolved."
1992651923dSDarrick J. Wong
2002651923dSDarrick J. Wong /* Salvage whatever we can of the target. */
2012651923dSDarrick J. Wong STATIC int
xrep_symlink_salvage(struct xfs_scrub * sc)2022651923dSDarrick J. Wong xrep_symlink_salvage(
2032651923dSDarrick J. Wong struct xfs_scrub *sc)
2042651923dSDarrick J. Wong {
2052651923dSDarrick J. Wong char *target_buf = sc->buf;
2062651923dSDarrick J. Wong ssize_t buflen = 0;
2072651923dSDarrick J. Wong
2082651923dSDarrick J. Wong BUILD_BUG_ON(sizeof(DUMMY_TARGET) - 1 <= NAME_MAX);
2092651923dSDarrick J. Wong
2102651923dSDarrick J. Wong /*
2112651923dSDarrick J. Wong * Salvage the target if there weren't any corruption problems observed
2122651923dSDarrick J. Wong * while scanning it.
2132651923dSDarrick J. Wong */
2142651923dSDarrick J. Wong if (!(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
2152651923dSDarrick J. Wong if (sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL)
2162651923dSDarrick J. Wong buflen = xrep_symlink_salvage_inline(sc);
2172651923dSDarrick J. Wong else
2182651923dSDarrick J. Wong buflen = xrep_symlink_salvage_remote(sc);
2192651923dSDarrick J. Wong if (buflen < 0)
2202651923dSDarrick J. Wong return buflen;
2212651923dSDarrick J. Wong
2222651923dSDarrick J. Wong /*
2232651923dSDarrick J. Wong * NULL-terminate the buffer because the ondisk target does not
2242651923dSDarrick J. Wong * do that for us. If salvage didn't find the exact amount of
2252651923dSDarrick J. Wong * data that we expected to find, don't salvage anything.
2262651923dSDarrick J. Wong */
2272651923dSDarrick J. Wong target_buf[buflen] = 0;
2282651923dSDarrick J. Wong if (strlen(target_buf) != sc->ip->i_disk_size)
2292651923dSDarrick J. Wong buflen = 0;
2302651923dSDarrick J. Wong }
2312651923dSDarrick J. Wong
2322651923dSDarrick J. Wong /*
2332651923dSDarrick J. Wong * Change an empty target into a dummy target and clear the symlink
2342651923dSDarrick J. Wong * target zapped flag.
2352651923dSDarrick J. Wong */
2362651923dSDarrick J. Wong if (buflen == 0) {
237*6f466970SDarrick J. Wong xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_SYMLINK_ZAPPED);
2382651923dSDarrick J. Wong sprintf(target_buf, DUMMY_TARGET);
2392651923dSDarrick J. Wong }
2402651923dSDarrick J. Wong
2412651923dSDarrick J. Wong trace_xrep_symlink_salvage_target(sc->ip, target_buf,
2422651923dSDarrick J. Wong strlen(target_buf));
2432651923dSDarrick J. Wong return 0;
2442651923dSDarrick J. Wong }
2452651923dSDarrick J. Wong
2462651923dSDarrick J. Wong STATIC void
xrep_symlink_local_to_remote(struct xfs_trans * tp,struct xfs_buf * bp,struct xfs_inode * ip,struct xfs_ifork * ifp,void * priv)2472651923dSDarrick J. Wong xrep_symlink_local_to_remote(
2482651923dSDarrick J. Wong struct xfs_trans *tp,
2492651923dSDarrick J. Wong struct xfs_buf *bp,
2502651923dSDarrick J. Wong struct xfs_inode *ip,
2512651923dSDarrick J. Wong struct xfs_ifork *ifp,
2522651923dSDarrick J. Wong void *priv)
2532651923dSDarrick J. Wong {
2542651923dSDarrick J. Wong struct xfs_scrub *sc = priv;
2552651923dSDarrick J. Wong struct xfs_dsymlink_hdr *dsl = bp->b_addr;
2562651923dSDarrick J. Wong
2572651923dSDarrick J. Wong xfs_symlink_local_to_remote(tp, bp, ip, ifp, NULL);
2582651923dSDarrick J. Wong
2592651923dSDarrick J. Wong if (!xfs_has_crc(sc->mp))
2602651923dSDarrick J. Wong return;
2612651923dSDarrick J. Wong
2622651923dSDarrick J. Wong dsl->sl_owner = cpu_to_be64(sc->ip->i_ino);
2632651923dSDarrick J. Wong xfs_trans_log_buf(tp, bp, 0,
2642651923dSDarrick J. Wong sizeof(struct xfs_dsymlink_hdr) + ifp->if_bytes - 1);
2652651923dSDarrick J. Wong }
2662651923dSDarrick J. Wong
2672651923dSDarrick J. Wong /*
2682651923dSDarrick J. Wong * Prepare both links' data forks for an exchange. Promote the tempfile from
2692651923dSDarrick J. Wong * local format to extents format, and if the file being repaired has a short
2702651923dSDarrick J. Wong * format data fork, turn it into an empty extent list.
2712651923dSDarrick J. Wong */
2722651923dSDarrick J. Wong STATIC int
xrep_symlink_swap_prep(struct xfs_scrub * sc,bool temp_local,bool ip_local)2732651923dSDarrick J. Wong xrep_symlink_swap_prep(
2742651923dSDarrick J. Wong struct xfs_scrub *sc,
2752651923dSDarrick J. Wong bool temp_local,
2762651923dSDarrick J. Wong bool ip_local)
2772651923dSDarrick J. Wong {
2782651923dSDarrick J. Wong int error;
2792651923dSDarrick J. Wong
2802651923dSDarrick J. Wong /*
2812651923dSDarrick J. Wong * If the temp link is in shortform format, convert that to a remote
2822651923dSDarrick J. Wong * target so that we can use the atomic mapping exchange.
2832651923dSDarrick J. Wong */
2842651923dSDarrick J. Wong if (temp_local) {
2852651923dSDarrick J. Wong int logflags = XFS_ILOG_CORE;
2862651923dSDarrick J. Wong
2872651923dSDarrick J. Wong error = xfs_bmap_local_to_extents(sc->tp, sc->tempip, 1,
2882651923dSDarrick J. Wong &logflags, XFS_DATA_FORK,
2892651923dSDarrick J. Wong xrep_symlink_local_to_remote,
2902651923dSDarrick J. Wong sc);
2912651923dSDarrick J. Wong if (error)
2922651923dSDarrick J. Wong return error;
2932651923dSDarrick J. Wong
2942651923dSDarrick J. Wong xfs_trans_log_inode(sc->tp, sc->ip, 0);
2952651923dSDarrick J. Wong
2962651923dSDarrick J. Wong error = xfs_defer_finish(&sc->tp);
2972651923dSDarrick J. Wong if (error)
2982651923dSDarrick J. Wong return error;
2992651923dSDarrick J. Wong }
3002651923dSDarrick J. Wong
3012651923dSDarrick J. Wong /*
3022651923dSDarrick J. Wong * If the file being repaired had a shortform data fork, convert that
3032651923dSDarrick J. Wong * to an empty extent list in preparation for the atomic mapping
3042651923dSDarrick J. Wong * exchange.
3052651923dSDarrick J. Wong */
3062651923dSDarrick J. Wong if (ip_local) {
3072651923dSDarrick J. Wong struct xfs_ifork *ifp;
3082651923dSDarrick J. Wong
3092651923dSDarrick J. Wong ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
3102651923dSDarrick J. Wong xfs_idestroy_fork(ifp);
3112651923dSDarrick J. Wong ifp->if_format = XFS_DINODE_FMT_EXTENTS;
3122651923dSDarrick J. Wong ifp->if_nextents = 0;
3132651923dSDarrick J. Wong ifp->if_bytes = 0;
3142651923dSDarrick J. Wong ifp->if_data = NULL;
3152651923dSDarrick J. Wong ifp->if_height = 0;
3162651923dSDarrick J. Wong
3172651923dSDarrick J. Wong xfs_trans_log_inode(sc->tp, sc->ip,
3182651923dSDarrick J. Wong XFS_ILOG_CORE | XFS_ILOG_DDATA);
3192651923dSDarrick J. Wong }
3202651923dSDarrick J. Wong
3212651923dSDarrick J. Wong return 0;
3222651923dSDarrick J. Wong }
3232651923dSDarrick J. Wong
3242651923dSDarrick J. Wong /* Exchange the temporary symlink's data fork with the one being repaired. */
3252651923dSDarrick J. Wong STATIC int
xrep_symlink_swap(struct xfs_scrub * sc)3262651923dSDarrick J. Wong xrep_symlink_swap(
3272651923dSDarrick J. Wong struct xfs_scrub *sc)
3282651923dSDarrick J. Wong {
3292651923dSDarrick J. Wong struct xrep_tempexch *tx = sc->buf;
3302651923dSDarrick J. Wong bool ip_local, temp_local;
3312651923dSDarrick J. Wong int error;
3322651923dSDarrick J. Wong
3332651923dSDarrick J. Wong ip_local = sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL;
3342651923dSDarrick J. Wong temp_local = sc->tempip->i_df.if_format == XFS_DINODE_FMT_LOCAL;
3352651923dSDarrick J. Wong
3362651923dSDarrick J. Wong /*
3372651923dSDarrick J. Wong * If the both links have a local format data fork and the rebuilt
3382651923dSDarrick J. Wong * remote data would fit in the repaired file's data fork, copy the
3392651923dSDarrick J. Wong * contents from the tempfile and declare ourselves done.
3402651923dSDarrick J. Wong */
3412651923dSDarrick J. Wong if (ip_local && temp_local &&
3422651923dSDarrick J. Wong sc->tempip->i_disk_size <= xfs_inode_data_fork_size(sc->ip)) {
3432651923dSDarrick J. Wong xrep_tempfile_copyout_local(sc, XFS_DATA_FORK);
3442651923dSDarrick J. Wong return 0;
3452651923dSDarrick J. Wong }
3462651923dSDarrick J. Wong
3472651923dSDarrick J. Wong /* Otherwise, make sure both data forks are in block-mapping mode. */
3482651923dSDarrick J. Wong error = xrep_symlink_swap_prep(sc, temp_local, ip_local);
3492651923dSDarrick J. Wong if (error)
3502651923dSDarrick J. Wong return error;
3512651923dSDarrick J. Wong
3522651923dSDarrick J. Wong return xrep_tempexch_contents(sc, tx);
3532651923dSDarrick J. Wong }
3542651923dSDarrick J. Wong
3552651923dSDarrick J. Wong /*
3562651923dSDarrick J. Wong * Free all the remote blocks and reset the data fork. The caller must join
3572651923dSDarrick J. Wong * the inode to the transaction. This function returns with the inode joined
3582651923dSDarrick J. Wong * to a clean scrub transaction.
3592651923dSDarrick J. Wong */
3602651923dSDarrick J. Wong STATIC int
xrep_symlink_reset_fork(struct xfs_scrub * sc)3612651923dSDarrick J. Wong xrep_symlink_reset_fork(
3622651923dSDarrick J. Wong struct xfs_scrub *sc)
3632651923dSDarrick J. Wong {
3642651923dSDarrick J. Wong struct xfs_ifork *ifp = xfs_ifork_ptr(sc->tempip, XFS_DATA_FORK);
3652651923dSDarrick J. Wong int error;
3662651923dSDarrick J. Wong
3672651923dSDarrick J. Wong /* Unmap all the remote target buffers. */
3682651923dSDarrick J. Wong if (xfs_ifork_has_extents(ifp)) {
3692651923dSDarrick J. Wong error = xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK);
3702651923dSDarrick J. Wong if (error)
3712651923dSDarrick J. Wong return error;
3722651923dSDarrick J. Wong }
3732651923dSDarrick J. Wong
3742651923dSDarrick J. Wong trace_xrep_symlink_reset_fork(sc->tempip);
3752651923dSDarrick J. Wong
3762651923dSDarrick J. Wong /* Reset the temp symlink target to dummy content. */
3772651923dSDarrick J. Wong xfs_idestroy_fork(ifp);
3782651923dSDarrick J. Wong return xfs_symlink_write_target(sc->tp, sc->tempip, sc->tempip->i_ino,
3792651923dSDarrick J. Wong "?", 1, 0, 0);
3802651923dSDarrick J. Wong }
3812651923dSDarrick J. Wong
3822651923dSDarrick J. Wong /*
3832651923dSDarrick J. Wong * Reinitialize a link target. Caller must ensure the inode is joined to
3842651923dSDarrick J. Wong * the transaction.
3852651923dSDarrick J. Wong */
3862651923dSDarrick J. Wong STATIC int
xrep_symlink_rebuild(struct xfs_scrub * sc)3872651923dSDarrick J. Wong xrep_symlink_rebuild(
3882651923dSDarrick J. Wong struct xfs_scrub *sc)
3892651923dSDarrick J. Wong {
3902651923dSDarrick J. Wong struct xrep_tempexch *tx;
3912651923dSDarrick J. Wong char *target_buf = sc->buf;
3922651923dSDarrick J. Wong xfs_fsblock_t fs_blocks;
3932651923dSDarrick J. Wong unsigned int target_len;
3942651923dSDarrick J. Wong unsigned int resblks;
3952651923dSDarrick J. Wong int error;
3962651923dSDarrick J. Wong
3972651923dSDarrick J. Wong /* How many blocks do we need? */
3982651923dSDarrick J. Wong target_len = strlen(target_buf);
3992651923dSDarrick J. Wong ASSERT(target_len != 0);
4002651923dSDarrick J. Wong if (target_len == 0 || target_len > XFS_SYMLINK_MAXLEN)
4012651923dSDarrick J. Wong return -EFSCORRUPTED;
4022651923dSDarrick J. Wong
4032651923dSDarrick J. Wong trace_xrep_symlink_rebuild(sc->ip);
4042651923dSDarrick J. Wong
4052651923dSDarrick J. Wong /*
4062651923dSDarrick J. Wong * In preparation to write the new symlink target to the temporary
4072651923dSDarrick J. Wong * file, drop the ILOCK of the file being repaired (it shouldn't be
4082651923dSDarrick J. Wong * joined) and take the ILOCK of the temporary file.
4092651923dSDarrick J. Wong *
4102651923dSDarrick J. Wong * The VFS does not take the IOLOCK while reading a symlink (and new
4112651923dSDarrick J. Wong * symlinks are hidden with INEW until they've been written) so it's
4122651923dSDarrick J. Wong * possible that a readlink() could see the old corrupted contents
4132651923dSDarrick J. Wong * while we're doing this.
4142651923dSDarrick J. Wong */
4152651923dSDarrick J. Wong xchk_iunlock(sc, XFS_ILOCK_EXCL);
4162651923dSDarrick J. Wong xrep_tempfile_ilock(sc);
4172651923dSDarrick J. Wong xfs_trans_ijoin(sc->tp, sc->tempip, 0);
4182651923dSDarrick J. Wong
4192651923dSDarrick J. Wong /*
4202651923dSDarrick J. Wong * Reserve resources to reinitialize the target. We're allowed to
4212651923dSDarrick J. Wong * exceed file quota to repair inconsistent metadata, though this is
4222651923dSDarrick J. Wong * unlikely.
4232651923dSDarrick J. Wong */
4242651923dSDarrick J. Wong fs_blocks = xfs_symlink_blocks(sc->mp, target_len);
4255d31a85dSAllison Henderson resblks = xfs_symlink_space_res(sc->mp, target_len, fs_blocks);
4262651923dSDarrick J. Wong error = xfs_trans_reserve_quota_nblks(sc->tp, sc->tempip, resblks, 0,
4272651923dSDarrick J. Wong true);
4282651923dSDarrick J. Wong if (error)
4292651923dSDarrick J. Wong return error;
4302651923dSDarrick J. Wong
4312651923dSDarrick J. Wong /* Erase the dummy target set up by the tempfile initialization. */
4322651923dSDarrick J. Wong xfs_idestroy_fork(&sc->tempip->i_df);
4332651923dSDarrick J. Wong sc->tempip->i_df.if_bytes = 0;
4342651923dSDarrick J. Wong sc->tempip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
4352651923dSDarrick J. Wong
4362651923dSDarrick J. Wong /* Write the salvaged target to the temporary link. */
4372651923dSDarrick J. Wong error = xfs_symlink_write_target(sc->tp, sc->tempip, sc->ip->i_ino,
4382651923dSDarrick J. Wong target_buf, target_len, fs_blocks, resblks);
4392651923dSDarrick J. Wong if (error)
4402651923dSDarrick J. Wong return error;
4412651923dSDarrick J. Wong
4422651923dSDarrick J. Wong /*
4432651923dSDarrick J. Wong * Commit the repair transaction so that we can use the atomic mapping
4442651923dSDarrick J. Wong * exchange functions to compute the correct block reservations and
4452651923dSDarrick J. Wong * re-lock the inodes.
4462651923dSDarrick J. Wong */
4472651923dSDarrick J. Wong target_buf = NULL;
4482651923dSDarrick J. Wong error = xrep_trans_commit(sc);
4492651923dSDarrick J. Wong if (error)
4502651923dSDarrick J. Wong return error;
4512651923dSDarrick J. Wong
4522651923dSDarrick J. Wong /* Last chance to abort before we start committing fixes. */
4532651923dSDarrick J. Wong if (xchk_should_terminate(sc, &error))
4542651923dSDarrick J. Wong return error;
4552651923dSDarrick J. Wong
4562651923dSDarrick J. Wong xrep_tempfile_iunlock(sc);
4572651923dSDarrick J. Wong
4582651923dSDarrick J. Wong /*
4592651923dSDarrick J. Wong * We're done with the temporary buffer, so we can reuse it for the
4602651923dSDarrick J. Wong * tempfile contents exchange information.
4612651923dSDarrick J. Wong */
4622651923dSDarrick J. Wong tx = sc->buf;
4632651923dSDarrick J. Wong error = xrep_tempexch_trans_alloc(sc, XFS_DATA_FORK, tx);
4642651923dSDarrick J. Wong if (error)
4652651923dSDarrick J. Wong return error;
4662651923dSDarrick J. Wong
4672651923dSDarrick J. Wong /*
4682651923dSDarrick J. Wong * Exchange the temp link's data fork with the file being repaired.
4692651923dSDarrick J. Wong * This recreates the transaction and takes the ILOCKs of the file
4702651923dSDarrick J. Wong * being repaired and the temporary file.
4712651923dSDarrick J. Wong */
4722651923dSDarrick J. Wong error = xrep_symlink_swap(sc);
4732651923dSDarrick J. Wong if (error)
4742651923dSDarrick J. Wong return error;
4752651923dSDarrick J. Wong
4762651923dSDarrick J. Wong /*
4772651923dSDarrick J. Wong * Release the old symlink blocks and reset the data fork of the temp
4782651923dSDarrick J. Wong * link to an empty shortform link. This is the last repair action we
4792651923dSDarrick J. Wong * perform on the symlink, so we don't need to clean the transaction.
4802651923dSDarrick J. Wong */
4812651923dSDarrick J. Wong return xrep_symlink_reset_fork(sc);
4822651923dSDarrick J. Wong }
4832651923dSDarrick J. Wong
4842651923dSDarrick J. Wong /* Repair a symbolic link. */
4852651923dSDarrick J. Wong int
xrep_symlink(struct xfs_scrub * sc)4862651923dSDarrick J. Wong xrep_symlink(
4872651923dSDarrick J. Wong struct xfs_scrub *sc)
4882651923dSDarrick J. Wong {
4892651923dSDarrick J. Wong int error;
4902651923dSDarrick J. Wong
4912651923dSDarrick J. Wong /* The rmapbt is required to reap the old data fork. */
4922651923dSDarrick J. Wong if (!xfs_has_rmapbt(sc->mp))
4932651923dSDarrick J. Wong return -EOPNOTSUPP;
4946d335233SDarrick J. Wong /* We require atomic file exchange range to rebuild anything. */
4956d335233SDarrick J. Wong if (!xfs_has_exchange_range(sc->mp))
4966d335233SDarrick J. Wong return -EOPNOTSUPP;
4972651923dSDarrick J. Wong
4982651923dSDarrick J. Wong ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
4992651923dSDarrick J. Wong
5002651923dSDarrick J. Wong error = xrep_symlink_salvage(sc);
5012651923dSDarrick J. Wong if (error)
5022651923dSDarrick J. Wong return error;
5032651923dSDarrick J. Wong
5042651923dSDarrick J. Wong /* Now reset the target. */
5052651923dSDarrick J. Wong error = xrep_symlink_rebuild(sc);
5062651923dSDarrick J. Wong if (error)
5072651923dSDarrick J. Wong return error;
5082651923dSDarrick J. Wong
5092651923dSDarrick J. Wong return xrep_trans_commit(sc);
5102651923dSDarrick J. Wong }
511