LCOV - code coverage report
Current view: top level - fs/xfs/scrub - parent.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 210 365 57.5 %
Date: 2023-07-31 20:08:27 Functions: 14 17 82.4 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2017-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_icache.h"
      16             : #include "xfs_dir2.h"
      17             : #include "xfs_dir2_priv.h"
      18             : #include "xfs_attr.h"
      19             : #include "xfs_parent.h"
      20             : #include "scrub/scrub.h"
      21             : #include "scrub/common.h"
      22             : #include "scrub/readdir.h"
      23             : #include "scrub/tempfile.h"
      24             : #include "scrub/repair.h"
      25             : #include "scrub/listxattr.h"
      26             : #include "scrub/xfile.h"
      27             : #include "scrub/xfarray.h"
      28             : #include "scrub/xfblob.h"
      29             : #include "scrub/trace.h"
      30             : 
      31             : /* Set us up to scrub parents. */
      32             : int
      33    78149477 : xchk_setup_parent(
      34             :         struct xfs_scrub        *sc)
      35             : {
      36    78149477 :         int                     error;
      37             : 
      38    78149477 :         if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
      39      281282 :                 error = xrep_setup_parent(sc);
      40      281282 :                 if (error)
      41             :                         return error;
      42             :         }
      43             : 
      44    78149477 :         return xchk_setup_inode_contents(sc, 0);
      45             : }
      46             : 
      47             : /* Parent pointers */
      48             : 
      49             : /* Look for an entry in a parent pointing to this inode. */
      50             : 
      51             : struct xchk_parent_ctx {
      52             :         struct xfs_scrub        *sc;
      53             :         xfs_nlink_t             nlink;
      54             : };
      55             : 
      56             : /* Look for a single entry in a directory pointing to an inode. */
      57             : STATIC int
      58       25873 : xchk_parent_actor(
      59             :         struct xfs_scrub        *sc,
      60             :         struct xfs_inode        *dp,
      61             :         xfs_dir2_dataptr_t      dapos,
      62             :         const struct xfs_name   *name,
      63             :         xfs_ino_t               ino,
      64             :         void                    *priv)
      65             : {
      66       25873 :         struct xchk_parent_ctx  *spc = priv;
      67       25873 :         int                     error = 0;
      68             : 
      69             :         /* Does this name make sense? */
      70       25873 :         if (!xfs_dir2_namecheck(name->name, name->len))
      71           0 :                 error = -EFSCORRUPTED;
      72       25873 :         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
      73           0 :                 return error;
      74             : 
      75       25873 :         if (sc->ip->i_ino == ino)
      76        2290 :                 spc->nlink++;
      77             : 
      78       25873 :         if (xchk_should_terminate(spc->sc, &error))
      79           0 :                 return error;
      80             : 
      81             :         return 0;
      82             : }
      83             : 
      84             : /*
      85             :  * Try to lock a parent directory for checking dirents.  Returns the inode
      86             :  * flags for the locks we now hold, or zero if we failed.
      87             :  */
      88             : STATIC unsigned int
      89        2290 : xchk_parent_ilock_dir(
      90             :         struct xfs_inode        *dp)
      91             : {
      92        2290 :         if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED))
      93             :                 return 0;
      94             : 
      95        2290 :         if (!xfs_need_iread_extents(&dp->i_df))
      96             :                 return XFS_ILOCK_SHARED;
      97             : 
      98           0 :         xfs_iunlock(dp, XFS_ILOCK_SHARED);
      99             : 
     100           0 :         if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL))
     101           0 :                 return 0;
     102             : 
     103             :         return XFS_ILOCK_EXCL;
     104             : }
     105             : 
     106             : /*
     107             :  * Given the inode number of the alleged parent of the inode being scrubbed,
     108             :  * try to validate that the parent has exactly one directory entry pointing
     109             :  * back to the inode being scrubbed.  Returns -EAGAIN if we need to revalidate
     110             :  * the dotdot entry.
     111             :  */
     112             : STATIC int
     113        2358 : xchk_parent_validate(
     114             :         struct xfs_scrub        *sc,
     115             :         xfs_ino_t               parent_ino)
     116             : {
     117        2358 :         struct xchk_parent_ctx  spc = {
     118             :                 .sc             = sc,
     119             :                 .nlink          = 0,
     120             :         };
     121        2358 :         struct xfs_mount        *mp = sc->mp;
     122        2358 :         struct xfs_inode        *dp = NULL;
     123        2358 :         xfs_nlink_t             expected_nlink;
     124        2358 :         unsigned int            lock_mode;
     125        2358 :         int                     error = 0;
     126             : 
     127             :         /* Is this the root dir?  Then '..' must point to itself. */
     128        2358 :         if (sc->ip == mp->m_rootip) {
     129          68 :                 if (sc->ip->i_ino != mp->m_sb.sb_rootino ||
     130             :                     sc->ip->i_ino != parent_ino)
     131           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     132          68 :                 return 0;
     133             :         }
     134             : 
     135             :         /* Is this the metadata root dir?  Then '..' must point to itself. */
     136        2290 :         if (sc->ip == mp->m_metadirip) {
     137           0 :                 if (sc->ip->i_ino != mp->m_sb.sb_metadirino ||
     138             :                     sc->ip->i_ino != parent_ino)
     139           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     140           0 :                 return 0;
     141             :         }
     142             : 
     143             :         /* '..' must not point to ourselves. */
     144        2290 :         if (sc->ip->i_ino == parent_ino) {
     145           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     146           0 :                 return 0;
     147             :         }
     148             : 
     149             :         /*
     150             :          * If we're an unlinked directory, the parent /won't/ have a link
     151             :          * to us.  Otherwise, it should have one link.
     152             :          */
     153        2290 :         expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;
     154             : 
     155             :         /*
     156             :          * Grab the parent directory inode.  This must be released before we
     157             :          * cancel the scrub transaction.
     158             :          *
     159             :          * If _iget returns -EINVAL or -ENOENT then the parent inode number is
     160             :          * garbage and the directory is corrupt.  If the _iget returns
     161             :          * -EFSCORRUPTED or -EFSBADCRC then the parent is corrupt which is a
     162             :          *  cross referencing error.  Any other error is an operational error.
     163             :          */
     164        2290 :         error = xchk_iget(sc, parent_ino, &dp);
     165        2290 :         if (error == -EINVAL || error == -ENOENT) {
     166           0 :                 error = -EFSCORRUPTED;
     167           0 :                 xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
     168           0 :                 return error;
     169             :         }
     170        2290 :         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
     171           0 :                 return error;
     172        2290 :         if (dp == sc->ip || xrep_is_tempfile(dp) ||
     173        2290 :             !S_ISDIR(VFS_I(dp)->i_mode)) {
     174           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     175           0 :                 goto out_rele;
     176             :         }
     177             : 
     178        2290 :         lock_mode = xchk_parent_ilock_dir(dp);
     179        2290 :         if (!lock_mode) {
     180           0 :                 xchk_iunlock(sc, XFS_ILOCK_EXCL);
     181           0 :                 xchk_ilock(sc, XFS_ILOCK_EXCL);
     182           0 :                 error = -EAGAIN;
     183           0 :                 goto out_rele;
     184             :         }
     185             : 
     186             :         /*
     187             :          * We cannot yet validate this parent pointer if the directory looks as
     188             :          * though it has been zapped by the inode record repair code.
     189             :          */
     190        2290 :         if (xchk_dir_looks_zapped(dp)) {
     191           0 :                 error = -EFSCORRUPTED;
     192           0 :                 xchk_set_incomplete(sc);
     193           0 :                 goto out_unlock;
     194             :         }
     195             : 
     196             :         /* Metadata and regular inodes cannot cross trees. */
     197        2290 :         if (xfs_is_metadir_inode(dp) != xfs_is_metadir_inode(sc->ip)) {
     198           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     199           0 :                 goto out_unlock;
     200             :         }
     201             : 
     202             :         /* Look for a directory entry in the parent pointing to the child. */
     203        2290 :         error = xchk_dir_walk(sc, dp, xchk_parent_actor, &spc);
     204        2290 :         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
     205           0 :                 goto out_unlock;
     206             : 
     207             :         /*
     208             :          * Ensure that the parent has as many links to the child as the child
     209             :          * thinks it has to the parent.
     210             :          */
     211        2290 :         if (spc.nlink != expected_nlink)
     212           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     213             : 
     214        2290 : out_unlock:
     215        2290 :         xfs_iunlock(dp, lock_mode);
     216        2290 : out_rele:
     217        2290 :         xchk_irele(sc, dp);
     218        2290 :         return error;
     219             : }
     220             : 
     221             : /*
     222             :  * Checking of Parent Pointers
     223             :  * ===========================
     224             :  *
     225             :  * On filesystems with directory parent pointers, we check the referential
     226             :  * integrity by visiting each parent pointer of a child file and checking that
     227             :  * the directory referenced by the pointer actually has a dirent pointing
     228             :  * forward to the child file.
     229             :  */
     230             : 
     231             : /* Deferred parent pointer entry that we saved for later. */
     232             : struct xchk_pptr {
     233             :         /* Cookie for retrieval of the pptr name. */
     234             :         xfblob_cookie                   name_cookie;
     235             : 
     236             :         /* Parent pointer attr key. */
     237             :         xfs_ino_t                       p_ino;
     238             :         uint32_t                        p_gen;
     239             : 
     240             :         /* Length of the pptr name. */
     241             :         uint8_t                         namelen;
     242             : };
     243             : 
     244             : struct xchk_pptrs {
     245             :         struct xfs_scrub        *sc;
     246             : 
     247             :         /* Scratch buffer for scanning pptr xattrs */
     248             :         struct xfs_parent_name_irec pptr;
     249             : 
     250             :         /* Fixed-size array of xchk_pptr structures. */
     251             :         struct xfarray          *pptr_entries;
     252             : 
     253             :         /* Blobs containing parent pointer names. */
     254             :         struct xfblob           *pptr_names;
     255             : 
     256             :         /* How many parent pointers did we find at the end? */
     257             :         unsigned long long      pptrs_found;
     258             : 
     259             :         /* Parent of this directory. */
     260             :         xfs_ino_t               parent_ino;
     261             : 
     262             :         /* If we've cycled the ILOCK, we must revalidate all deferred pptrs. */
     263             :         bool                    need_revalidate;
     264             : 
     265             :         /* xattr key and da args for parent pointer revalidation. */
     266             :         struct xfs_parent_scratch pptr_scratch;
     267             : };
     268             : 
     269             : /*
     270             :  * Walk an xattr of a file.  If this xattr is a parent pointer, follow it up
     271             :  * to a parent directory and check that the parent has a dirent pointing back
     272             :  * to us.
     273             :  */
     274             : STATIC int
     275    11638745 : xchk_parent_scan_dotdot(
     276             :         struct xfs_scrub        *sc,
     277             :         struct xfs_inode        *ip,
     278             :         unsigned int            attr_flags,
     279             :         const unsigned char     *name,
     280             :         unsigned int            namelen,
     281             :         const void              *value,
     282             :         unsigned int            valuelen,
     283             :         void                    *priv)
     284             : {
     285    11638745 :         struct xchk_pptrs       *pp = priv;
     286    11638745 :         const struct xfs_parent_name_rec *rec = (const void *)name;
     287             : 
     288             :         /* Ignore anything that isn't a parent pointer. */
     289    11638745 :         if (!(attr_flags & XFS_ATTR_PARENT))
     290             :                 return 0;
     291             : 
     292             :         /* Does the ondisk parent pointer structure make sense? */
     293    11200820 :         if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags)) {
     294           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     295           0 :                 return -ECANCELED;
     296             :         }
     297             : 
     298    11200786 :         if (!xfs_parent_valuecheck(sc->mp, value, valuelen)) {
     299           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     300           0 :                 return -ECANCELED;
     301             :         }
     302             : 
     303    11200827 :         xfs_parent_irec_from_disk(&pp->pptr, rec, value, valuelen);
     304             : 
     305    11200710 :         if (pp->parent_ino == pp->pptr.p_ino)
     306    11200801 :                 return -ECANCELED;
     307             : 
     308             :         return 0;
     309             : }
     310             : 
     311             : /* Look up the dotdot entry so that we can check it as we walk the pptrs. */
     312             : STATIC int
     313    11226346 : xchk_parent_pptr_and_dotdot(
     314             :         struct xchk_pptrs       *pp)
     315             : {
     316    11226346 :         struct xfs_scrub        *sc = pp->sc;
     317    11226346 :         int                     error;
     318             : 
     319             :         /* Look up '..' */
     320    11226346 :         error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &pp->parent_ino);
     321    11226474 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
     322           0 :                 return error;
     323    11226498 :         if (!xfs_verify_dir_ino(sc->mp, pp->parent_ino)) {
     324           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     325           0 :                 return 0;
     326             :         }
     327             : 
     328             :         /* Is this the root dir?  Then '..' must point to itself. */
     329    11226319 :         if (sc->ip == sc->mp->m_rootip || sc->ip == sc->mp->m_metadirip) {
     330       25670 :                 if (sc->ip->i_ino != pp->parent_ino)
     331           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     332       25670 :                 return 0;
     333             :         }
     334             : 
     335             :         /*
     336             :          * If this is now an unlinked directory, the dotdot value is
     337             :          * meaningless as long as it points to a valid inode.
     338             :          */
     339    11200649 :         if (VFS_I(sc->ip)->i_nlink == 0)
     340             :                 return 0;
     341             : 
     342    11200649 :         if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     343             :                 return 0;
     344             : 
     345             :         /* Otherwise, walk the pptrs again, and check. */
     346    11200826 :         error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_dotdot, NULL, pp);
     347    11200641 :         if (error == -ECANCELED)
     348             :                 return 0;
     349           0 :         if (error == 0) {
     350             :                 /* Didn't find a matching parent pointer. */
     351           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     352             :         }
     353           0 :         return error;
     354             : }
     355             : 
     356             : /*
     357             :  * Try to lock a parent directory for checking dirents.  Returns the inode
     358             :  * flags for the locks we now hold, or zero if we failed.
     359             :  */
     360             : STATIC unsigned int
     361    84120711 : xchk_parent_lock_dir(
     362             :         struct xfs_scrub        *sc,
     363             :         struct xfs_inode        *dp)
     364             : {
     365    84120711 :         if (!xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED))
     366             :                 return 0;
     367             : 
     368    62033109 :         if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED)) {
     369      448621 :                 xfs_iunlock(dp, XFS_IOLOCK_SHARED);
     370      448621 :                 return 0;
     371             :         }
     372             : 
     373    61584923 :         if (!xfs_need_iread_extents(&dp->i_df))
     374             :                 return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED;
     375             : 
     376         304 :         xfs_iunlock(dp, XFS_ILOCK_SHARED);
     377             : 
     378         304 :         if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {
     379           0 :                 xfs_iunlock(dp, XFS_IOLOCK_SHARED);
     380           0 :                 return 0;
     381             :         }
     382             : 
     383             :         return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
     384             : }
     385             : 
     386             : /* Check the forward link (dirent) associated with this parent pointer. */
     387             : STATIC int
     388    61584093 : xchk_parent_dirent(
     389             :         struct xchk_pptrs       *pp,
     390             :         struct xfs_inode        *dp)
     391             : {
     392    61584093 :         struct xfs_name         xname = {
     393    61584093 :                 .name           = pp->pptr.p_name,
     394    61584093 :                 .len            = pp->pptr.p_namelen,
     395             :         };
     396    61584093 :         struct xfs_scrub        *sc = pp->sc;
     397    61584093 :         xfs_ino_t               child_ino;
     398    61584093 :         int                     error;
     399             : 
     400             :         /*
     401             :          * Use the name attached to this parent pointer to look up the
     402             :          * directory entry in the alleged parent.
     403             :          */
     404    61584093 :         error = xchk_dir_lookup(sc, dp, &xname, &child_ino);
     405    61581739 :         if (error == -ENOENT) {
     406           0 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     407           0 :                 return 0;
     408             :         }
     409    61581739 :         if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
     410           0 :                 return error;
     411             : 
     412             :         /* Does the inode number match? */
     413    61581663 :         if (child_ino != sc->ip->i_ino) {
     414           0 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     415           0 :                 return 0;
     416             :         }
     417             : 
     418             :         return 0;
     419             : }
     420             : 
     421             : /* Try to grab a parent directory. */
     422             : STATIC int
     423    84122880 : xchk_parent_iget(
     424             :         struct xchk_pptrs               *pp,
     425             :         struct xfs_inode                **dpp)
     426             : {
     427    84122880 :         struct xfs_scrub                *sc = pp->sc;
     428    84122880 :         struct xfs_inode                *ip;
     429    84122880 :         int                             error;
     430             : 
     431             :         /* Validate inode number. */
     432    84122880 :         error = xfs_dir_ino_validate(sc->mp, pp->pptr.p_ino);
     433    84123281 :         if (error) {
     434           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     435           0 :                 return -ECANCELED;
     436             :         }
     437             : 
     438    84123281 :         error = xchk_iget(sc, pp->pptr.p_ino, &ip);
     439    84123725 :         if (error == -EINVAL || error == -ENOENT) {
     440           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     441           0 :                 return -ECANCELED;
     442             :         }
     443    84123725 :         if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
     444           0 :                 return error;
     445             : 
     446             :         /* The parent must be a directory. */
     447    84120356 :         if (!S_ISDIR(VFS_I(ip)->i_mode)) {
     448           2 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     449           2 :                 goto out_rele;
     450             :         }
     451             : 
     452             :         /* Validate generation number. */
     453    84120354 :         if (VFS_I(ip)->i_generation != pp->pptr.p_gen) {
     454           0 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     455           0 :                 goto out_rele;
     456             :         }
     457             : 
     458    84120354 :         *dpp = ip;
     459    84120354 :         return 0;
     460           2 : out_rele:
     461           2 :         xchk_irele(sc, ip);
     462           2 :         return 0;
     463             : }
     464             : 
     465             : /*
     466             :  * Walk an xattr of a file.  If this xattr is a parent pointer, follow it up
     467             :  * to a parent directory and check that the parent has a dirent pointing back
     468             :  * to us.
     469             :  */
     470             : STATIC int
     471   124193645 : xchk_parent_scan_attr(
     472             :         struct xfs_scrub        *sc,
     473             :         struct xfs_inode        *ip,
     474             :         unsigned int            attr_flags,
     475             :         const unsigned char     *name,
     476             :         unsigned int            namelen,
     477             :         const void              *value,
     478             :         unsigned int            valuelen,
     479             :         void                    *priv)
     480             : {
     481   124193645 :         struct xfs_name         dname = {
     482             :                 .name           = value,
     483             :                 .len            = valuelen,
     484             :         };
     485   124193645 :         struct xchk_pptrs       *pp = priv;
     486   124193645 :         struct xfs_inode        *dp = NULL;
     487   124193645 :         const struct xfs_parent_name_rec *rec = (const void *)name;
     488   124193645 :         unsigned int            lockmode;
     489   124193645 :         xfs_dahash_t            computed_hash;
     490   124193645 :         int                     error;
     491             : 
     492             :         /* Ignore anything that isn't a parent pointer. */
     493   124193645 :         if (!(attr_flags & XFS_ATTR_PARENT))
     494             :                 return 0;
     495             : 
     496             :         /* Does the ondisk parent pointer structure make sense? */
     497    84121231 :         if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags)) {
     498           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     499           0 :                 return -ECANCELED;
     500             :         }
     501             : 
     502    84119986 :         if (!xfs_parent_valuecheck(sc->mp, value, valuelen)) {
     503           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     504           0 :                 return -ECANCELED;
     505             :         }
     506             : 
     507    84123173 :         xfs_parent_irec_from_disk(&pp->pptr, rec, value, valuelen);
     508             : 
     509             :         /*
     510             :          * If the namehash of the dirent name encoded in the parent pointer
     511             :          * attr value doesn't match the namehash in the parent pointer key,
     512             :          * the parent pointer is corrupt.
     513             :          */
     514    84123624 :         computed_hash = xfs_dir2_hashname(ip->i_mount, &dname);
     515    84123771 :         if (pp->pptr.p_namehash != computed_hash) {
     516           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     517           0 :                 return -ECANCELED;
     518             :         }
     519    84123771 :         pp->pptrs_found++;
     520             : 
     521    84123771 :         error = xchk_parent_iget(pp, &dp);
     522    84120381 :         if (error)
     523             :                 return error;
     524    84120381 :         if (!dp)
     525             :                 return 0;
     526             : 
     527             :         /* Try to lock the inode. */
     528    84120379 :         lockmode = xchk_parent_lock_dir(sc, dp);
     529    84123049 :         if (!lockmode) {
     530    22538541 :                 struct xchk_pptr        save_pp = {
     531    22538541 :                         .p_ino          = pp->pptr.p_ino,
     532    22538541 :                         .p_gen          = pp->pptr.p_gen,
     533    22538541 :                         .namelen        = pp->pptr.p_namelen,
     534             :                 };
     535             : 
     536             :                 /* Couldn't lock the inode, so save the pptr for later. */
     537    22538541 :                 trace_xchk_parent_defer(sc->ip, pp->pptr.p_name,
     538    22538541 :                                 pp->pptr.p_namelen, dp->i_ino);
     539             : 
     540    45077433 :                 error = xfblob_store(pp->pptr_names, &save_pp.name_cookie,
     541    22538717 :                                 pp->pptr.p_name, pp->pptr.p_namelen);
     542    22538716 :                 if (xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,
     543             :                                         &error))
     544    22538726 :                         goto out_rele;
     545             : 
     546           0 :                 error = xfarray_append(pp->pptr_entries, &save_pp);
     547           0 :                 if (xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,
     548             :                                         &error))
     549             :                         goto out_rele;
     550             : 
     551           0 :                 goto out_rele;
     552             :         }
     553             : 
     554    61584508 :         error = xchk_parent_dirent(pp, dp);
     555    61582314 :         if (error)
     556             :                 goto out_unlock;
     557             : 
     558             : out_unlock:
     559    61582314 :         xfs_iunlock(dp, lockmode);
     560    84123934 : out_rele:
     561    84123934 :         xchk_irele(sc, dp);
     562    84124104 :         return error;
     563             : }
     564             : 
     565             : /*
     566             :  * Revalidate a parent pointer that we collected in the past but couldn't check
     567             :  * because of lock contention.  Returns 0 if the parent pointer is still valid,
     568             :  * -ENOENT if it has gone away on us, or a negative errno.
     569             :  */
     570             : STATIC int
     571           0 : xchk_parent_revalidate_pptr(
     572             :         struct xchk_pptrs       *pp)
     573             : {
     574           0 :         struct xfs_scrub        *sc = pp->sc;
     575           0 :         int                     error;
     576             : 
     577           0 :         error = xfs_parent_lookup(sc->tp, sc->ip, &pp->pptr,
     578             :                         &pp->pptr_scratch);
     579           0 :         if (error == -ENOATTR) {
     580             :                 /* Parent pointer went away, nothing to revalidate. */
     581           0 :                 return -ENOENT;
     582             :         }
     583             : 
     584             :         return error;
     585             : }
     586             : 
     587             : /*
     588             :  * Check a parent pointer the slow way, which means we cycle locks a bunch
     589             :  * and put up with revalidation until we get it done.
     590             :  */
     591             : STATIC int
     592           0 : xchk_parent_slow_pptr(
     593             :         struct xchk_pptrs       *pp,
     594             :         struct xchk_pptr        *pptr)
     595             : {
     596           0 :         struct xfs_scrub        *sc = pp->sc;
     597           0 :         struct xfs_inode        *dp = NULL;
     598           0 :         unsigned int            lockmode;
     599           0 :         int                     error;
     600             : 
     601             :         /* Restore the saved parent pointer into the irec. */
     602           0 :         pp->pptr.p_ino = pptr->p_ino;
     603           0 :         pp->pptr.p_gen = pptr->p_gen;
     604             : 
     605           0 :         error = xfblob_load(pp->pptr_names, pptr->name_cookie, pp->pptr.p_name,
     606           0 :                         pptr->namelen);
     607           0 :         if (error)
     608             :                 return error;
     609           0 :         pp->pptr.p_name[MAXNAMELEN - 1] = 0;
     610           0 :         pp->pptr.p_namelen = pptr->namelen;
     611           0 :         xfs_parent_irec_hashname(sc->mp, &pp->pptr);
     612             : 
     613             :         /* Check that the deferred parent pointer still exists. */
     614           0 :         if (pp->need_revalidate) {
     615           0 :                 error = xchk_parent_revalidate_pptr(pp);
     616           0 :                 if (error == -ENOENT)
     617             :                         return 0;
     618           0 :                 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,
     619             :                                         &error))
     620           0 :                         return error;
     621             :         }
     622             : 
     623           0 :         error = xchk_parent_iget(pp, &dp);
     624           0 :         if (error)
     625             :                 return error;
     626           0 :         if (!dp)
     627             :                 return 0;
     628             : 
     629             :         /*
     630             :          * If we can grab both IOLOCK and ILOCK of the alleged parent, we
     631             :          * can proceed with the validation.
     632             :          */
     633           0 :         lockmode = xchk_parent_lock_dir(sc, dp);
     634           0 :         if (lockmode)
     635           0 :                 goto check_dirent;
     636             : 
     637             :         /*
     638             :          * We couldn't lock the parent dir.  Drop all the locks and try to
     639             :          * get them again, one at a time.
     640             :          */
     641           0 :         xchk_iunlock(sc, sc->ilock_flags);
     642           0 :         pp->need_revalidate = true;
     643             : 
     644           0 :         trace_xchk_parent_slowpath(sc->ip, pp->pptr.p_name, pptr->namelen,
     645           0 :                         dp->i_ino);
     646             : 
     647           0 :         while (true) {
     648           0 :                 xchk_ilock(sc, XFS_IOLOCK_EXCL);
     649           0 :                 if (xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
     650           0 :                         xchk_ilock(sc, XFS_ILOCK_EXCL);
     651           0 :                         if (xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {
     652             :                                 break;
     653             :                         }
     654           0 :                         xchk_iunlock(sc, XFS_ILOCK_EXCL);
     655             :                 }
     656           0 :                 xchk_iunlock(sc, XFS_IOLOCK_EXCL);
     657             : 
     658           0 :                 if (xchk_should_terminate(sc, &error))
     659           0 :                         goto out_rele;
     660             : 
     661           0 :                 delay(1);
     662             :         }
     663           0 :         lockmode = XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
     664             : 
     665             :         /* Revalidate the parent pointer now that we cycled locks. */
     666           0 :         error = xchk_parent_revalidate_pptr(pp);
     667           0 :         if (error == -ENOENT)
     668           0 :                 goto out_unlock;
     669           0 :         if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
     670           0 :                 goto out_unlock;
     671             : 
     672           0 : check_dirent:
     673           0 :         error = xchk_parent_dirent(pp, dp);
     674           0 : out_unlock:
     675           0 :         xfs_iunlock(dp, lockmode);
     676           0 : out_rele:
     677           0 :         xchk_irele(sc, dp);
     678           0 :         return error;
     679             : }
     680             : 
     681             : /* Check all the parent pointers that we deferred the first time around. */
     682             : STATIC int
     683    77621229 : xchk_parent_finish_slow_pptrs(
     684             :         struct xchk_pptrs       *pp)
     685             : {
     686    77621229 :         xfarray_idx_t           array_cur;
     687    77621229 :         int                     error;
     688             : 
     689    77621229 :         foreach_xfarray_idx(pp->pptr_entries, array_cur) {
     690           0 :                 struct xchk_pptr        pptr;
     691             : 
     692           0 :                 if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     693           0 :                         return 0;
     694             : 
     695           0 :                 error = xfarray_load(pp->pptr_entries, array_cur, &pptr);
     696           0 :                 if (error)
     697           0 :                         return error;
     698             : 
     699           0 :                 error = xchk_parent_slow_pptr(pp, &pptr);
     700           0 :                 if (error)
     701           0 :                         return error;
     702             :         }
     703             : 
     704             :         /* Empty out both xfiles now that we've checked everything. */
     705    77621093 :         xfarray_truncate(pp->pptr_entries);
     706    77621828 :         xfblob_truncate(pp->pptr_names);
     707    77621828 :         return 0;
     708             : }
     709             : 
     710             : /* Count the number of parent pointers. */
     711             : STATIC int
     712           0 : xchk_parent_count_pptr(
     713             :         struct xfs_scrub        *sc,
     714             :         struct xfs_inode        *ip,
     715             :         unsigned int            attr_flags,
     716             :         const unsigned char     *name,
     717             :         unsigned int            namelen,
     718             :         const void              *value,
     719             :         unsigned int            valuelen,
     720             :         void                    *priv)
     721             : {
     722           0 :         struct xchk_pptrs       *pp = priv;
     723             : 
     724           0 :         if (attr_flags & XFS_ATTR_PARENT)
     725           0 :                 pp->pptrs_found++;
     726           0 :         return 0;
     727             : }
     728             : 
     729             : /*
     730             :  * Compare the number of parent pointers to the link count.  For
     731             :  * non-directories these should be the same.  For unlinked directories the
     732             :  * count should be zero; for linked directories, it should be nonzero.
     733             :  */
     734             : STATIC int
     735    77621349 : xchk_parent_count_pptrs(
     736             :         struct xchk_pptrs       *pp)
     737             : {
     738    77621349 :         struct xfs_scrub        *sc = pp->sc;
     739    77621349 :         int                     error;
     740             : 
     741             :         /*
     742             :          * If we cycled the ILOCK while cross-checking parent pointers with
     743             :          * dirents, then we need to recalculate the number of parent pointers.
     744             :          */
     745    77621349 :         if (pp->need_revalidate) {
     746           0 :                 pp->pptrs_found = 0;
     747           0 :                 error = xchk_xattr_walk(sc, sc->ip, xchk_parent_count_pptr,
     748             :                                 NULL, pp);
     749           0 :                 if (error == -ECANCELED)
     750             :                         return 0;
     751           0 :                 if (error)
     752             :                         return error;
     753             :         }
     754             : 
     755    77621349 :         if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {
     756    11226332 :                 if (sc->ip == sc->mp->m_rootip ||
     757    11200662 :                     sc->ip == sc->mp->m_metadirip)
     758       25670 :                         pp->pptrs_found++;
     759             : 
     760    11226332 :                 if (VFS_I(sc->ip)->i_nlink == 0 && pp->pptrs_found > 0)
     761           0 :                         xchk_ino_set_corrupt(sc, sc->ip->i_ino);
     762    11226332 :                 else if (VFS_I(sc->ip)->i_nlink > 0 &&
     763    11226484 :                          pp->pptrs_found == 0)
     764           0 :                         xchk_ino_set_corrupt(sc, sc->ip->i_ino);
     765             :         } else {
     766    66395017 :                 if (VFS_I(sc->ip)->i_nlink != pp->pptrs_found)
     767           0 :                         xchk_ino_set_corrupt(sc, sc->ip->i_ino);
     768             :         }
     769             : 
     770             :         return 0;
     771             : }
     772             : 
     773             : /* Check parent pointers of a file. */
     774             : STATIC int
     775    77620375 : xchk_parent_pptr(
     776             :         struct xfs_scrub        *sc)
     777             : {
     778    77620375 :         struct xchk_pptrs       *pp;
     779    77620375 :         char                    *descr;
     780    77620375 :         int                     error;
     781             : 
     782    77620375 :         pp = kvzalloc(sizeof(struct xchk_pptrs), XCHK_GFP_FLAGS);
     783    77620773 :         if (!pp)
     784             :                 return -ENOMEM;
     785    77620773 :         pp->sc = sc;
     786             : 
     787             :         /*
     788             :          * Set up some staging memory for parent pointers that we can't check
     789             :          * due to locking contention.
     790             :          */
     791    77620773 :         descr = xchk_xfile_ino_descr(sc, "slow parent pointer entries");
     792    77620553 :         error = xfarray_create(descr, 0, sizeof(struct xchk_pptr),
     793             :                         &pp->pptr_entries);
     794    77620769 :         kfree(descr);
     795    77620371 :         if (error)
     796           0 :                 goto out_pp;
     797             : 
     798    77620371 :         descr = xchk_xfile_ino_descr(sc, "slow parent pointer names");
     799    77620574 :         error = xfblob_create(descr, &pp->pptr_names);
     800    77621542 :         kfree(descr);
     801    77621625 :         if (error)
     802           0 :                 goto out_entries;
     803             : 
     804    77621625 :         error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, NULL, pp);
     805    77620249 :         if (error == -ECANCELED) {
     806           0 :                 error = 0;
     807           0 :                 goto out_names;
     808             :         }
     809    77620249 :         if (error)
     810           0 :                 goto out_names;
     811             : 
     812    77620249 :         error = xchk_parent_finish_slow_pptrs(pp);
     813    77621918 :         if (error)
     814           0 :                 goto out_names;
     815             : 
     816    77621918 :         if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     817           0 :                 goto out_names;
     818             : 
     819             :         /*
     820             :          * For subdirectories, make sure the dotdot entry references the same
     821             :          * inode as the parent pointers.
     822             :          *
     823             :          * If we're scanning a /consistent/ directory, there should only be
     824             :          * one parent pointer, and it should point to the same directory as
     825             :          * the dotdot entry.
     826             :          *
     827             :          * However, a corrupt directory tree might feature a subdirectory with
     828             :          * multiple parents.  The directory loop scanner is responsible for
     829             :          * correcting that kind of problem, so for now we only validate that
     830             :          * the dotdot entry matches /one/ of the parents.
     831             :          */
     832    77621918 :         if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {
     833    11226402 :                 error = xchk_parent_pptr_and_dotdot(pp);
     834    11226481 :                 if (error)
     835           0 :                         goto out_names;
     836             :         }
     837             : 
     838             :         if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     839             :                 goto out_pp;
     840             : 
     841             :         /*
     842    77621997 :          * Complain if the number of parent pointers doesn't match the link
     843    77620949 :          * count.  This could be a sign of missing parent pointers (or an
     844           0 :          * incorrect link count).
     845             :          */
     846    77620949 :         error = xchk_parent_count_pptrs(pp);
     847    77620949 :         if (error)
     848    77621872 :                 goto out_names;
     849    77621872 : 
     850    77622038 : out_names:
     851    77622038 :         xfblob_destroy(pp->pptr_names);
     852    77622038 : out_entries:
     853             :         xfarray_destroy(pp->pptr_entries);
     854             : out_pp:
     855             :         kvfree(pp);
     856             :         return error;
     857    77636706 : }
     858             : 
     859             : /* Scrub a parent pointer. */
     860    77636706 : int
     861    77636706 : xchk_parent(
     862    77636706 :         struct xfs_scrub        *sc)
     863             : {
     864    77636706 :         struct xfs_mount        *mp = sc->mp;
     865    77621592 :         xfs_ino_t               parent_ino;
     866             :         int                     error = 0;
     867             : 
     868             :         if (xfs_has_parent(mp))
     869             :                 return xchk_parent_pptr(sc);
     870             : 
     871       15114 :         /*
     872             :          * If we're a directory, check that the '..' link points up to
     873             :          * a directory that has one entry pointing to us.
     874             :          */
     875        2358 :         if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
     876           0 :                 return -ENOENT;
     877           0 : 
     878             :         /* We're not a special inode, are we? */
     879             :         if (!xfs_verify_dir_ino(mp, sc->ip->i_ino)) {
     880        2358 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     881        2358 :                 return 0;
     882             :         }
     883             : 
     884             :         do {
     885        2358 :                 if (xchk_should_terminate(sc, &error))
     886             :                         break;
     887        2358 : 
     888           0 :                 /* Look up '..' */
     889        2358 :                 error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,
     890           0 :                                 &parent_ino);
     891           0 :                 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
     892             :                         return error;
     893             :                 if (!xfs_verify_dir_ino(mp, parent_ino)) {
     894             :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     895             :                         return 0;
     896             :                 }
     897             : 
     898        2358 :                 /*
     899        2358 :                  * Check that the dotdot entry points to a parent directory
     900             :                  * containing a dirent pointing to this subdirectory.
     901        2358 :                  */
     902             :                 error = xchk_parent_validate(sc, parent_ino);
     903             :         } while (error == -EAGAIN);
     904             : 
     905             :         return error;
     906             : }

Generated by: LCOV version 1.14