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

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2021-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_log_format.h"
      13             : #include "xfs_trans.h"
      14             : #include "xfs_inode.h"
      15             : #include "xfs_ialloc.h"
      16             : #include "xfs_quota.h"
      17             : #include "xfs_bmap.h"
      18             : #include "xfs_bmap_btree.h"
      19             : #include "xfs_trans_space.h"
      20             : #include "xfs_dir2.h"
      21             : #include "xfs_xchgrange.h"
      22             : #include "xfs_swapext.h"
      23             : #include "xfs_defer.h"
      24             : #include "xfs_symlink_remote.h"
      25             : #include "scrub/scrub.h"
      26             : #include "scrub/common.h"
      27             : #include "scrub/repair.h"
      28             : #include "scrub/trace.h"
      29             : #include "scrub/tempfile.h"
      30             : #include "scrub/tempswap.h"
      31             : #include "scrub/xfile.h"
      32             : 
      33             : /*
      34             :  * Create a temporary file for reconstructing metadata, with the intention of
      35             :  * atomically swapping the temporary file's contents with the file that's
      36             :  * being repaired.
      37             :  */
      38             : int
      39      554785 : xrep_tempfile_create(
      40             :         struct xfs_scrub        *sc,
      41             :         uint16_t                mode)
      42             : {
      43      554785 :         struct xfs_icreate_args args = { .pip = sc->mp->m_rootip, };
      44      554785 :         struct xfs_mount        *mp = sc->mp;
      45      554785 :         struct xfs_trans        *tp = NULL;
      46      554785 :         struct xfs_dquot        *udqp;
      47      554785 :         struct xfs_dquot        *gdqp;
      48      554785 :         struct xfs_dquot        *pdqp;
      49      554785 :         struct xfs_trans_res    *tres;
      50      554785 :         struct xfs_inode        *dp = mp->m_rootip;
      51      554785 :         xfs_ino_t               ino;
      52      554785 :         unsigned int            resblks;
      53      554785 :         bool                    is_dir = S_ISDIR(mode);
      54      554785 :         int                     error;
      55             : 
      56     1109570 :         if (xfs_is_shutdown(mp))
      57             :                 return -EIO;
      58     1109570 :         if (xfs_is_readonly(mp))
      59             :                 return -EROFS;
      60             : 
      61      554785 :         ASSERT(sc->tp == NULL);
      62      554785 :         ASSERT(sc->tempip == NULL);
      63             : 
      64             :         /* Force everything to have the root ids and mode we want. */
      65      554785 :         xfs_icreate_args_rootfile(&args, mp, mode, false);
      66             : 
      67             :         /*
      68             :          * Make sure that we have allocated dquot(s) on disk.  The temporary
      69             :          * inode should be completely root owned so that we don't fail due to
      70             :          * quota limits.
      71             :          */
      72      554797 :         error = xfs_icreate_dqalloc(&args, &udqp, &gdqp, &pdqp);
      73      554910 :         if (error)
      74             :                 return error;
      75             : 
      76      554910 :         if (is_dir) {
      77       25089 :                 resblks = xfs_mkdir_space_res(mp, 0);
      78       25089 :                 tres = &M_RES(mp)->tr_mkdir;
      79             :         } else {
      80      529821 :                 resblks = XFS_IALLOC_SPACE_RES(mp);
      81      529821 :                 tres = &M_RES(mp)->tr_create_tmpfile;
      82             :         }
      83             : 
      84      554910 :         error = xfs_trans_alloc_icreate(mp, tres, udqp, gdqp, pdqp, resblks,
      85             :                         &tp);
      86      554910 :         if (error)
      87           0 :                 goto out_release_dquots;
      88             : 
      89             :         /* Allocate inode, set up directory. */
      90      554910 :         error = xfs_dialloc(&tp, dp, mode, &ino);
      91      554903 :         if (error)
      92           0 :                 goto out_trans_cancel;
      93      554903 :         error = xfs_icreate(tp, ino, &args, &sc->tempip);
      94      554910 :         if (error)
      95           0 :                 goto out_trans_cancel;
      96             : 
      97             :         /* We don't touch file data, so drop the realtime flags. */
      98      554910 :         sc->tempip->i_diflags &= ~(XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT);
      99      554910 :         xfs_trans_log_inode(tp, sc->tempip, XFS_ILOG_CORE);
     100             : 
     101             :         /*
     102             :          * Mark our temporary file as private so that LSMs and the ACL code
     103             :          * don't try to add their own metadata or reason about these files.
     104             :          * The file should never be exposed to userspace.
     105             :          */
     106      554902 :         VFS_I(sc->tempip)->i_flags |= S_PRIVATE;
     107      554902 :         VFS_I(sc->tempip)->i_opflags &= ~IOP_XATTR;
     108             : 
     109      554902 :         if (is_dir) {
     110       25081 :                 error = xfs_dir_init(tp, sc->tempip, dp);
     111       25061 :                 if (error)
     112           0 :                         goto out_trans_cancel;
     113      529821 :         } else if (S_ISLNK(VFS_I(sc->tempip)->i_mode)) {
     114        7520 :                 error = xfs_symlink_write_target(tp, sc->tempip, ".", 1, 0, 0);
     115        7520 :                 if (error)
     116           0 :                         goto out_trans_cancel;
     117             :         }
     118             : 
     119             :         /*
     120             :          * Attach the dquot(s) to the inodes and modify them incore.
     121             :          * These ids of the inode couldn't have changed since the new
     122             :          * inode has been locked ever since it was created.
     123             :          */
     124      554882 :         xfs_qm_vop_create_dqattach(tp, sc->tempip, udqp, gdqp, pdqp);
     125             : 
     126             :         /*
     127             :          * Put our temp file on the unlinked list so it's purged automatically.
     128             :          * Anything being reconstructed using this file must be atomically
     129             :          * swapped with the original file because the contents here will be
     130             :          * purged when the inode is dropped or log recovery cleans out the
     131             :          * unlinked list.
     132             :          */
     133      554910 :         error = xfs_iunlink(tp, sc->tempip);
     134      554902 :         if (error)
     135           0 :                 goto out_trans_cancel;
     136             : 
     137      554902 :         error = xfs_trans_commit(tp);
     138      554909 :         if (error)
     139           0 :                 goto out_release_inode;
     140             : 
     141      554909 :         trace_xrep_tempfile_create(sc);
     142             : 
     143      554910 :         xfs_qm_dqrele(udqp);
     144      554910 :         xfs_qm_dqrele(gdqp);
     145      554909 :         xfs_qm_dqrele(pdqp);
     146             : 
     147             :         /* Finish setting up the incore / vfs context. */
     148      554910 :         xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);
     149      554910 :         xfs_setup_iops(sc->tempip);
     150      554908 :         xfs_finish_inode_setup(sc->tempip);
     151             : 
     152      554910 :         sc->temp_ilock_flags = 0;
     153      554910 :         return error;
     154             : 
     155           0 : out_trans_cancel:
     156           0 :         xfs_trans_cancel(tp);
     157           0 : out_release_inode:
     158             :         /*
     159             :          * Wait until after the current transaction is aborted to finish the
     160             :          * setup of the inode and release the inode.  This prevents recursive
     161             :          * transactions and deadlocks from xfs_inactive.
     162             :          */
     163           0 :         if (sc->tempip) {
     164           0 :                 xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);
     165           0 :                 xfs_finish_inode_setup(sc->tempip);
     166           0 :                 xchk_irele(sc, sc->tempip);
     167             :         }
     168           0 : out_release_dquots:
     169           0 :         xfs_qm_dqrele(udqp);
     170           0 :         xfs_qm_dqrele(gdqp);
     171           0 :         xfs_qm_dqrele(pdqp);
     172             : 
     173           0 :         return error;
     174             : }
     175             : 
     176             : /* Take IOLOCK_EXCL on the temporary file, maybe. */
     177             : bool
     178      533361 : xrep_tempfile_iolock_nowait(
     179             :         struct xfs_scrub        *sc)
     180             : {
     181      533361 :         if (xfs_ilock_nowait(sc->tempip, XFS_IOLOCK_EXCL)) {
     182      533365 :                 sc->temp_ilock_flags |= XFS_IOLOCK_EXCL;
     183      533365 :                 return true;
     184             :         }
     185             : 
     186             :         return false;
     187             : }
     188             : 
     189             : /*
     190             :  * Take the temporary file's IOLOCK while holding a different inode's IOLOCK.
     191             :  * In theory nobody else should hold the tempfile's IOLOCK, but we use trylock
     192             :  * to avoid deadlocks and lockdep complaints.
     193             :  */
     194             : int
     195      533364 : xrep_tempfile_iolock_polled(
     196             :         struct xfs_scrub        *sc)
     197             : {
     198      533364 :         int                     error = 0;
     199             : 
     200      533364 :         while (!xrep_tempfile_iolock_nowait(sc)) {
     201           0 :                 if (xchk_should_terminate(sc, &error))
     202           0 :                         return error;
     203           0 :                 delay(1);
     204             :         }
     205             : 
     206             :         return 0;
     207             : }
     208             : 
     209             : /* Release IOLOCK_EXCL on the temporary file. */
     210             : void
     211      191392 : xrep_tempfile_iounlock(
     212             :         struct xfs_scrub        *sc)
     213             : {
     214      191392 :         xfs_iunlock(sc->tempip, XFS_IOLOCK_EXCL);
     215      191392 :         sc->temp_ilock_flags &= ~XFS_IOLOCK_EXCL;
     216      191392 : }
     217             : 
     218             : /* Prepare the temporary file for metadata updates by grabbing ILOCK_EXCL. */
     219             : void
     220      243813 : xrep_tempfile_ilock(
     221             :         struct xfs_scrub        *sc)
     222             : {
     223      243813 :         sc->temp_ilock_flags |= XFS_ILOCK_EXCL;
     224      243813 :         xfs_ilock(sc->tempip, XFS_ILOCK_EXCL);
     225      243812 : }
     226             : 
     227             : /* Try to grab ILOCK_EXCL on the temporary file. */
     228             : bool
     229       54170 : xrep_tempfile_ilock_nowait(
     230             :         struct xfs_scrub        *sc)
     231             : {
     232       54170 :         if (xfs_ilock_nowait(sc->tempip, XFS_ILOCK_EXCL)) {
     233       54169 :                 sc->temp_ilock_flags |= XFS_ILOCK_EXCL;
     234       54169 :                 return true;
     235             :         }
     236             : 
     237             :         return false;
     238             : }
     239             : 
     240             : /* Unlock ILOCK_EXCL on the temporary file after an update. */
     241             : void
     242      382561 : xrep_tempfile_iunlock(
     243             :         struct xfs_scrub        *sc)
     244             : {
     245      382561 :         xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);
     246      382549 :         sc->temp_ilock_flags &= ~XFS_ILOCK_EXCL;
     247      382549 : }
     248             : 
     249             : /*
     250             :  * Begin the process of making changes to both the file being scrubbed and
     251             :  * the temporary file by taking ILOCK_EXCL on both.
     252             :  */
     253             : void
     254           0 : xrep_tempfile_ilock_both(
     255             :         struct xfs_scrub        *sc)
     256             : {
     257           0 :         xfs_lock_two_inodes(sc->ip, XFS_ILOCK_EXCL, sc->tempip, XFS_ILOCK_EXCL);
     258           0 :         sc->ilock_flags |= XFS_ILOCK_EXCL;
     259           0 :         sc->temp_ilock_flags |= XFS_ILOCK_EXCL;
     260           0 : }
     261             : 
     262             : /* Unlock ILOCK_EXCL on both files. */
     263             : void
     264           0 : xrep_tempfile_iunlock_both(
     265             :         struct xfs_scrub        *sc)
     266             : {
     267           0 :         xrep_tempfile_iunlock(sc);
     268           0 :         xchk_iunlock(sc, XFS_ILOCK_EXCL);
     269           0 : }
     270             : 
     271             : /* Release the temporary file. */
     272             : void
     273   506455032 : xrep_tempfile_rele(
     274             :         struct xfs_scrub        *sc)
     275             : {
     276   506455032 :         if (!sc->tempip)
     277             :                 return;
     278             : 
     279      554894 :         if (sc->temp_ilock_flags) {
     280      403505 :                 xfs_iunlock(sc->tempip, sc->temp_ilock_flags);
     281      403474 :                 sc->temp_ilock_flags = 0;
     282             :         }
     283             : 
     284      554863 :         xchk_irele(sc, sc->tempip);
     285      554830 :         sc->tempip = NULL;
     286             : }
     287             : 
     288             : /*
     289             :  * Make sure that the given range of the data fork of the temporary file is
     290             :  * mapped to written blocks.  The caller must ensure that both inodes are
     291             :  * joined to the transaction.
     292             :  */
     293             : int
     294       54169 : xrep_tempfile_prealloc(
     295             :         struct xfs_scrub        *sc,
     296             :         xfs_fileoff_t           off,
     297             :         xfs_filblks_t           len)
     298             : {
     299       54169 :         struct xfs_bmbt_irec    map;
     300       54169 :         xfs_fileoff_t           end = off + len;
     301       54169 :         int                     error;
     302             : 
     303       54169 :         ASSERT(sc->tempip != NULL);
     304       54169 :         ASSERT(!XFS_NOT_DQATTACHED(sc->mp, sc->tempip));
     305             : 
     306      108338 :         for (; off < end; off = map.br_startoff + map.br_blockcount) {
     307       54169 :                 int             nmaps = 1;
     308             : 
     309             :                 /*
     310             :                  * If we have a real extent mapping this block then we're
     311             :                  * in ok shape.
     312             :                  */
     313       54169 :                 error = xfs_bmapi_read(sc->tempip, off, end - off, &map, &nmaps,
     314             :                                 XFS_DATA_FORK);
     315       54169 :                 if (error)
     316           0 :                         return error;
     317       54169 :                 if (nmaps == 0) {
     318           0 :                         ASSERT(nmaps != 0);
     319           0 :                         return -EFSCORRUPTED;
     320             :                 }
     321             : 
     322       54169 :                 if (xfs_bmap_is_written_extent(&map))
     323           0 :                         continue;
     324             : 
     325             :                 /*
     326             :                  * If we find a delalloc reservation then something is very
     327             :                  * very wrong.  Bail out.
     328             :                  */
     329       54169 :                 if (map.br_startblock == DELAYSTARTBLOCK)
     330             :                         return -EFSCORRUPTED;
     331             : 
     332             :                 /*
     333             :                  * Make sure this block has a real zeroed extent allocated to
     334             :                  * it.
     335             :                  */
     336       54169 :                 nmaps = 1;
     337       54169 :                 error = xfs_bmapi_write(sc->tp, sc->tempip, off, end - off,
     338             :                                 XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO, 0, &map,
     339             :                                 &nmaps);
     340       54169 :                 if (error)
     341           0 :                         return error;
     342             : 
     343       54169 :                 trace_xrep_tempfile_prealloc(sc, XFS_DATA_FORK, &map);
     344             : 
     345             :                 /* Commit new extent and all deferred work. */
     346       54169 :                 error = xfs_defer_finish(&sc->tp);
     347       54169 :                 if (error)
     348           0 :                         return error;
     349             :         }
     350             : 
     351             :         return 0;
     352             : }
     353             : 
     354             : /*
     355             :  * Write data to each block of a file.  The given range of the tempfile's data
     356             :  * fork must already be populated with written extents.
     357             :  */
     358             : int
     359       54169 : xrep_tempfile_copyin(
     360             :         struct xfs_scrub        *sc,
     361             :         xfs_fileoff_t           off,
     362             :         xfs_filblks_t           len,
     363             :         xrep_tempfile_copyin_fn prep_fn,
     364             :         void                    *data)
     365             : {
     366       54169 :         LIST_HEAD(buffers_list);
     367       54169 :         struct xfs_mount        *mp = sc->mp;
     368       54169 :         struct xfs_buf          *bp;
     369       54169 :         xfs_fileoff_t           flush_mask;
     370       54169 :         xfs_fileoff_t           end = off + len;
     371       54169 :         loff_t                  pos = XFS_FSB_TO_B(mp, off);
     372       54169 :         int                     error = 0;
     373             : 
     374       54169 :         ASSERT(S_ISREG(VFS_I(sc->tempip)->i_mode));
     375             : 
     376             :         /* Flush buffers to disk every 512K */
     377       54169 :         flush_mask = XFS_B_TO_FSBT(mp, (1U << 19)) - 1;
     378             : 
     379     2009730 :         for (; off < end; off++, pos += mp->m_sb.sb_blocksize) {
     380     1955561 :                 struct xfs_bmbt_irec    map;
     381     1955561 :                 int                     nmaps = 1;
     382             : 
     383             :                 /* Read block mapping for this file block. */
     384     1955561 :                 error = xfs_bmapi_read(sc->tempip, off, 1, &map, &nmaps, 0);
     385     1955561 :                 if (error)
     386           0 :                         goto out_err;
     387     1955561 :                 if (nmaps == 0 || !xfs_bmap_is_written_extent(&map)) {
     388           0 :                         error = -EFSCORRUPTED;
     389           0 :                         goto out_err;
     390             :                 }
     391             : 
     392             :                 /* Get the metadata buffer for this offset in the file. */
     393     1955561 :                 error = xfs_trans_get_buf(sc->tp, mp->m_ddev_targp,
     394     1955561 :                                 XFS_FSB_TO_DADDR(mp, map.br_startblock),
     395             :                                 mp->m_bsize, 0, &bp);
     396     1955561 :                 if (error)
     397           0 :                         goto out_err;
     398             : 
     399     1955561 :                 trace_xrep_tempfile_copyin(sc, XFS_DATA_FORK, &map);
     400             : 
     401             :                 /* Read in a block's worth of data from the xfile. */
     402     1955561 :                 error = prep_fn(sc, bp, data);
     403     1955561 :                 if (error) {
     404           0 :                         xfs_trans_brelse(sc->tp, bp);
     405           0 :                         goto out_err;
     406             :                 }
     407             : 
     408             :                 /* Queue buffer, and flush if we have too much dirty data. */
     409     1955561 :                 xfs_buf_delwri_queue_here(bp, &buffers_list);
     410     1955561 :                 xfs_trans_brelse(sc->tp, bp);
     411             : 
     412     1955561 :                 if (!(off & flush_mask)) {
     413       41033 :                         error = xfs_buf_delwri_submit(&buffers_list);
     414       41033 :                         if (error)
     415           0 :                                 goto out_err;
     416             :                 }
     417             :         }
     418             : 
     419             :         /*
     420             :          * Write the new blocks to disk.  If the ordered list isn't empty after
     421             :          * that, then something went wrong and we have to fail.  This should
     422             :          * never happen, but we'll check anyway.
     423             :          */
     424       54169 :         error = xfs_buf_delwri_submit(&buffers_list);
     425       54169 :         if (error)
     426           0 :                 goto out_err;
     427             : 
     428       54169 :         if (!list_empty(&buffers_list)) {
     429           0 :                 ASSERT(list_empty(&buffers_list));
     430           0 :                 error = -EIO;
     431           0 :                 goto out_err;
     432             :         }
     433             : 
     434             :         return 0;
     435             : 
     436           0 : out_err:
     437           0 :         xfs_buf_delwri_cancel(&buffers_list);
     438           0 :         return error;
     439             : }
     440             : 
     441             : /*
     442             :  * Set the temporary file's size.  Caller must join the tempfile to the scrub
     443             :  * transaction and is responsible for adjusting block mappings as needed.
     444             :  */
     445             : int
     446       54169 : xrep_tempfile_set_isize(
     447             :         struct xfs_scrub        *sc,
     448             :         unsigned long long      isize)
     449             : {
     450       54169 :         if (sc->tempip->i_disk_size == isize)
     451             :                 return 0;
     452             : 
     453       54169 :         sc->tempip->i_disk_size = isize;
     454       54169 :         i_size_write(VFS_I(sc->tempip), isize);
     455       54169 :         return xrep_tempfile_roll_trans(sc);
     456             : }
     457             : 
     458             : /*
     459             :  * Roll a repair transaction involving the temporary file.  Caller must join
     460             :  * both the temporary file and the file being scrubbed to the transaction.
     461             :  * This function return with both inodes joined to a new scrub transaction,
     462             :  * or the usual negative errno.
     463             :  */
     464             : int
     465      405211 : xrep_tempfile_roll_trans(
     466             :         struct xfs_scrub        *sc)
     467             : {
     468      405211 :         int                     error;
     469             : 
     470      405211 :         xfs_trans_log_inode(sc->tp, sc->tempip, XFS_ILOG_CORE);
     471      405212 :         error = xrep_roll_trans(sc);
     472      405218 :         if (error)
     473             :                 return error;
     474             : 
     475      405218 :         xfs_trans_ijoin(sc->tp, sc->tempip, 0);
     476      405218 :         return 0;
     477             : }
     478             : 
     479             : /* Enable atomic extent swapping. */
     480             : int
     481      408135 : xrep_tempswap_grab_log_assist(
     482             :         struct xfs_scrub        *sc)
     483             : {
     484      408135 :         bool                    need_rele = false;
     485      408135 :         int                     error;
     486             : 
     487      408135 :         if (sc->flags & XREP_FSGATES_ATOMIC_XCHG)
     488             :                 return 0;
     489             : 
     490      408135 :         error = xfs_xchg_range_grab_log_assist(sc->mp, true, &need_rele);
     491      408129 :         if (error)
     492             :                 return error;
     493      408129 :         if (!need_rele) {
     494           0 :                 ASSERT(need_rele);
     495           0 :                 return -EOPNOTSUPP;
     496             :         }
     497             : 
     498      408129 :         trace_xchk_fsgates_enable(sc, XREP_FSGATES_ATOMIC_XCHG);
     499             : 
     500      408129 :         sc->flags |= XREP_FSGATES_ATOMIC_XCHG;
     501      408129 :         return 0;
     502             : }
     503             : 
     504             : /*
     505             :  * Fill out the swapext request in preparation for swapping the contents of a
     506             :  * metadata file that we've rebuilt in the temp file.
     507             :  */
     508             : STATIC int
     509      403513 : xrep_tempswap_prep_request(
     510             :         struct xfs_scrub        *sc,
     511             :         int                     whichfork,
     512             :         xfs_fileoff_t           off,
     513             :         xfs_filblks_t           len,
     514             :         struct xrep_tempswap    *tx)
     515             : {
     516      403513 :         struct xfs_swapext_req  *req = &tx->req;
     517             : 
     518      403513 :         memset(tx, 0, sizeof(struct xrep_tempswap));
     519             : 
     520             :         /* COW forks don't exist on disk. */
     521      403513 :         if (whichfork == XFS_COW_FORK) {
     522           0 :                 ASSERT(0);
     523           0 :                 return -EINVAL;
     524             :         }
     525             : 
     526             :         /* Both files should have the relevant forks. */
     527      807018 :         if (!xfs_ifork_ptr(sc->ip, whichfork) ||
     528      403512 :             !xfs_ifork_ptr(sc->tempip, whichfork)) {
     529           0 :                 ASSERT(xfs_ifork_ptr(sc->ip, whichfork) != NULL);
     530           0 :                 ASSERT(xfs_ifork_ptr(sc->tempip, whichfork) != NULL);
     531           0 :                 return -EINVAL;
     532             :         }
     533             : 
     534             :         /* Swap all mappings in both forks. */
     535      403505 :         req->ip1 = sc->tempip;
     536      403505 :         req->ip2 = sc->ip;
     537      403505 :         req->startoff1 = off;
     538      403505 :         req->startoff2 = off;
     539      403505 :         req->whichfork = whichfork;
     540      403505 :         req->blockcount = len;
     541      403505 :         req->req_flags = XFS_SWAP_REQ_LOGGED;
     542             : 
     543             :         /* Always swap sizes when we're swapping data fork mappings. */
     544      403505 :         if (whichfork == XFS_DATA_FORK)
     545       86284 :                 req->req_flags |= XFS_SWAP_REQ_SET_SIZES;
     546             : 
     547             :         /*
     548             :          * If we're repairing symlinks, xattrs, or directories, always try to
     549             :          * convert ip2 to short format after swapping.
     550             :          */
     551      403505 :         if (whichfork == XFS_ATTR_FORK || S_ISDIR(VFS_I(sc->ip)->i_mode) ||
     552             :             S_ISLNK(VFS_I(sc->ip)->i_mode))
     553      349336 :                 req->req_flags |= XFS_SWAP_REQ_CVT_INO2_SF;
     554             : 
     555             :         return 0;
     556             : }
     557             : 
     558             : /*
     559             :  * Fill out the swapext resource estimation structures in preparation for
     560             :  * swapping the contents of a metadata file that we've rebuilt in the temp
     561             :  * file.  Caller must hold IOLOCK_EXCL but not ILOCK_EXCL on both files.
     562             :  */
     563             : STATIC int
     564      349338 : xrep_tempswap_estimate(
     565             :         struct xfs_scrub        *sc,
     566             :         struct xrep_tempswap    *tx)
     567             : {
     568      349338 :         struct xfs_swapext_req  *req = &tx->req;
     569      349338 :         struct xfs_ifork        *ifp;
     570      349338 :         struct xfs_ifork        *tifp;
     571      349338 :         int                     state = 0;
     572             : 
     573             :         /*
     574             :          * Deal with either fork being in local format.  The swapext code only
     575             :          * knows how to exchange block mappings for regular files, so we only
     576             :          * have to know about local format for xattrs and directories.
     577             :          */
     578      349338 :         ifp = xfs_ifork_ptr(sc->ip, req->whichfork);
     579      349349 :         if (ifp->if_format == XFS_DINODE_FMT_LOCAL)
     580      301908 :                 state |= 1;
     581             : 
     582      349349 :         tifp = xfs_ifork_ptr(sc->tempip, req->whichfork);
     583      349345 :         if (tifp->if_format == XFS_DINODE_FMT_LOCAL)
     584      305803 :                 state |= 2;
     585             : 
     586      349345 :         switch (state) {
     587       43537 :         case 0:
     588             :                 /* Both files have mapped extents; use the regular estimate. */
     589       43537 :                 return xfs_xchg_range_estimate(req);
     590           5 :         case 1:
     591             :                 /*
     592             :                  * The file being repaired is in local format, but the temp
     593             :                  * file has mapped extents.  To perform the swap, the file
     594             :                  * being repaired must have its shorform data converted to a
     595             :                  * fsblock, and the fork changed to extents format.  We need
     596             :                  * one resblk for the conversion; the number of exchanges is
     597             :                  * (worst case) the temporary file's extent count plus the
     598             :                  * block we converted.
     599             :                  */
     600           5 :                 req->ip1_bcount = sc->tempip->i_nblocks;
     601           5 :                 req->ip2_bcount = 1;
     602           5 :                 req->nr_exchanges = 1 + tifp->if_nextents;
     603           5 :                 req->resblks = 1;
     604           5 :                 break;
     605        3903 :         case 2:
     606             :                 /*
     607             :                  * The temporary file is in local format, but the file being
     608             :                  * repaired has mapped extents.  To perform the swap, the temp
     609             :                  * file must have its shortform data converted to an fsblock,
     610             :                  * and the fork changed to extents format.  We need one resblk
     611             :                  * for the conversion; the number of exchanges is (worst case)
     612             :                  * the extent count of the file being repaired plus the block
     613             :                  * we converted.
     614             :                  */
     615        3903 :                 req->ip1_bcount = 1;
     616        3903 :                 req->ip2_bcount = sc->ip->i_nblocks;
     617        3903 :                 req->nr_exchanges = 1 + ifp->if_nextents;
     618        3903 :                 req->resblks = 1;
     619        3903 :                 break;
     620      301900 :         case 3:
     621             :                 /*
     622             :                  * Both forks are in local format.  To perform the swap, both
     623             :                  * files must have their shortform data converted to fsblocks,
     624             :                  * and both forks must be converted to extents format.  We
     625             :                  * need two resblks for the two conversions, and the number of
     626             :                  * exchanges is 1 since there's only one block at fileoff 0.
     627             :                  * Presumably, the caller could not exchange the two inode fork
     628             :                  * areas directly.
     629             :                  */
     630      301900 :                 req->ip1_bcount = 1;
     631      301900 :                 req->ip2_bcount = 1;
     632      301900 :                 req->nr_exchanges = 1;
     633      301900 :                 req->resblks = 2;
     634      301900 :                 break;
     635             :         }
     636             : 
     637      305808 :         return xfs_swapext_estimate_overhead(req);
     638             : }
     639             : 
     640             : /*
     641             :  * Obtain a quota reservation to make sure we don't hit EDQUOT.  We can skip
     642             :  * this if quota enforcement is disabled or if both inodes' dquots are the
     643             :  * same.  The qretry structure must be initialized to zeroes before the first
     644             :  * call to this function.
     645             :  */
     646             : STATIC int
     647      403517 : xrep_tempswap_reserve_quota(
     648             :         struct xfs_scrub                *sc,
     649             :         const struct xrep_tempswap      *tx)
     650             : {
     651      403517 :         struct xfs_trans                *tp = sc->tp;
     652      403517 :         const struct xfs_swapext_req    *req = &tx->req;
     653      403517 :         int64_t                         ddelta, rdelta;
     654      403517 :         int                             error;
     655             : 
     656             :         /*
     657             :          * Don't bother with a quota reservation if we're not enforcing them
     658             :          * or the two inodes have the same dquots.
     659             :          */
     660      403517 :         if (!XFS_IS_QUOTA_ON(tp->t_mountp) || req->ip1 == req->ip2 ||
     661      403517 :             (req->ip1->i_udquot == req->ip2->i_udquot &&
     662      302354 :              req->ip1->i_gdquot == req->ip2->i_gdquot &&
     663      301236 :              req->ip1->i_pdquot == req->ip2->i_pdquot))
     664             :                 return 0;
     665             : 
     666             :         /*
     667             :          * Quota reservation for each file comes from two sources.  First, we
     668             :          * need to account for any net gain in mapped blocks during the swap.
     669             :          * Second, we need reservation for the gross gain in mapped blocks so
     670             :          * that we don't trip over any quota block reservation assertions.  We
     671             :          * must reserve the gross gain because the quota code subtracts from
     672             :          * bcount the number of blocks that we unmap; it does not add that
     673             :          * quantity back to the quota block reservation.
     674             :          */
     675      105060 :         ddelta = max_t(int64_t, 0, req->ip2_bcount - req->ip1_bcount);
     676      105060 :         rdelta = max_t(int64_t, 0, req->ip2_rtbcount - req->ip1_rtbcount);
     677      105060 :         error = xfs_trans_reserve_quota_nblks(tp, req->ip1,
     678      105060 :                         ddelta + req->ip1_bcount, rdelta + req->ip1_rtbcount,
     679             :                         true);
     680      105060 :         if (error)
     681             :                 return error;
     682             : 
     683      105060 :         ddelta = max_t(int64_t, 0, req->ip1_bcount - req->ip2_bcount);
     684      105060 :         rdelta = max_t(int64_t, 0, req->ip1_rtbcount - req->ip2_rtbcount);
     685      105060 :         return xfs_trans_reserve_quota_nblks(tp, req->ip2,
     686      105060 :                         ddelta + req->ip2_bcount, rdelta + req->ip2_rtbcount,
     687             :                         true);
     688             : }
     689             : 
     690             : /*
     691             :  * Prepare an existing transaction for a swap.  The caller must hold
     692             :  * the ILOCK of both the inode being repaired and the temporary file.
     693             :  * Only use this when those ILOCKs cannot be dropped.
     694             :  *
     695             :  * Fill out the swapext request and resource estimation structures in
     696             :  * preparation for swapping the contents of a metadata file that we've rebuilt
     697             :  * in the temp file, then reserve space and quota to the transaction.
     698             :  */
     699             : int
     700       54169 : xrep_tempswap_trans_reserve(
     701             :         struct xfs_scrub        *sc,
     702             :         int                     whichfork,
     703             :         xfs_fileoff_t           off,
     704             :         xfs_filblks_t           len,
     705             :         struct xrep_tempswap    *tx)
     706             : {
     707       54169 :         int                     error;
     708             : 
     709       54169 :         ASSERT(sc->tp != NULL);
     710       54169 :         ASSERT(xfs_isilocked(sc->ip, XFS_ILOCK_EXCL));
     711       54169 :         ASSERT(xfs_isilocked(sc->tempip, XFS_ILOCK_EXCL));
     712             : 
     713       54169 :         error = xrep_tempswap_prep_request(sc, whichfork, off, len, tx);
     714       54169 :         if (error)
     715             :                 return error;
     716             : 
     717       54169 :         error = xfs_swapext_estimate(&tx->req);
     718       54169 :         if (error)
     719             :                 return error;
     720             : 
     721       54169 :         error = xfs_trans_reserve_more(sc->tp, tx->req.resblks, 0);
     722       54169 :         if (error)
     723             :                 return error;
     724             : 
     725       54169 :         return xrep_tempswap_reserve_quota(sc, tx);
     726             : }
     727             : 
     728             : /*
     729             :  * Allocate a transaction, ILOCK the temporary file and the file being
     730             :  * repaired, and join them to the transaction in preparation to swap fork
     731             :  * contents as part of a repair operation.
     732             :  */
     733             : int
     734      349343 : xrep_tempswap_trans_alloc(
     735             :         struct xfs_scrub        *sc,
     736             :         int                     whichfork,
     737             :         struct xrep_tempswap    *tx)
     738             : {
     739      349343 :         unsigned int            flags = 0;
     740      349343 :         int                     error;
     741             : 
     742      349343 :         ASSERT(sc->tp == NULL);
     743             : 
     744      349343 :         error = xrep_tempswap_prep_request(sc, whichfork, 0, XFS_MAX_FILEOFF,
     745             :                         tx);
     746      349346 :         if (error)
     747             :                 return error;
     748             : 
     749      349346 :         error = xrep_tempswap_estimate(sc, tx);
     750      349330 :         if (error)
     751             :                 return error;
     752             : 
     753      349330 :         if (xfs_has_lazysbcount(sc->mp))
     754      349330 :                 flags |= XFS_TRANS_RES_FDBLKS;
     755             : 
     756      349330 :         error = xrep_tempswap_grab_log_assist(sc);
     757      349343 :         if (error)
     758             :                 return error;
     759             : 
     760      349343 :         error = xfs_trans_alloc(sc->mp, &M_RES(sc->mp)->tr_itruncate,
     761      349343 :                         tx->req.resblks, 0, flags, &sc->tp);
     762      349348 :         if (error)
     763             :                 return error;
     764             : 
     765      349347 :         sc->temp_ilock_flags |= XFS_ILOCK_EXCL;
     766      349347 :         sc->ilock_flags |= XFS_ILOCK_EXCL;
     767      349347 :         xfs_xchg_range_ilock(sc->tp, sc->ip, sc->tempip);
     768             : 
     769      349347 :         return xrep_tempswap_reserve_quota(sc, tx);
     770             : }
     771             : 
     772             : /* Swap forks between the file being repaired and the temporary file. */
     773             : int
     774      101614 : xrep_tempswap_contents(
     775             :         struct xfs_scrub        *sc,
     776             :         struct xrep_tempswap    *tx)
     777             : {
     778      101614 :         int                     error;
     779             : 
     780      101614 :         ASSERT(sc->flags & XREP_FSGATES_ATOMIC_XCHG);
     781             : 
     782      101614 :         xfs_swapext(sc->tp, &tx->req);
     783      101614 :         error = xfs_defer_finish(&sc->tp);
     784      101614 :         if (error)
     785             :                 return error;
     786             : 
     787             :         /*
     788             :          * If we swapped the ondisk sizes of two metadata files, we must swap
     789             :          * the incore sizes as well.  Since online fsck doesn't use swapext on
     790             :          * the data forks of user-accessible files, the two sizes are always
     791             :          * the same, so we don't need to log the inodes.
     792             :          */
     793      101614 :         if (tx->req.req_flags & XFS_SWAP_REQ_SET_SIZES) {
     794       61834 :                 loff_t  temp;
     795             : 
     796       61834 :                 temp = i_size_read(VFS_I(sc->ip));
     797       61834 :                 i_size_write(VFS_I(sc->ip), i_size_read(VFS_I(sc->tempip)));
     798       61834 :                 i_size_write(VFS_I(sc->tempip), temp);
     799             :         }
     800             : 
     801             :         return 0;
     802             : }
     803             : 
     804             : /*
     805             :  * Write local format data from one of the temporary file's forks into the same
     806             :  * fork of file being repaired, and swap the file sizes, if appropriate.
     807             :  * Caller must ensure that the file being repaired has enough fork space to
     808             :  * hold all the bytes.
     809             :  */
     810             : void
     811      301893 : xrep_tempfile_copyout_local(
     812             :         struct xfs_scrub        *sc,
     813             :         int                     whichfork)
     814             : {
     815      301893 :         struct xfs_ifork        *temp_ifp;
     816      301893 :         struct xfs_ifork        *ifp;
     817      301893 :         unsigned int            ilog_flags = XFS_ILOG_CORE;
     818             : 
     819      301893 :         temp_ifp = xfs_ifork_ptr(sc->tempip, whichfork);
     820      301885 :         ifp = xfs_ifork_ptr(sc->ip, whichfork);
     821             : 
     822      301889 :         ASSERT(temp_ifp != NULL);
     823      301889 :         ASSERT(ifp != NULL);
     824      301889 :         ASSERT(temp_ifp->if_format == XFS_DINODE_FMT_LOCAL);
     825      301889 :         ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
     826             : 
     827      301889 :         switch (whichfork) {
     828       24443 :         case XFS_DATA_FORK:
     829       24443 :                 ASSERT(sc->tempip->i_disk_size <= xfs_inode_data_fork_size(sc->ip));
     830             :                 break;
     831      277446 :         case XFS_ATTR_FORK:
     832      277446 :                 ASSERT(sc->tempip->i_forkoff >= sc->ip->i_forkoff);
     833             :                 break;
     834           0 :         default:
     835           0 :                 ASSERT(0);
     836           0 :                 return;
     837             :         }
     838             : 
     839      301889 :         xfs_idestroy_fork(ifp);
     840      301885 :         xfs_init_local_fork(sc->ip, whichfork, temp_ifp->if_u1.if_data,
     841             :                         temp_ifp->if_bytes);
     842             : 
     843      301882 :         if (whichfork == XFS_DATA_FORK) {
     844       24448 :                 i_size_write(VFS_I(sc->ip), i_size_read(VFS_I(sc->tempip)));
     845       24448 :                 sc->ip->i_disk_size = sc->tempip->i_disk_size;
     846             :         }
     847             : 
     848      301882 :         ilog_flags |= xfs_ilog_fdata(whichfork);
     849      301882 :         xfs_trans_log_inode(sc->tp, sc->ip, ilog_flags);
     850             : }
     851             : 
     852             : /* Decide if a given XFS inode is a temporary file for a repair. */
     853             : bool
     854  8214878860 : xrep_is_tempfile(
     855             :         const struct xfs_inode  *ip)
     856             : {
     857  8214878860 :         const struct inode      *inode = &ip->i_vnode;
     858  8214878860 :         struct xfs_mount        *mp = ip->i_mount;
     859             : 
     860             :         /*
     861             :          * Files in the metadata directory tree also have S_PRIVATE set and
     862             :          * IOP_XATTR unset, so we must distinguish them separately.
     863             :          */
     864  8214878860 :         if (xfs_has_metadir(mp) && (ip->i_diflags2 & XFS_DIFLAG2_METADIR))
     865             :                 return false;
     866             : 
     867  8210672904 :         if (IS_PRIVATE(inode) && !(inode->i_opflags & IOP_XATTR))
     868      629245 :                 return true;
     869             : 
     870             :         return false;
     871             : }

Generated by: LCOV version 1.14