LCOV - code coverage report
Current view: top level - fs/xfs/scrub - symlink_repair.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 147 161 91.3 %
Date: 2023-07-31 20:08:27 Functions: 10 10 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2018-2023 Oracle.  All Rights Reserved.
       4             :  * Author: Darrick J. Wong <djwong@kernel.org>
       5             :  */
       6             : #include "xfs.h"
       7             : #include "xfs_fs.h"
       8             : #include "xfs_shared.h"
       9             : #include "xfs_format.h"
      10             : #include "xfs_trans_resv.h"
      11             : #include "xfs_mount.h"
      12             : #include "xfs_defer.h"
      13             : #include "xfs_btree.h"
      14             : #include "xfs_bit.h"
      15             : #include "xfs_log_format.h"
      16             : #include "xfs_trans.h"
      17             : #include "xfs_sb.h"
      18             : #include "xfs_inode.h"
      19             : #include "xfs_inode_fork.h"
      20             : #include "xfs_symlink.h"
      21             : #include "xfs_bmap.h"
      22             : #include "xfs_quota.h"
      23             : #include "xfs_da_format.h"
      24             : #include "xfs_da_btree.h"
      25             : #include "xfs_bmap_btree.h"
      26             : #include "xfs_trans_space.h"
      27             : #include "xfs_symlink_remote.h"
      28             : #include "xfs_swapext.h"
      29             : #include "xfs_xchgrange.h"
      30             : #include "scrub/xfs_scrub.h"
      31             : #include "scrub/scrub.h"
      32             : #include "scrub/common.h"
      33             : #include "scrub/trace.h"
      34             : #include "scrub/repair.h"
      35             : #include "scrub/tempfile.h"
      36             : #include "scrub/tempswap.h"
      37             : #include "scrub/reap.h"
      38             : 
      39             : /*
      40             :  * Symbolic Link Repair
      41             :  * ====================
      42             :  *
      43             :  * We repair symbolic links by reading whatever target data we can find, up to
      44             :  * the first NULL byte.  Zero length symlinks are turned into links to the
      45             :  * current directory.  The new target is written into a private hidden
      46             :  * temporary file, and then an atomic extent swap commits the new symlink
      47             :  * target to the file being repaired.
      48             :  */
      49             : 
      50             : /* Set us up to repair the rtsummary file. */
      51             : int
      52        7520 : xrep_setup_symlink(
      53             :         struct xfs_scrub        *sc,
      54             :         unsigned int            *resblks)
      55             : {
      56        7520 :         struct xfs_mount        *mp = sc->mp;
      57        7520 :         unsigned long long      blocks;
      58        7520 :         int                     error;
      59             : 
      60        7520 :         error = xrep_tempfile_create(sc, S_IFLNK);
      61        7520 :         if (error)
      62             :                 return error;
      63             : 
      64             :         /*
      65             :          * If we're doing a repair, we reserve enough blocks to write out a
      66             :          * completely new symlink file, plus twice as many blocks as we would
      67             :          * need if we can only allocate one block per data fork mapping.  This
      68             :          * should cover the preallocation of the temporary file and swapping
      69             :          * the extent mappings.
      70             :          *
      71             :          * We cannot use xfs_swapext_estimate because we have not yet
      72             :          * constructed the replacement rtsummary and therefore do not know how
      73             :          * many extents it will use.  By the time we do, we will have a dirty
      74             :          * transaction (which we cannot drop because we cannot drop the
      75             :          * rtsummary ILOCK) and cannot ask for more reservation.
      76             :          */
      77        7520 :         blocks = xfs_symlink_blocks(sc->mp, XFS_SYMLINK_MAXLEN);
      78        7520 :         blocks += xfs_bmbt_calc_size(mp, blocks) * 2;
      79        7520 :         if (blocks > UINT_MAX)
      80             :                 return -EOPNOTSUPP;
      81             : 
      82        7520 :         *resblks += blocks;
      83        7520 :         return 0;
      84             : }
      85             : 
      86             : /* Try to salvage the pathname from rmt blocks. */
      87             : STATIC int
      88        5964 : xrep_symlink_salvage_remote(
      89             :         struct xfs_scrub        *sc)
      90             : {
      91        5964 :         struct xfs_bmbt_irec    mval[XFS_SYMLINK_MAPS];
      92        5964 :         struct xfs_inode        *ip = sc->ip;
      93        5964 :         struct xfs_buf          *bp;
      94        5964 :         char                    *target_buf = sc->buf;
      95        5964 :         xfs_failaddr_t          fa;
      96        5964 :         xfs_filblks_t           fsblocks;
      97        5964 :         xfs_daddr_t             d;
      98        5964 :         loff_t                  len;
      99        5964 :         loff_t                  offset;
     100        5964 :         unsigned int            byte_cnt;
     101        5964 :         bool                    magic_ok;
     102        5964 :         bool                    hdr_ok;
     103        5964 :         int                     n;
     104        5964 :         int                     nmaps = XFS_SYMLINK_MAPS;
     105        5964 :         int                     error;
     106             : 
     107             :         /* We'll only read until the buffer is full. */
     108        5964 :         len = min_t(loff_t, ip->i_disk_size, XFS_SYMLINK_MAXLEN);
     109        5964 :         fsblocks = xfs_symlink_blocks(sc->mp, len);
     110        5964 :         error = xfs_bmapi_read(ip, 0, fsblocks, mval, &nmaps, 0);
     111        5964 :         if (error)
     112             :                 return error;
     113             : 
     114             :         offset = 0;
     115       11928 :         for (n = 0; n < nmaps; n++) {
     116        5964 :                 struct xfs_dsymlink_hdr *dsl;
     117             : 
     118        5964 :                 d = XFS_FSB_TO_DADDR(sc->mp, mval[n].br_startblock);
     119             : 
     120             :                 /* Read the rmt block.  We'll run the verifiers manually. */
     121        5964 :                 error = xfs_trans_read_buf(sc->mp, sc->tp, sc->mp->m_ddev_targp,
     122        5964 :                                 d, XFS_FSB_TO_BB(sc->mp, mval[n].br_blockcount),
     123             :                                 0, &bp, NULL);
     124        5964 :                 if (error)
     125           0 :                         return error;
     126        5964 :                 bp->b_ops = &xfs_symlink_buf_ops;
     127             : 
     128             :                 /* How many bytes do we expect to get out of this buffer? */
     129        5964 :                 byte_cnt = XFS_FSB_TO_B(sc->mp, mval[n].br_blockcount);
     130        5964 :                 byte_cnt = XFS_SYMLINK_BUF_SPACE(sc->mp, byte_cnt);
     131        5964 :                 byte_cnt = min_t(unsigned int, byte_cnt, len);
     132             : 
     133             :                 /*
     134             :                  * See if the verifiers accept this block.  We're willing to
     135             :                  * salvage if the if the offset/byte/ino are ok and either the
     136             :                  * verifier passed or the magic is ok.  Anything else and we
     137             :                  * stop dead in our tracks.
     138             :                  */
     139        5964 :                 fa = bp->b_ops->verify_struct(bp);
     140        5964 :                 dsl = bp->b_addr;
     141        5964 :                 magic_ok = dsl->sl_magic == cpu_to_be32(XFS_SYMLINK_MAGIC);
     142        5964 :                 hdr_ok = xfs_symlink_hdr_ok(ip->i_ino, offset, byte_cnt, bp);
     143        5964 :                 if (!hdr_ok || (fa != NULL && !magic_ok))
     144             :                         break;
     145             : 
     146       11928 :                 memcpy(target_buf + offset, dsl + 1, byte_cnt);
     147             : 
     148        5964 :                 len -= byte_cnt;
     149        5964 :                 offset += byte_cnt;
     150             :         }
     151             : 
     152             :         /* Ensure we have a zero at the end, and /some/ contents. */
     153        5964 :         if (offset == 0 || target_buf[0] == 0)
     154           0 :                 sprintf(target_buf, ".");
     155             :         else
     156        5964 :                 target_buf[offset] = 0;
     157             :         return 0;
     158             : }
     159             : 
     160             : /*
     161             :  * Try to salvage an inline symlink's contents.  Empty symlinks become a link
     162             :  * to the current directory.
     163             :  */
     164             : STATIC void
     165        1405 : xrep_symlink_salvage_inline(
     166             :         struct xfs_scrub        *sc)
     167             : {
     168        1405 :         struct xfs_inode        *ip = sc->ip;
     169        1405 :         char                    *target_buf = sc->buf;
     170        1405 :         struct xfs_ifork        *ifp;
     171             : 
     172        1405 :         ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
     173        1405 :         if (ifp->if_u1.if_data)
     174        1405 :                 strncpy(target_buf, ifp->if_u1.if_data, xfs_inode_data_fork_size(ip));
     175        1405 :         if (target_buf[0] == 0)
     176           0 :                 sprintf(target_buf, ".");
     177        1405 : }
     178             : 
     179             : /* Salvage whatever we can of the target. */
     180             : STATIC int
     181        7369 : xrep_symlink_salvage(
     182             :         struct xfs_scrub        *sc)
     183             : {
     184        7369 :         if (sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
     185        1405 :                 xrep_symlink_salvage_inline(sc);
     186             :         } else {
     187        5964 :                 int             error = xrep_symlink_salvage_remote(sc);
     188             : 
     189        5964 :                 if (error)
     190             :                         return error;
     191             :         }
     192             : 
     193        7369 :         trace_xrep_symlink_salvage_target(sc->ip, sc->buf, strlen(sc->buf));
     194        7369 :         return 0;
     195             : }
     196             : 
     197             : STATIC void
     198        1043 : xrep_symlink_local_to_remote(
     199             :         struct xfs_trans        *tp,
     200             :         struct xfs_buf          *bp,
     201             :         struct xfs_inode        *ip,
     202             :         struct xfs_ifork        *ifp,
     203             :         void                    *priv)
     204             : {
     205        1043 :         struct xfs_scrub        *sc = priv;
     206        1043 :         struct xfs_dsymlink_hdr *dsl = bp->b_addr;
     207             : 
     208        1043 :         xfs_symlink_local_to_remote(tp, bp, ip, ifp, NULL);
     209             : 
     210        1043 :         if (!xfs_has_crc(sc->mp))
     211             :                 return;
     212             : 
     213        1043 :         dsl->sl_owner = cpu_to_be64(sc->ip->i_ino);
     214        1043 :         xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsymlink_hdr) +
     215        1043 :                                         ifp->if_bytes - 1);
     216             : }
     217             : 
     218             : /*
     219             :  * Prepare both links' data forks for extent swapping.  Promote the tempfile
     220             :  * from local format to extents format, and if the file being repaired has a
     221             :  * short format data fork, turn it into an empty extent list.
     222             :  */
     223             : STATIC int
     224        5964 : xrep_symlink_swap_prep(
     225             :         struct xfs_scrub        *sc,
     226             :         bool                    temp_local,
     227             :         bool                    ip_local)
     228             : {
     229        5964 :         int                     error;
     230             : 
     231             :         /*
     232             :          * If the temp link is in shortform format, convert that to a remote
     233             :          * target so that we can use the atomic extent swap.
     234             :          */
     235        5964 :         if (temp_local) {
     236        1043 :                 int             logflags = XFS_ILOG_CORE;
     237             : 
     238        1043 :                 error = xfs_bmap_local_to_extents(sc->tp, sc->tempip, 1,
     239             :                                 &logflags, XFS_DATA_FORK,
     240             :                                 xrep_symlink_local_to_remote,
     241             :                                 sc);
     242        1043 :                 if (error)
     243           0 :                         return error;
     244             : 
     245        1043 :                 xfs_trans_log_inode(sc->tp, sc->ip, 0);
     246             : 
     247        1043 :                 error = xfs_defer_finish(&sc->tp);
     248        1043 :                 if (error)
     249             :                         return error;
     250             :         }
     251             : 
     252             :         /*
     253             :          * If the file being repaired had a shortform data fork, convert that
     254             :          * to an empty extent list in preparation for the atomic extent swap.
     255             :          */
     256        5964 :         if (ip_local) {
     257           0 :                 struct xfs_ifork        *ifp;
     258             : 
     259           0 :                 ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
     260           0 :                 xfs_idestroy_fork(ifp);
     261           0 :                 ifp->if_format = XFS_DINODE_FMT_EXTENTS;
     262           0 :                 ifp->if_nextents = 0;
     263           0 :                 ifp->if_bytes = 0;
     264           0 :                 ifp->if_u1.if_root = NULL;
     265           0 :                 ifp->if_height = 0;
     266             : 
     267           0 :                 xfs_trans_log_inode(sc->tp, sc->ip,
     268             :                                 XFS_ILOG_CORE | XFS_ILOG_DDATA);
     269             :         }
     270             : 
     271             :         return 0;
     272             : }
     273             : 
     274             : /* Swap the temporary link's data fork with the one being repaired. */
     275             : STATIC int
     276        7369 : xrep_symlink_swap(
     277             :         struct xfs_scrub        *sc)
     278             : {
     279        7369 :         struct xrep_tempswap    *tx = sc->buf;
     280        7369 :         bool                    ip_local, temp_local;
     281        7369 :         int                     error;
     282             : 
     283             :         /*
     284             :          * We're done with the temporary buffer, so we can reuse it for the
     285             :          * tempfile swap information.
     286             :          */
     287        7369 :         error = xrep_tempswap_trans_alloc(sc, XFS_DATA_FORK, tx);
     288        7369 :         if (error)
     289             :                 return error;
     290             : 
     291        7369 :         ip_local = sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL;
     292        7369 :         temp_local = sc->tempip->i_df.if_format == XFS_DINODE_FMT_LOCAL;
     293             : 
     294             :         /*
     295             :          * If the both links have a local format data fork and the rebuilt
     296             :          * remote data would fit in the repaired file's data fork, copy the
     297             :          * contents from the tempfile and declare ourselves done.
     298             :          */
     299        7369 :         if (ip_local && temp_local &&
     300        1405 :             sc->tempip->i_disk_size <= xfs_inode_data_fork_size(sc->ip)) {
     301        1405 :                 xrep_tempfile_copyout_local(sc, XFS_DATA_FORK);
     302        1405 :                 return 0;
     303             :         }
     304             : 
     305             :         /* Otherwise, make sure both data forks are in block-mapping mode. */
     306        5964 :         error = xrep_symlink_swap_prep(sc, temp_local, ip_local);
     307        5964 :         if (error)
     308             :                 return error;
     309             : 
     310        5964 :         return xrep_tempswap_contents(sc, tx);
     311             : }
     312             : 
     313             : /*
     314             :  * Free all the remote blocks and reset the data fork.  The caller must join
     315             :  * the inode to the transaction.  This function returns with the inode joined
     316             :  * to a clean scrub transaction.
     317             :  */
     318             : STATIC int
     319        7369 : xrep_symlink_reset_fork(
     320             :         struct xfs_scrub        *sc)
     321             : {
     322        7369 :         struct xfs_ifork        *ifp = xfs_ifork_ptr(sc->tempip, XFS_DATA_FORK);
     323        7369 :         int                     error;
     324             : 
     325             :         /* Unmap all the remote target buffers. */
     326        7369 :         if (xfs_ifork_has_extents(ifp)) {
     327        5964 :                 error = xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK);
     328        5964 :                 if (error)
     329             :                         return error;
     330             :         }
     331             : 
     332        7369 :         trace_xrep_symlink_reset_fork(sc->tempip);
     333             : 
     334             :         /* Reset the temp link to have the same dummy content. */
     335        7369 :         xfs_idestroy_fork(ifp);
     336        7369 :         error = xfs_symlink_write_target(sc->tp, sc->tempip, ".", 1, 0, 0);
     337        7369 :         if (error)
     338             :                 return error;
     339             : 
     340        7369 :         return xrep_tempfile_roll_trans(sc);
     341             : }
     342             : 
     343             : /*
     344             :  * Reinitialize a link target.  Caller must ensure the inode is joined to
     345             :  * the transaction.
     346             :  */
     347             : STATIC int
     348        7369 : xrep_symlink_rebuild(
     349             :         struct xfs_scrub        *sc)
     350             : {
     351        7369 :         char                    *target_buf = sc->buf;
     352        7369 :         xfs_fsblock_t           fs_blocks;
     353        7369 :         unsigned int            target_len;
     354        7369 :         unsigned int            resblks;
     355        7369 :         int                     error;
     356             : 
     357             :         /* How many blocks do we need? */
     358        7369 :         target_len = strlen(target_buf);
     359        7369 :         ASSERT(target_len != 0);
     360        7369 :         if (target_len == 0 || target_len > XFS_SYMLINK_MAXLEN)
     361             :                 return -EFSCORRUPTED;
     362             : 
     363        7369 :         trace_xrep_symlink_rebuild(sc->ip);
     364             : 
     365             :         /*
     366             :          * In preparation to write the new symlink target to the temporary
     367             :          * file, drop the ILOCK of the file being repaired (it shouldn't be
     368             :          * joined) and take the ILOCK of the temporary file.
     369             :          *
     370             :          * The VFS does not take the IOLOCK while reading a symlink (and new
     371             :          * symlinks are hidden with INEW until they've been written) so it's
     372             :          * possible that a readlink() could see the old corrupted contents
     373             :          * while we're doing this.
     374             :          */
     375        7369 :         xchk_iunlock(sc, XFS_ILOCK_EXCL);
     376        7369 :         xrep_tempfile_ilock(sc);
     377        7369 :         xfs_trans_ijoin(sc->tp, sc->tempip, 0);
     378             : 
     379             :         /*
     380             :          * Reserve resources to reinitialize the target.  We're allowed to
     381             :          * exceed file quota to repair inconsistent metadata, though this is
     382             :          * unlikely.
     383             :          */
     384        7369 :         fs_blocks = xfs_symlink_blocks(sc->mp, target_len);
     385        7369 :         resblks = xfs_symlink_space_res(sc->mp, target_len, fs_blocks);
     386        7369 :         error = xfs_trans_reserve_quota_nblks(sc->tp, sc->tempip, resblks, 0,
     387             :                         true);
     388        7369 :         if (error)
     389             :                 return error;
     390             : 
     391             :         /* Erase the dummy target set up by the tempfile initialization. */
     392        7369 :         xfs_idestroy_fork(&sc->tempip->i_df);
     393        7369 :         sc->tempip->i_df.if_bytes = 0;
     394        7369 :         sc->tempip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
     395             : 
     396             :         /* Write the salvaged target to the temporary link. */
     397        7369 :         error = __xfs_symlink_write_target(sc->tp, sc->tempip, sc->ip->i_ino,
     398             :                         target_buf, target_len, fs_blocks, resblks);
     399        7369 :         if (error)
     400             :                 return error;
     401             : 
     402             :         /*
     403             :          * Commit the repair transaction so that we can use the atomic extent
     404             :          * swap helper functions to compute the correct block reservations and
     405             :          * re-lock the inodes.
     406             :          */
     407        7369 :         error = xrep_trans_commit(sc);
     408        7369 :         if (error)
     409             :                 return error;
     410             : 
     411             :         /* Last chance to abort before we start committing fixes. */
     412        7369 :         if (xchk_should_terminate(sc, &error))
     413           0 :                 return error;
     414             : 
     415        7369 :         xrep_tempfile_iunlock(sc);
     416             : 
     417             :         /*
     418             :          * Swap the temp link's data fork with the file being repaired.  This
     419             :          * recreates the transaction and takes the ILOCKs of the file being
     420             :          * repaired and the temporary file.
     421             :          */
     422        7369 :         error = xrep_symlink_swap(sc);
     423        7369 :         if (error)
     424             :                 return error;
     425             : 
     426             :         /*
     427             :          * Release the old symlink blocks and reset the data fork of the temp
     428             :          * link to an empty shortform link.
     429             :          */
     430        7369 :         return xrep_symlink_reset_fork(sc);
     431             : }
     432             : 
     433             : /* Repair a symbolic link. */
     434             : int
     435        7369 : xrep_symlink(
     436             :         struct xfs_scrub        *sc)
     437             : {
     438        7369 :         int                     error;
     439             : 
     440             :         /* The rmapbt is required to reap the old data fork. */
     441        7369 :         if (!xfs_has_rmapbt(sc->mp))
     442             :                 return -EOPNOTSUPP;
     443             : 
     444        7369 :         ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);
     445             : 
     446        7369 :         error = xrep_symlink_salvage(sc);
     447        7369 :         if (error)
     448             :                 return error;
     449             : 
     450             :         /* Now reset the target. */
     451        7369 :         return xrep_symlink_rebuild(sc);
     452             : }

Generated by: LCOV version 1.14