LCOV - code coverage report
Current view: top level - fs/xfs/scrub - parent.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-acha @ Mon Jul 31 20:08:06 PDT 2023 Lines: 207 357 58.0 %
Date: 2023-07-31 20:08:07 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   100537591 : xchk_setup_parent(
      34             :         struct xfs_scrub        *sc)
      35             : {
      36   100537591 :         int                     error;
      37             : 
      38   100537591 :         if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
      39     3416270 :                 error = xrep_setup_parent(sc);
      40     3416261 :                 if (error)
      41             :                         return error;
      42             :         }
      43             : 
      44   100537582 :         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       13453 : 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       13453 :         struct xchk_parent_ctx  *spc = priv;
      67       13453 :         int                     error = 0;
      68             : 
      69             :         /* Does this name make sense? */
      70       13453 :         if (!xfs_dir2_namecheck(name->name, name->len))
      71           0 :                 error = -EFSCORRUPTED;
      72       13454 :         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
      73           0 :                 return error;
      74             : 
      75       13455 :         if (sc->ip->i_ino == ino)
      76        1250 :                 spc->nlink++;
      77             : 
      78       13455 :         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        1250 : xchk_parent_ilock_dir(
      90             :         struct xfs_inode        *dp)
      91             : {
      92        1250 :         if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED))
      93             :                 return 0;
      94             : 
      95        1250 :         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        1318 : xchk_parent_validate(
     114             :         struct xfs_scrub        *sc,
     115             :         xfs_ino_t               parent_ino)
     116             : {
     117        1318 :         struct xchk_parent_ctx  spc = {
     118             :                 .sc             = sc,
     119             :                 .nlink          = 0,
     120             :         };
     121        1318 :         struct xfs_mount        *mp = sc->mp;
     122        1318 :         struct xfs_inode        *dp = NULL;
     123        1318 :         xfs_nlink_t             expected_nlink;
     124        1318 :         unsigned int            lock_mode;
     125        1318 :         int                     error = 0;
     126             : 
     127             :         /* Is this the root dir?  Then '..' must point to itself. */
     128        1318 :         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             :         /* '..' must not point to ourselves. */
     136        1250 :         if (sc->ip->i_ino == parent_ino) {
     137           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     138           0 :                 return 0;
     139             :         }
     140             : 
     141             :         /*
     142             :          * If we're an unlinked directory, the parent /won't/ have a link
     143             :          * to us.  Otherwise, it should have one link.
     144             :          */
     145        1250 :         expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;
     146             : 
     147             :         /*
     148             :          * Grab the parent directory inode.  This must be released before we
     149             :          * cancel the scrub transaction.
     150             :          *
     151             :          * If _iget returns -EINVAL or -ENOENT then the parent inode number is
     152             :          * garbage and the directory is corrupt.  If the _iget returns
     153             :          * -EFSCORRUPTED or -EFSBADCRC then the parent is corrupt which is a
     154             :          *  cross referencing error.  Any other error is an operational error.
     155             :          */
     156        1250 :         error = xchk_iget(sc, parent_ino, &dp);
     157        1250 :         if (error == -EINVAL || error == -ENOENT) {
     158           0 :                 error = -EFSCORRUPTED;
     159           0 :                 xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
     160           0 :                 return error;
     161             :         }
     162        1250 :         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
     163           0 :                 return error;
     164        1250 :         if (dp == sc->ip || xrep_is_tempfile(dp) ||
     165        1250 :             !S_ISDIR(VFS_I(dp)->i_mode)) {
     166           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     167           0 :                 goto out_rele;
     168             :         }
     169             : 
     170        1250 :         lock_mode = xchk_parent_ilock_dir(dp);
     171        1250 :         if (!lock_mode) {
     172           0 :                 xchk_iunlock(sc, XFS_ILOCK_EXCL);
     173           0 :                 xchk_ilock(sc, XFS_ILOCK_EXCL);
     174           0 :                 error = -EAGAIN;
     175           0 :                 goto out_rele;
     176             :         }
     177             : 
     178             :         /*
     179             :          * We cannot yet validate this parent pointer if the directory looks as
     180             :          * though it has been zapped by the inode record repair code.
     181             :          */
     182        1250 :         if (xchk_dir_looks_zapped(dp)) {
     183           0 :                 error = -EFSCORRUPTED;
     184           0 :                 xchk_set_incomplete(sc);
     185           0 :                 goto out_unlock;
     186             :         }
     187             : 
     188             :         /* Look for a directory entry in the parent pointing to the child. */
     189        1250 :         error = xchk_dir_walk(sc, dp, xchk_parent_actor, &spc);
     190        1250 :         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
     191           0 :                 goto out_unlock;
     192             : 
     193             :         /*
     194             :          * Ensure that the parent has as many links to the child as the child
     195             :          * thinks it has to the parent.
     196             :          */
     197        1250 :         if (spc.nlink != expected_nlink)
     198           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     199             : 
     200        1250 : out_unlock:
     201        1250 :         xfs_iunlock(dp, lock_mode);
     202        1250 : out_rele:
     203        1250 :         xchk_irele(sc, dp);
     204        1250 :         return error;
     205             : }
     206             : 
     207             : /*
     208             :  * Checking of Parent Pointers
     209             :  * ===========================
     210             :  *
     211             :  * On filesystems with directory parent pointers, we check the referential
     212             :  * integrity by visiting each parent pointer of a child file and checking that
     213             :  * the directory referenced by the pointer actually has a dirent pointing
     214             :  * forward to the child file.
     215             :  */
     216             : 
     217             : /* Deferred parent pointer entry that we saved for later. */
     218             : struct xchk_pptr {
     219             :         /* Cookie for retrieval of the pptr name. */
     220             :         xfblob_cookie                   name_cookie;
     221             : 
     222             :         /* Parent pointer attr key. */
     223             :         xfs_ino_t                       p_ino;
     224             :         uint32_t                        p_gen;
     225             : 
     226             :         /* Length of the pptr name. */
     227             :         uint8_t                         namelen;
     228             : };
     229             : 
     230             : struct xchk_pptrs {
     231             :         struct xfs_scrub        *sc;
     232             : 
     233             :         /* Scratch buffer for scanning pptr xattrs */
     234             :         struct xfs_parent_name_irec pptr;
     235             : 
     236             :         /* Fixed-size array of xchk_pptr structures. */
     237             :         struct xfarray          *pptr_entries;
     238             : 
     239             :         /* Blobs containing parent pointer names. */
     240             :         struct xfblob           *pptr_names;
     241             : 
     242             :         /* How many parent pointers did we find at the end? */
     243             :         unsigned long long      pptrs_found;
     244             : 
     245             :         /* Parent of this directory. */
     246             :         xfs_ino_t               parent_ino;
     247             : 
     248             :         /* If we've cycled the ILOCK, we must revalidate all deferred pptrs. */
     249             :         bool                    need_revalidate;
     250             : 
     251             :         /* xattr key and da args for parent pointer revalidation. */
     252             :         struct xfs_parent_scratch pptr_scratch;
     253             : };
     254             : 
     255             : /*
     256             :  * Walk an xattr of a file.  If this xattr is a parent pointer, follow it up
     257             :  * to a parent directory and check that the parent has a dirent pointing back
     258             :  * to us.
     259             :  */
     260             : STATIC int
     261    14912357 : xchk_parent_scan_dotdot(
     262             :         struct xfs_scrub        *sc,
     263             :         struct xfs_inode        *ip,
     264             :         unsigned int            attr_flags,
     265             :         const unsigned char     *name,
     266             :         unsigned int            namelen,
     267             :         const void              *value,
     268             :         unsigned int            valuelen,
     269             :         void                    *priv)
     270             : {
     271    14912357 :         struct xchk_pptrs       *pp = priv;
     272    14912357 :         const struct xfs_parent_name_rec *rec = (const void *)name;
     273             : 
     274             :         /* Ignore anything that isn't a parent pointer. */
     275    14912357 :         if (!(attr_flags & XFS_ATTR_PARENT))
     276             :                 return 0;
     277             : 
     278             :         /* Does the ondisk parent pointer structure make sense? */
     279    14291049 :         if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags)) {
     280           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     281           0 :                 return -ECANCELED;
     282             :         }
     283             : 
     284    14290946 :         if (!xfs_parent_valuecheck(sc->mp, value, valuelen)) {
     285           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     286           0 :                 return -ECANCELED;
     287             :         }
     288             : 
     289    14290973 :         xfs_parent_irec_from_disk(&pp->pptr, rec, value, valuelen);
     290             : 
     291    14291020 :         if (pp->parent_ino == pp->pptr.p_ino)
     292    14291025 :                 return -ECANCELED;
     293             : 
     294             :         return 0;
     295             : }
     296             : 
     297             : /* Look up the dotdot entry so that we can check it as we walk the pptrs. */
     298             : STATIC int
     299    14331051 : xchk_parent_pptr_and_dotdot(
     300             :         struct xchk_pptrs       *pp)
     301             : {
     302    14331051 :         struct xfs_scrub        *sc = pp->sc;
     303    14331051 :         int                     error;
     304             : 
     305             :         /* Look up '..' */
     306    14331051 :         error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &pp->parent_ino);
     307    14331032 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
     308           0 :                 return error;
     309    14331011 :         if (!xfs_verify_dir_ino(sc->mp, pp->parent_ino)) {
     310           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     311           0 :                 return 0;
     312             :         }
     313             : 
     314             :         /* Is this the root dir?  Then '..' must point to itself. */
     315    14331057 :         if (sc->ip == sc->mp->m_rootip) {
     316       40028 :                 if (sc->ip->i_ino != pp->parent_ino)
     317           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     318       40028 :                 return 0;
     319             :         }
     320             : 
     321             :         /*
     322             :          * If this is now an unlinked directory, the dotdot value is
     323             :          * meaningless as long as it points to a valid inode.
     324             :          */
     325    14291029 :         if (VFS_I(sc->ip)->i_nlink == 0)
     326             :                 return 0;
     327             : 
     328    14291029 :         if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     329             :                 return 0;
     330             : 
     331             :         /* Otherwise, walk the pptrs again, and check. */
     332    14291058 :         error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_dotdot, NULL, pp);
     333    14290954 :         if (error == -ECANCELED)
     334             :                 return 0;
     335           0 :         if (error == 0) {
     336             :                 /* Didn't find a matching parent pointer. */
     337           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     338             :         }
     339           0 :         return error;
     340             : }
     341             : 
     342             : /*
     343             :  * Try to lock a parent directory for checking dirents.  Returns the inode
     344             :  * flags for the locks we now hold, or zero if we failed.
     345             :  */
     346             : STATIC unsigned int
     347   107898664 : xchk_parent_lock_dir(
     348             :         struct xfs_scrub        *sc,
     349             :         struct xfs_inode        *dp)
     350             : {
     351   107898664 :         if (!xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED))
     352             :                 return 0;
     353             : 
     354    81132401 :         if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED)) {
     355      531611 :                 xfs_iunlock(dp, XFS_IOLOCK_SHARED);
     356      531611 :                 return 0;
     357             :         }
     358             : 
     359    80600291 :         if (!xfs_need_iread_extents(&dp->i_df))
     360             :                 return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED;
     361             : 
     362         375 :         xfs_iunlock(dp, XFS_ILOCK_SHARED);
     363             : 
     364         375 :         if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {
     365           0 :                 xfs_iunlock(dp, XFS_IOLOCK_SHARED);
     366           0 :                 return 0;
     367             :         }
     368             : 
     369             :         return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
     370             : }
     371             : 
     372             : /* Check the forward link (dirent) associated with this parent pointer. */
     373             : STATIC int
     374    80599738 : xchk_parent_dirent(
     375             :         struct xchk_pptrs       *pp,
     376             :         struct xfs_inode        *dp)
     377             : {
     378    80599738 :         struct xfs_name         xname = {
     379    80599738 :                 .name           = pp->pptr.p_name,
     380    80599738 :                 .len            = pp->pptr.p_namelen,
     381             :         };
     382    80599738 :         struct xfs_scrub        *sc = pp->sc;
     383    80599738 :         xfs_ino_t               child_ino;
     384    80599738 :         int                     error;
     385             : 
     386             :         /*
     387             :          * Use the name attached to this parent pointer to look up the
     388             :          * directory entry in the alleged parent.
     389             :          */
     390    80599738 :         error = xchk_dir_lookup(sc, dp, &xname, &child_ino);
     391    80598217 :         if (error == -ENOENT) {
     392           0 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     393           0 :                 return 0;
     394             :         }
     395    80598217 :         if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
     396           0 :                 return error;
     397             : 
     398             :         /* Does the inode number match? */
     399    80596934 :         if (child_ino != sc->ip->i_ino) {
     400           0 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     401           0 :                 return 0;
     402             :         }
     403             : 
     404             :         return 0;
     405             : }
     406             : 
     407             : /* Try to grab a parent directory. */
     408             : STATIC int
     409   107902818 : xchk_parent_iget(
     410             :         struct xchk_pptrs               *pp,
     411             :         struct xfs_inode                **dpp)
     412             : {
     413   107902818 :         struct xfs_scrub                *sc = pp->sc;
     414   107902818 :         struct xfs_inode                *ip;
     415   107902818 :         int                             error;
     416             : 
     417             :         /* Validate inode number. */
     418   107902818 :         error = xfs_dir_ino_validate(sc->mp, pp->pptr.p_ino);
     419   107903319 :         if (error) {
     420           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     421           0 :                 return -ECANCELED;
     422             :         }
     423             : 
     424   107903319 :         error = xchk_iget(sc, pp->pptr.p_ino, &ip);
     425   107905865 :         if (error == -EINVAL || error == -ENOENT) {
     426           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     427           0 :                 return -ECANCELED;
     428             :         }
     429   107905865 :         if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
     430           0 :                 return error;
     431             : 
     432             :         /* The parent must be a directory. */
     433   107898445 :         if (!S_ISDIR(VFS_I(ip)->i_mode)) {
     434           2 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     435           2 :                 goto out_rele;
     436             :         }
     437             : 
     438             :         /* Validate generation number. */
     439   107898443 :         if (VFS_I(ip)->i_generation != pp->pptr.p_gen) {
     440           0 :                 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
     441           0 :                 goto out_rele;
     442             :         }
     443             : 
     444   107898443 :         *dpp = ip;
     445   107898443 :         return 0;
     446           2 : out_rele:
     447           2 :         xchk_irele(sc, ip);
     448           2 :         return 0;
     449             : }
     450             : 
     451             : /*
     452             :  * Walk an xattr of a file.  If this xattr is a parent pointer, follow it up
     453             :  * to a parent directory and check that the parent has a dirent pointing back
     454             :  * to us.
     455             :  */
     456             : STATIC int
     457   131019098 : xchk_parent_scan_attr(
     458             :         struct xfs_scrub        *sc,
     459             :         struct xfs_inode        *ip,
     460             :         unsigned int            attr_flags,
     461             :         const unsigned char     *name,
     462             :         unsigned int            namelen,
     463             :         const void              *value,
     464             :         unsigned int            valuelen,
     465             :         void                    *priv)
     466             : {
     467   131019098 :         struct xfs_name         dname = {
     468             :                 .name           = value,
     469             :                 .len            = valuelen,
     470             :         };
     471   131019098 :         struct xchk_pptrs       *pp = priv;
     472   131019098 :         struct xfs_inode        *dp = NULL;
     473   131019098 :         const struct xfs_parent_name_rec *rec = (const void *)name;
     474   131019098 :         unsigned int            lockmode;
     475   131019098 :         xfs_dahash_t            computed_hash;
     476   131019098 :         int                     error;
     477             : 
     478             :         /* Ignore anything that isn't a parent pointer. */
     479   131019098 :         if (!(attr_flags & XFS_ATTR_PARENT))
     480             :                 return 0;
     481             : 
     482             :         /* Does the ondisk parent pointer structure make sense? */
     483   107904067 :         if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags)) {
     484           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     485           0 :                 return -ECANCELED;
     486             :         }
     487             : 
     488   107904860 :         if (!xfs_parent_valuecheck(sc->mp, value, valuelen)) {
     489           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     490           0 :                 return -ECANCELED;
     491             :         }
     492             : 
     493   107904418 :         xfs_parent_irec_from_disk(&pp->pptr, rec, value, valuelen);
     494             : 
     495             :         /*
     496             :          * If the namehash of the dirent name encoded in the parent pointer
     497             :          * attr value doesn't match the namehash in the parent pointer key,
     498             :          * the parent pointer is corrupt.
     499             :          */
     500   107904316 :         computed_hash = xfs_dir2_hashname(ip->i_mount, &dname);
     501   107905918 :         if (pp->pptr.p_namehash != computed_hash) {
     502           0 :                 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
     503           0 :                 return -ECANCELED;
     504             :         }
     505   107905918 :         pp->pptrs_found++;
     506             : 
     507   107905918 :         error = xchk_parent_iget(pp, &dp);
     508   107897036 :         if (error)
     509             :                 return error;
     510   107897036 :         if (!dp)
     511             :                 return 0;
     512             : 
     513             :         /* Try to lock the inode. */
     514   107897034 :         lockmode = xchk_parent_lock_dir(sc, dp);
     515   107903967 :         if (!lockmode) {
     516    27302797 :                 struct xchk_pptr        save_pp = {
     517    27302797 :                         .p_ino          = pp->pptr.p_ino,
     518    27302797 :                         .p_gen          = pp->pptr.p_gen,
     519    27302797 :                         .namelen        = pp->pptr.p_namelen,
     520             :                 };
     521             : 
     522             :                 /* Couldn't lock the inode, so save the pptr for later. */
     523    27302797 :                 trace_xchk_parent_defer(sc->ip, pp->pptr.p_name,
     524    27302797 :                                 pp->pptr.p_namelen, dp->i_ino);
     525             : 
     526    54604821 :                 error = xfblob_store(pp->pptr_names, &save_pp.name_cookie,
     527    27302898 :                                 pp->pptr.p_name, pp->pptr.p_namelen);
     528    27301923 :                 if (xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,
     529             :                                         &error))
     530    27302489 :                         goto out_rele;
     531             : 
     532           0 :                 error = xfarray_append(pp->pptr_entries, &save_pp);
     533           0 :                 if (xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,
     534             :                                         &error))
     535             :                         goto out_rele;
     536             : 
     537           0 :                 goto out_rele;
     538             :         }
     539             : 
     540    80601170 :         error = xchk_parent_dirent(pp, dp);
     541    80598627 :         if (error)
     542             :                 goto out_unlock;
     543             : 
     544             : out_unlock:
     545    80598627 :         xfs_iunlock(dp, lockmode);
     546   107905548 : out_rele:
     547   107905548 :         xchk_irele(sc, dp);
     548   107908067 :         return error;
     549             : }
     550             : 
     551             : /*
     552             :  * Revalidate a parent pointer that we collected in the past but couldn't check
     553             :  * because of lock contention.  Returns 0 if the parent pointer is still valid,
     554             :  * -ENOENT if it has gone away on us, or a negative errno.
     555             :  */
     556             : STATIC int
     557           0 : xchk_parent_revalidate_pptr(
     558             :         struct xchk_pptrs       *pp)
     559             : {
     560           0 :         struct xfs_scrub        *sc = pp->sc;
     561           0 :         int                     error;
     562             : 
     563           0 :         error = xfs_parent_lookup(sc->tp, sc->ip, &pp->pptr,
     564             :                         &pp->pptr_scratch);
     565           0 :         if (error == -ENOATTR) {
     566             :                 /* Parent pointer went away, nothing to revalidate. */
     567           0 :                 return -ENOENT;
     568             :         }
     569             : 
     570             :         return error;
     571             : }
     572             : 
     573             : /*
     574             :  * Check a parent pointer the slow way, which means we cycle locks a bunch
     575             :  * and put up with revalidation until we get it done.
     576             :  */
     577             : STATIC int
     578           0 : xchk_parent_slow_pptr(
     579             :         struct xchk_pptrs       *pp,
     580             :         struct xchk_pptr        *pptr)
     581             : {
     582           0 :         struct xfs_scrub        *sc = pp->sc;
     583           0 :         struct xfs_inode        *dp = NULL;
     584           0 :         unsigned int            lockmode;
     585           0 :         int                     error;
     586             : 
     587             :         /* Restore the saved parent pointer into the irec. */
     588           0 :         pp->pptr.p_ino = pptr->p_ino;
     589           0 :         pp->pptr.p_gen = pptr->p_gen;
     590             : 
     591           0 :         error = xfblob_load(pp->pptr_names, pptr->name_cookie, pp->pptr.p_name,
     592           0 :                         pptr->namelen);
     593           0 :         if (error)
     594             :                 return error;
     595           0 :         pp->pptr.p_name[MAXNAMELEN - 1] = 0;
     596           0 :         pp->pptr.p_namelen = pptr->namelen;
     597           0 :         xfs_parent_irec_hashname(sc->mp, &pp->pptr);
     598             : 
     599             :         /* Check that the deferred parent pointer still exists. */
     600           0 :         if (pp->need_revalidate) {
     601           0 :                 error = xchk_parent_revalidate_pptr(pp);
     602           0 :                 if (error == -ENOENT)
     603             :                         return 0;
     604           0 :                 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,
     605             :                                         &error))
     606           0 :                         return error;
     607             :         }
     608             : 
     609           0 :         error = xchk_parent_iget(pp, &dp);
     610           0 :         if (error)
     611             :                 return error;
     612           0 :         if (!dp)
     613             :                 return 0;
     614             : 
     615             :         /*
     616             :          * If we can grab both IOLOCK and ILOCK of the alleged parent, we
     617             :          * can proceed with the validation.
     618             :          */
     619           0 :         lockmode = xchk_parent_lock_dir(sc, dp);
     620           0 :         if (lockmode)
     621           0 :                 goto check_dirent;
     622             : 
     623             :         /*
     624             :          * We couldn't lock the parent dir.  Drop all the locks and try to
     625             :          * get them again, one at a time.
     626             :          */
     627           0 :         xchk_iunlock(sc, sc->ilock_flags);
     628           0 :         pp->need_revalidate = true;
     629             : 
     630           0 :         trace_xchk_parent_slowpath(sc->ip, pp->pptr.p_name, pptr->namelen,
     631           0 :                         dp->i_ino);
     632             : 
     633           0 :         while (true) {
     634           0 :                 xchk_ilock(sc, XFS_IOLOCK_EXCL);
     635           0 :                 if (xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
     636           0 :                         xchk_ilock(sc, XFS_ILOCK_EXCL);
     637           0 :                         if (xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {
     638             :                                 break;
     639             :                         }
     640           0 :                         xchk_iunlock(sc, XFS_ILOCK_EXCL);
     641             :                 }
     642           0 :                 xchk_iunlock(sc, XFS_IOLOCK_EXCL);
     643             : 
     644           0 :                 if (xchk_should_terminate(sc, &error))
     645           0 :                         goto out_rele;
     646             : 
     647           0 :                 delay(1);
     648             :         }
     649           0 :         lockmode = XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
     650             : 
     651             :         /* Revalidate the parent pointer now that we cycled locks. */
     652           0 :         error = xchk_parent_revalidate_pptr(pp);
     653           0 :         if (error == -ENOENT)
     654           0 :                 goto out_unlock;
     655           0 :         if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
     656           0 :                 goto out_unlock;
     657             : 
     658           0 : check_dirent:
     659           0 :         error = xchk_parent_dirent(pp, dp);
     660           0 : out_unlock:
     661           0 :         xfs_iunlock(dp, lockmode);
     662           0 : out_rele:
     663           0 :         xchk_irele(sc, dp);
     664           0 :         return error;
     665             : }
     666             : 
     667             : /* Check all the parent pointers that we deferred the first time around. */
     668             : STATIC int
     669    99904809 : xchk_parent_finish_slow_pptrs(
     670             :         struct xchk_pptrs       *pp)
     671             : {
     672    99904809 :         xfarray_idx_t           array_cur;
     673    99904809 :         int                     error;
     674             : 
     675    99904809 :         foreach_xfarray_idx(pp->pptr_entries, array_cur) {
     676           0 :                 struct xchk_pptr        pptr;
     677             : 
     678           0 :                 if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     679           0 :                         return 0;
     680             : 
     681           0 :                 error = xfarray_load(pp->pptr_entries, array_cur, &pptr);
     682           0 :                 if (error)
     683           0 :                         return error;
     684             : 
     685           0 :                 error = xchk_parent_slow_pptr(pp, &pptr);
     686           0 :                 if (error)
     687           0 :                         return error;
     688             :         }
     689             : 
     690             :         /* Empty out both xfiles now that we've checked everything. */
     691    99902574 :         xfarray_truncate(pp->pptr_entries);
     692    99902466 :         xfblob_truncate(pp->pptr_names);
     693    99902466 :         return 0;
     694             : }
     695             : 
     696             : /* Count the number of parent pointers. */
     697             : STATIC int
     698           0 : xchk_parent_count_pptr(
     699             :         struct xfs_scrub        *sc,
     700             :         struct xfs_inode        *ip,
     701             :         unsigned int            attr_flags,
     702             :         const unsigned char     *name,
     703             :         unsigned int            namelen,
     704             :         const void              *value,
     705             :         unsigned int            valuelen,
     706             :         void                    *priv)
     707             : {
     708           0 :         struct xchk_pptrs       *pp = priv;
     709             : 
     710           0 :         if (attr_flags & XFS_ATTR_PARENT)
     711           0 :                 pp->pptrs_found++;
     712           0 :         return 0;
     713             : }
     714             : 
     715             : /*
     716             :  * Compare the number of parent pointers to the link count.  For
     717             :  * non-directories these should be the same.  For unlinked directories the
     718             :  * count should be zero; for linked directories, it should be nonzero.
     719             :  */
     720             : STATIC int
     721    99904296 : xchk_parent_count_pptrs(
     722             :         struct xchk_pptrs       *pp)
     723             : {
     724    99904296 :         struct xfs_scrub        *sc = pp->sc;
     725    99904296 :         int                     error;
     726             : 
     727             :         /*
     728             :          * If we cycled the ILOCK while cross-checking parent pointers with
     729             :          * dirents, then we need to recalculate the number of parent pointers.
     730             :          */
     731    99904296 :         if (pp->need_revalidate) {
     732           0 :                 pp->pptrs_found = 0;
     733           0 :                 error = xchk_xattr_walk(sc, sc->ip, xchk_parent_count_pptr,
     734             :                                 NULL, pp);
     735           0 :                 if (error == -ECANCELED)
     736             :                         return 0;
     737           0 :                 if (error)
     738             :                         return error;
     739             :         }
     740             : 
     741    99904296 :         if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {
     742    14330973 :                 if (sc->ip == sc->mp->m_rootip)
     743       40028 :                         pp->pptrs_found++;
     744             : 
     745    14330973 :                 if (VFS_I(sc->ip)->i_nlink == 0 && pp->pptrs_found > 0)
     746           0 :                         xchk_ino_set_corrupt(sc, sc->ip->i_ino);
     747    14330973 :                 else if (VFS_I(sc->ip)->i_nlink > 0 &&
     748    14330973 :                          pp->pptrs_found == 0)
     749           0 :                         xchk_ino_set_corrupt(sc, sc->ip->i_ino);
     750             :         } else {
     751    85573323 :                 if (VFS_I(sc->ip)->i_nlink != pp->pptrs_found)
     752           0 :                         xchk_ino_set_corrupt(sc, sc->ip->i_ino);
     753             :         }
     754             : 
     755             :         return 0;
     756             : }
     757             : 
     758             : /* Check parent pointers of a file. */
     759             : STATIC int
     760    99906840 : xchk_parent_pptr(
     761             :         struct xfs_scrub        *sc)
     762             : {
     763    99906840 :         struct xchk_pptrs       *pp;
     764    99906840 :         char                    *descr;
     765    99906840 :         int                     error;
     766             : 
     767    99906840 :         pp = kvzalloc(sizeof(struct xchk_pptrs), XCHK_GFP_FLAGS);
     768    99907224 :         if (!pp)
     769             :                 return -ENOMEM;
     770    99907224 :         pp->sc = sc;
     771             : 
     772             :         /*
     773             :          * Set up some staging memory for parent pointers that we can't check
     774             :          * due to locking contention.
     775             :          */
     776    99907224 :         descr = xchk_xfile_ino_descr(sc, "slow parent pointer entries");
     777    99904403 :         error = xfarray_create(descr, 0, sizeof(struct xchk_pptr),
     778             :                         &pp->pptr_entries);
     779    99903663 :         kfree(descr);
     780    99907326 :         if (error)
     781           0 :                 goto out_pp;
     782             : 
     783    99907326 :         descr = xchk_xfile_ino_descr(sc, "slow parent pointer names");
     784    99901379 :         error = xfblob_create(descr, &pp->pptr_names);
     785    99908602 :         kfree(descr);
     786    99908231 :         if (error)
     787           0 :                 goto out_entries;
     788             : 
     789    99908231 :         error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, NULL, pp);
     790    99906342 :         if (error == -ECANCELED) {
     791           0 :                 error = 0;
     792           0 :                 goto out_names;
     793             :         }
     794    99906342 :         if (error)
     795           0 :                 goto out_names;
     796             : 
     797    99906342 :         error = xchk_parent_finish_slow_pptrs(pp);
     798    99906138 :         if (error)
     799           0 :                 goto out_names;
     800             : 
     801    99906138 :         if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     802           0 :                 goto out_names;
     803             : 
     804             :         /*
     805             :          * For subdirectories, make sure the dotdot entry references the same
     806             :          * inode as the parent pointers.
     807             :          *
     808             :          * If we're scanning a /consistent/ directory, there should only be
     809             :          * one parent pointer, and it should point to the same directory as
     810             :          * the dotdot entry.
     811             :          *
     812             :          * However, a corrupt directory tree might feature a subdirectory with
     813             :          * multiple parents.  The directory loop scanner is responsible for
     814             :          * correcting that kind of problem, so for now we only validate that
     815             :          * the dotdot entry matches /one/ of the parents.
     816             :          */
     817    99906138 :         if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {
     818    14331100 :                 error = xchk_parent_pptr_and_dotdot(pp);
     819    14331024 :                 if (error)
     820           0 :                         goto out_names;
     821             :         }
     822             : 
     823             :         /*
     824             :          * If the parent pointers aren't corrupt, complain if the number of
     825             :          * parent pointers doesn't match the link count.
     826             :          */
     827    99906062 :         error = xchk_parent_count_pptrs(pp);
     828    99903769 :         if (error)
     829           0 :                 goto out_names;
     830             : 
     831    99903769 : out_names:
     832    99903769 :         xfblob_destroy(pp->pptr_names);
     833    99905914 : out_entries:
     834    99905914 :         xfarray_destroy(pp->pptr_entries);
     835    99907428 : out_pp:
     836    99907428 :         kvfree(pp);
     837    99907428 :         return error;
     838             : }
     839             : 
     840             : /* Scrub a parent pointer. */
     841             : int
     842    99916519 : xchk_parent(
     843             :         struct xfs_scrub        *sc)
     844             : {
     845    99916519 :         struct xfs_mount        *mp = sc->mp;
     846    99916519 :         xfs_ino_t               parent_ino;
     847    99916519 :         int                     error = 0;
     848             : 
     849    99916519 :         if (xfs_has_parent(mp))
     850    99907005 :                 return xchk_parent_pptr(sc);
     851             : 
     852             :         /*
     853             :          * If we're a directory, check that the '..' link points up to
     854             :          * a directory that has one entry pointing to us.
     855             :          */
     856        9514 :         if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
     857             :                 return -ENOENT;
     858             : 
     859             :         /* We're not a special inode, are we? */
     860        1318 :         if (!xfs_verify_dir_ino(mp, sc->ip->i_ino)) {
     861           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     862           0 :                 return 0;
     863             :         }
     864             : 
     865        1318 :         do {
     866        1318 :                 if (xchk_should_terminate(sc, &error))
     867             :                         break;
     868             : 
     869             :                 /* Look up '..' */
     870        1318 :                 error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,
     871             :                                 &parent_ino);
     872        1318 :                 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
     873           0 :                         return error;
     874        1318 :                 if (!xfs_verify_dir_ino(mp, parent_ino)) {
     875           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
     876           0 :                         return 0;
     877             :                 }
     878             : 
     879             :                 /*
     880             :                  * Check that the dotdot entry points to a parent directory
     881             :                  * containing a dirent pointing to this subdirectory.
     882             :                  */
     883        1318 :                 error = xchk_parent_validate(sc, parent_ino);
     884        1318 :         } while (error == -EAGAIN);
     885             : 
     886        1318 :         return error;
     887             : }

Generated by: LCOV version 1.14