LCOV - code coverage report
Current view: top level - fs/xfs/scrub - dir.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-djwx @ Mon Jul 31 20:08:22 PDT 2023 Lines: 325 406 80.0 %
Date: 2023-07-31 20:08:22 Functions: 11 11 100.0 %

          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 "scrub/scrub.h"
      19             : #include "scrub/common.h"
      20             : #include "scrub/dabtree.h"
      21             : #include "scrub/readdir.h"
      22             : 
      23             : /* Set us up to scrub directories. */
      24             : int
      25    89510773 : xchk_setup_directory(
      26             :         struct xfs_scrub        *sc)
      27             : {
      28    89510773 :         return xchk_setup_inode_contents(sc, 0);
      29             : }
      30             : 
      31             : /* Directories */
      32             : 
      33             : /* Scrub a directory entry. */
      34             : 
      35             : /* Check that an inode's mode matches a given XFS_DIR3_FT_* type. */
      36             : STATIC void
      37   615484398 : xchk_dir_check_ftype(
      38             :         struct xfs_scrub        *sc,
      39             :         xfs_fileoff_t           offset,
      40             :         struct xfs_inode        *ip,
      41             :         int                     ftype)
      42             : {
      43   615484398 :         struct xfs_mount        *mp = sc->mp;
      44             : 
      45   615484398 :         if (!xfs_has_ftype(mp)) {
      46           0 :                 if (ftype != XFS_DIR3_FT_UNKNOWN && ftype != XFS_DIR3_FT_DIR)
      47           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
      48           0 :                 return;
      49             :         }
      50             : 
      51   615484398 :         if (xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype)
      52           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
      53             : }
      54             : 
      55             : /*
      56             :  * Scrub a single directory entry.
      57             :  *
      58             :  * Check the inode number to make sure it's sane, then we check that we can
      59             :  * look up this filename.  Finally, we check the ftype.
      60             :  */
      61             : STATIC int
      62   614838823 : xchk_dir_actor(
      63             :         struct xfs_scrub        *sc,
      64             :         struct xfs_inode        *dp,
      65             :         xfs_dir2_dataptr_t      dapos,
      66             :         const struct xfs_name   *name,
      67             :         xfs_ino_t               ino,
      68             :         void                    *priv)
      69             : {
      70   614838823 :         struct xfs_mount        *mp = dp->i_mount;
      71   614838823 :         struct xfs_inode        *ip;
      72   614838823 :         xfs_ino_t               lookup_ino;
      73   614838823 :         xfs_dablk_t             offset;
      74   614838823 :         int                     error = 0;
      75             : 
      76   614838823 :         offset = xfs_dir2_db_to_da(mp->m_dir_geo,
      77             :                         xfs_dir2_dataptr_to_db(mp->m_dir_geo, dapos));
      78             : 
      79   614947469 :         if (xchk_should_terminate(sc, &error))
      80          27 :                 return error;
      81             : 
      82             :         /* Does this inode number make sense? */
      83   615003135 :         if (!xfs_verify_dir_ino(mp, ino)) {
      84           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
      85           0 :                 return -ECANCELED;
      86             :         }
      87             : 
      88             :         /* Does this name make sense? */
      89   614463048 :         if (!xfs_dir2_namecheck(name->name, name->len)) {
      90          11 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
      91          11 :                 return -ECANCELED;
      92             :         }
      93             : 
      94   614366478 :         if (!strncmp(".", name->name, name->len)) {
      95             :                 /* If this is "." then check that the inum matches the dir. */
      96    89442506 :                 if (ino != dp->i_ino)
      97           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
      98   524923972 :         } else if (!strncmp("..", name->name, name->len)) {
      99             :                 /*
     100             :                  * If this is ".." in the root inode, check that the inum
     101             :                  * matches this dir.
     102             :                  */
     103    89502840 :                 if (dp->i_ino == mp->m_sb.sb_rootino && ino != dp->i_ino)
     104           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     105             :         }
     106             : 
     107             :         /* Verify that we can look up this name by hash. */
     108   614366478 :         error = xchk_dir_lookup(sc, dp, name, &lookup_ino);
     109             :         /* ENOENT means the hash lookup failed and the dir is corrupt */
     110   615135554 :         if (error == -ENOENT)
     111           0 :                 error = -EFSCORRUPTED;
     112   615135554 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error))
     113           0 :                 goto out;
     114   614411353 :         if (lookup_ino != ino) {
     115           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
     116           0 :                 return -ECANCELED;
     117             :         }
     118             : 
     119             :         /*
     120             :          * Grab the inode pointed to by the dirent.  We release the inode
     121             :          * before we cancel the scrub transaction.
     122             :          *
     123             :          * If _iget returns -EINVAL or -ENOENT then the child inode number is
     124             :          * garbage and the directory is corrupt.  If the _iget returns
     125             :          * -EFSCORRUPTED or -EFSBADCRC then the child is corrupt which is a
     126             :          *  cross referencing error.  Any other error is an operational error.
     127             :          */
     128   614411353 :         error = xchk_iget(sc, ino, &ip);
     129   615382711 :         if (error == -EINVAL || error == -ENOENT) {
     130           0 :                 error = -EFSCORRUPTED;
     131           0 :                 xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
     132           0 :                 goto out;
     133             :         }
     134   615382711 :         if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, offset, &error))
     135           0 :                 goto out;
     136             : 
     137   615494170 :         xchk_dir_check_ftype(sc, offset, ip, name->type);
     138   614789351 :         xchk_irele(sc, ip);
     139   615237096 : out:
     140   615237096 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     141             :                 return -ECANCELED;
     142   615237096 :         return error;
     143             : }
     144             : 
     145             : /* Scrub a directory btree record. */
     146             : STATIC int
     147   136734726 : xchk_dir_rec(
     148             :         struct xchk_da_btree            *ds,
     149             :         int                             level)
     150             : {
     151   136734726 :         struct xfs_name                 dname = { };
     152   136734726 :         struct xfs_da_state_blk         *blk = &ds->state->path.blk[level];
     153   136720857 :         struct xfs_mount                *mp = ds->state->mp;
     154   136720857 :         struct xfs_inode                *dp = ds->dargs.dp;
     155   136720857 :         struct xfs_da_geometry          *geo = mp->m_dir_geo;
     156   136720857 :         struct xfs_dir2_data_entry      *dent;
     157   136720857 :         struct xfs_buf                  *bp;
     158   136720857 :         struct xfs_dir2_leaf_entry      *ent;
     159   136720857 :         unsigned int                    end;
     160   136720857 :         unsigned int                    iter_off;
     161   136720857 :         xfs_ino_t                       ino;
     162   136720857 :         xfs_dablk_t                     rec_bno;
     163   136720857 :         xfs_dir2_db_t                   db;
     164   136720857 :         xfs_dir2_data_aoff_t            off;
     165   136720857 :         xfs_dir2_dataptr_t              ptr;
     166   136720857 :         xfs_dahash_t                    calc_hash;
     167   136720857 :         xfs_dahash_t                    hash;
     168   136720857 :         struct xfs_dir3_icleaf_hdr      hdr;
     169   136720857 :         unsigned int                    tag;
     170   136720857 :         int                             error;
     171             : 
     172   136720857 :         ASSERT(blk->magic == XFS_DIR2_LEAF1_MAGIC ||
     173             :                blk->magic == XFS_DIR2_LEAFN_MAGIC);
     174             : 
     175   136720857 :         xfs_dir2_leaf_hdr_from_disk(mp, &hdr, blk->bp->b_addr);
     176   136521049 :         ent = hdr.ents + blk->index;
     177             : 
     178             :         /* Check the hash of the entry. */
     179   136521049 :         error = xchk_da_btree_hash(ds, level, &ent->hashval);
     180   136518696 :         if (error)
     181           0 :                 goto out;
     182             : 
     183             :         /* Valid hash pointer? */
     184   136518696 :         ptr = be32_to_cpu(ent->address);
     185   136518696 :         if (ptr == 0)
     186             :                 return 0;
     187             : 
     188             :         /* Find the directory entry's location. */
     189   117138347 :         db = xfs_dir2_dataptr_to_db(geo, ptr);
     190   117121040 :         off = xfs_dir2_dataptr_to_off(geo, ptr);
     191   117121040 :         rec_bno = xfs_dir2_db_to_da(geo, db);
     192             : 
     193   117122035 :         if (rec_bno >= geo->leafblk) {
     194           0 :                 xchk_da_set_corrupt(ds, level);
     195           0 :                 goto out;
     196             :         }
     197   117122035 :         error = xfs_dir3_data_read(ds->dargs.trans, dp, rec_bno,
     198             :                         XFS_DABUF_MAP_HOLE_OK, &bp);
     199   116720714 :         if (!xchk_fblock_process_error(ds->sc, XFS_DATA_FORK, rec_bno,
     200             :                         &error))
     201           0 :                 goto out;
     202   116545591 :         if (!bp) {
     203           0 :                 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
     204           0 :                 goto out;
     205             :         }
     206   116545591 :         xchk_buffer_recheck(ds->sc, bp);
     207             : 
     208   117105835 :         if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     209           0 :                 goto out_relse;
     210             : 
     211   117105835 :         dent = bp->b_addr + off;
     212             : 
     213             :         /* Make sure we got a real directory entry. */
     214   117105835 :         iter_off = geo->data_entry_offset;
     215   117105835 :         end = xfs_dir3_data_end_offset(geo, bp->b_addr);
     216   117349359 :         if (!end) {
     217           0 :                 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
     218           0 :                 goto out_relse;
     219             :         }
     220 12047624670 :         for (;;) {
     221 12047624670 :                 struct xfs_dir2_data_entry      *dep = bp->b_addr + iter_off;
     222 12047624670 :                 struct xfs_dir2_data_unused     *dup = bp->b_addr + iter_off;
     223             : 
     224 12047624670 :                 if (iter_off >= end) {
     225           0 :                         xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
     226           0 :                         goto out_relse;
     227             :                 }
     228             : 
     229 12047624670 :                 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
     230   470971356 :                         iter_off += be16_to_cpu(dup->length);
     231   470971356 :                         continue;
     232             :                 }
     233 11576653314 :                 if (dep == dent)
     234             :                         break;
     235 22915901010 :                 iter_off += xfs_dir2_data_entsize(mp, dep->namelen);
     236             :         }
     237             : 
     238             :         /* Retrieve the entry, sanity check it, and compare hashes. */
     239   117349359 :         ino = be64_to_cpu(dent->inumber);
     240   117349359 :         hash = be32_to_cpu(ent->hashval);
     241   117349359 :         tag = be16_to_cpup(xfs_dir2_data_entry_tag_p(mp, dent));
     242   117287128 :         if (!xfs_verify_dir_ino(mp, ino) || tag != off)
     243         293 :                 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
     244   117204323 :         if (dent->namelen == 0) {
     245           0 :                 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
     246           0 :                 goto out_relse;
     247             :         }
     248             : 
     249             :         /* Does the directory hash match? */
     250   117204323 :         dname.name = dent->name;
     251   117204323 :         dname.len = dent->namelen;
     252   117204323 :         calc_hash = xfs_dir2_hashname(mp, &dname);
     253   117219392 :         if (calc_hash != hash)
     254           0 :                 xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
     255             : 
     256   117219392 : out_relse:
     257   117219392 :         xfs_trans_brelse(ds->dargs.trans, bp);
     258   117244454 : out:
     259   117244454 :         return error;
     260             : }
     261             : 
     262             : /*
     263             :  * Is this unused entry either in the bestfree or smaller than all of
     264             :  * them?  We've already checked that the bestfrees are sorted longest to
     265             :  * shortest, and that there aren't any bogus entries.
     266             :  */
     267             : STATIC void
     268     8076939 : xchk_directory_check_free_entry(
     269             :         struct xfs_scrub                *sc,
     270             :         xfs_dablk_t                     lblk,
     271             :         struct xfs_dir2_data_free       *bf,
     272             :         struct xfs_dir2_data_unused     *dup)
     273             : {
     274     8076939 :         struct xfs_dir2_data_free       *dfp;
     275     8076939 :         unsigned int                    dup_length;
     276             : 
     277     8076939 :         dup_length = be16_to_cpu(dup->length);
     278             : 
     279             :         /* Unused entry is shorter than any of the bestfrees */
     280     8076939 :         if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length))
     281             :                 return;
     282             : 
     283     6521962 :         for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--)
     284     6521962 :                 if (dup_length == be16_to_cpu(dfp->length))
     285             :                         return;
     286             : 
     287             :         /* Unused entry should be in the bestfrees but wasn't found. */
     288           0 :         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     289             : }
     290             : 
     291             : /* Check free space info in a directory data block. */
     292             : STATIC int
     293      930499 : xchk_directory_data_bestfree(
     294             :         struct xfs_scrub                *sc,
     295             :         xfs_dablk_t                     lblk,
     296             :         bool                            is_block)
     297             : {
     298      930499 :         struct xfs_dir2_data_unused     *dup;
     299      930499 :         struct xfs_dir2_data_free       *dfp;
     300      930499 :         struct xfs_buf                  *bp;
     301      930499 :         struct xfs_dir2_data_free       *bf;
     302      930499 :         struct xfs_mount                *mp = sc->mp;
     303      930499 :         u16                             tag;
     304      930499 :         unsigned int                    nr_bestfrees = 0;
     305      930499 :         unsigned int                    nr_frees = 0;
     306      930499 :         unsigned int                    smallest_bestfree;
     307      930499 :         int                             newlen;
     308      930499 :         unsigned int                    offset;
     309      930499 :         unsigned int                    end;
     310      930499 :         int                             error;
     311             : 
     312      930499 :         if (is_block) {
     313             :                 /* dir block format */
     314      192445 :                 if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET))
     315           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     316      192445 :                 error = xfs_dir3_block_read(sc->tp, sc->ip, &bp);
     317             :         } else {
     318             :                 /* dir data format */
     319      738054 :                 error = xfs_dir3_data_read(sc->tp, sc->ip, lblk, 0, &bp);
     320             :         }
     321      930498 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
     322           0 :                 goto out;
     323      930495 :         xchk_buffer_recheck(sc, bp);
     324             : 
     325             :         /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */
     326             : 
     327      930508 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     328           0 :                 goto out_buf;
     329             : 
     330             :         /* Do the bestfrees correspond to actual free space? */
     331      930508 :         bf = xfs_dir2_data_bestfree_p(mp, bp->b_addr);
     332      930508 :         smallest_bestfree = UINT_MAX;
     333     4652544 :         for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
     334     2791527 :                 offset = be16_to_cpu(dfp->offset);
     335     2791527 :                 if (offset == 0)
     336     1205706 :                         continue;
     337     1585821 :                 if (offset >= mp->m_dir_geo->blksize) {
     338           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     339           0 :                         goto out_buf;
     340             :                 }
     341     1585821 :                 dup = bp->b_addr + offset;
     342     1585821 :                 tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
     343             : 
     344             :                 /* bestfree doesn't match the entry it points at? */
     345     1585821 :                 if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) ||
     346     1585821 :                     be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) ||
     347             :                     tag != offset) {
     348           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     349           0 :                         goto out_buf;
     350             :                 }
     351             : 
     352             :                 /* bestfree records should be ordered largest to smallest */
     353     1585821 :                 if (smallest_bestfree < be16_to_cpu(dfp->length)) {
     354           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     355           0 :                         goto out_buf;
     356             :                 }
     357             : 
     358     1585821 :                 smallest_bestfree = be16_to_cpu(dfp->length);
     359     1585821 :                 nr_bestfrees++;
     360             :         }
     361             : 
     362             :         /* Make sure the bestfrees are actually the best free spaces. */
     363      930509 :         offset = mp->m_dir_geo->data_entry_offset;
     364      930509 :         end = xfs_dir3_data_end_offset(mp->m_dir_geo, bp->b_addr);
     365             : 
     366             :         /* Iterate the entries, stopping when we hit or go past the end. */
     367   131042174 :         while (offset < end) {
     368   130111658 :                 dup = bp->b_addr + offset;
     369             : 
     370             :                 /* Skip real entries */
     371   130111658 :                 if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) {
     372   122034756 :                         struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
     373             : 
     374   122034756 :                         newlen = xfs_dir2_data_entsize(mp, dep->namelen);
     375   122034756 :                         if (newlen <= 0) {
     376             :                                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     377             :                                                 lblk);
     378             :                                 goto out_buf;
     379             :                         }
     380   122034756 :                         offset += newlen;
     381   122034756 :                         continue;
     382             :                 }
     383             : 
     384             :                 /* Spot check this free entry */
     385     8076902 :                 tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
     386     8076902 :                 if (tag != offset) {
     387           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     388           0 :                         goto out_buf;
     389             :                 }
     390             : 
     391             :                 /*
     392             :                  * Either this entry is a bestfree or it's smaller than
     393             :                  * any of the bestfrees.
     394             :                  */
     395     8076902 :                 xchk_directory_check_free_entry(sc, lblk, bf, dup);
     396     8076909 :                 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     397           0 :                         goto out_buf;
     398             : 
     399             :                 /* Move on. */
     400     8076909 :                 newlen = be16_to_cpu(dup->length);
     401     8076909 :                 if (newlen <= 0) {
     402           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     403           0 :                         goto out_buf;
     404             :                 }
     405     8076909 :                 offset += newlen;
     406     8076909 :                 if (offset <= end)
     407     8076912 :                         nr_frees++;
     408             :         }
     409             : 
     410             :         /* We're required to fill all the space. */
     411      930516 :         if (offset != end)
     412           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     413             : 
     414             :         /* Did we see at least as many free slots as there are bestfrees? */
     415      930516 :         if (nr_frees < nr_bestfrees)
     416           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     417      930516 : out_buf:
     418      930516 :         xfs_trans_brelse(sc->tp, bp);
     419      930510 : out:
     420      930510 :         return error;
     421             : }
     422             : 
     423             : /*
     424             :  * Does the free space length in the free space index block ($len) match
     425             :  * the longest length in the directory data block's bestfree array?
     426             :  * Assume that we've already checked that the data block's bestfree
     427             :  * array is in order.
     428             :  */
     429             : STATIC void
     430      738065 : xchk_directory_check_freesp(
     431             :         struct xfs_scrub                *sc,
     432             :         xfs_dablk_t                     lblk,
     433             :         struct xfs_buf                  *dbp,
     434             :         unsigned int                    len)
     435             : {
     436      738065 :         struct xfs_dir2_data_free       *dfp;
     437             : 
     438      738065 :         dfp = xfs_dir2_data_bestfree_p(sc->mp, dbp->b_addr);
     439             : 
     440      738065 :         if (len != be16_to_cpu(dfp->length))
     441           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     442             : 
     443      738065 :         if (len > 0 && be16_to_cpu(dfp->offset) == 0)
     444           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     445      738065 : }
     446             : 
     447             : /* Check free space info in a directory leaf1 block. */
     448             : STATIC int
     449       29750 : xchk_directory_leaf1_bestfree(
     450             :         struct xfs_scrub                *sc,
     451             :         struct xfs_da_args              *args,
     452             :         xfs_dir2_db_t                   last_data_db,
     453             :         xfs_dablk_t                     lblk)
     454             : {
     455       29750 :         struct xfs_dir3_icleaf_hdr      leafhdr;
     456       29750 :         struct xfs_dir2_leaf_tail       *ltp;
     457       29750 :         struct xfs_dir2_leaf            *leaf;
     458       29750 :         struct xfs_buf                  *dbp;
     459       29750 :         struct xfs_buf                  *bp;
     460       29750 :         struct xfs_da_geometry          *geo = sc->mp->m_dir_geo;
     461       29750 :         __be16                          *bestp;
     462       29750 :         __u16                           best;
     463       29750 :         __u32                           hash;
     464       29750 :         __u32                           lasthash = 0;
     465       29750 :         __u32                           bestcount;
     466       29750 :         unsigned int                    stale = 0;
     467       29750 :         int                             i;
     468       29750 :         int                             error;
     469             : 
     470             :         /* Read the free space block. */
     471       29750 :         error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, &bp);
     472       29750 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
     473           0 :                 return error;
     474       29750 :         xchk_buffer_recheck(sc, bp);
     475             : 
     476       29750 :         leaf = bp->b_addr;
     477       29750 :         xfs_dir2_leaf_hdr_from_disk(sc->ip->i_mount, &leafhdr, leaf);
     478       29750 :         ltp = xfs_dir2_leaf_tail_p(geo, leaf);
     479       29750 :         bestcount = be32_to_cpu(ltp->bestcount);
     480       29750 :         bestp = xfs_dir2_leaf_bests_p(ltp);
     481             : 
     482       29750 :         if (xfs_has_crc(sc->mp)) {
     483       29750 :                 struct xfs_dir3_leaf_hdr        *hdr3 = bp->b_addr;
     484             : 
     485       29750 :                 if (hdr3->pad != cpu_to_be32(0))
     486           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     487             :         }
     488             : 
     489             :         /*
     490             :          * There must be enough bestfree slots to cover all the directory data
     491             :          * blocks that we scanned.  It is possible for there to be a hole
     492             :          * between the last data block and i_disk_size.  This seems like an
     493             :          * oversight to the scrub author, but as we have been writing out
     494             :          * directories like this (and xfs_repair doesn't mind them) for years,
     495             :          * that's what we have to check.
     496             :          */
     497       29750 :         if (bestcount != last_data_db + 1) {
     498           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     499           0 :                 goto out;
     500             :         }
     501             : 
     502             :         /* Is the leaf count even remotely sane? */
     503       29750 :         if (leafhdr.count > geo->leaf_max_ents) {
     504           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     505           0 :                 goto out;
     506             :         }
     507             : 
     508             :         /* Leaves and bests don't overlap in leaf format. */
     509       29750 :         if ((char *)&leafhdr.ents[leafhdr.count] > (char *)bestp) {
     510           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     511           0 :                 goto out;
     512             :         }
     513             : 
     514             :         /* Check hash value order, count stale entries.  */
     515     7895046 :         for (i = 0; i < leafhdr.count; i++) {
     516     7865296 :                 hash = be32_to_cpu(leafhdr.ents[i].hashval);
     517     7865296 :                 if (i > 0 && lasthash > hash)
     518           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     519     7865296 :                 lasthash = hash;
     520     7865296 :                 if (leafhdr.ents[i].address ==
     521             :                     cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
     522     1379654 :                         stale++;
     523             :         }
     524       29750 :         if (leafhdr.stale != stale)
     525           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     526       29750 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     527           0 :                 goto out;
     528             : 
     529             :         /* Check all the bestfree entries. */
     530       93246 :         for (i = 0; i < bestcount; i++, bestp++) {
     531       63496 :                 best = be16_to_cpu(*bestp);
     532       63496 :                 error = xfs_dir3_data_read(sc->tp, sc->ip,
     533             :                                 xfs_dir2_db_to_da(args->geo, i),
     534             :                                 XFS_DABUF_MAP_HOLE_OK,
     535             :                                 &dbp);
     536       63496 :                 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
     537             :                                 &error))
     538             :                         break;
     539             : 
     540       63496 :                 if (!dbp) {
     541         125 :                         if (best != NULLDATAOFF) {
     542           0 :                                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
     543             :                                                 lblk);
     544           0 :                                 break;
     545             :                         }
     546         125 :                         continue;
     547             :                 }
     548             : 
     549       63371 :                 if (best == NULLDATAOFF)
     550           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     551             :                 else
     552       63371 :                         xchk_directory_check_freesp(sc, lblk, dbp, best);
     553       63371 :                 xfs_trans_brelse(sc->tp, dbp);
     554       63371 :                 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     555             :                         break;
     556             :         }
     557       29750 : out:
     558       29750 :         xfs_trans_brelse(sc->tp, bp);
     559       29750 :         return error;
     560             : }
     561             : 
     562             : /* Check free space info in a directory freespace block. */
     563             : STATIC int
     564       51661 : xchk_directory_free_bestfree(
     565             :         struct xfs_scrub                *sc,
     566             :         struct xfs_da_args              *args,
     567             :         xfs_dablk_t                     lblk)
     568             : {
     569       51661 :         struct xfs_dir3_icfree_hdr      freehdr;
     570       51661 :         struct xfs_buf                  *dbp;
     571       51661 :         struct xfs_buf                  *bp;
     572       51661 :         __u16                           best;
     573       51661 :         unsigned int                    stale = 0;
     574       51661 :         int                             i;
     575       51661 :         int                             error;
     576             : 
     577             :         /* Read the free space block */
     578       51661 :         error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp);
     579       51661 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
     580           0 :                 return error;
     581       51661 :         xchk_buffer_recheck(sc, bp);
     582             : 
     583       51661 :         if (xfs_has_crc(sc->mp)) {
     584       51661 :                 struct xfs_dir3_free_hdr        *hdr3 = bp->b_addr;
     585             : 
     586       51661 :                 if (hdr3->pad != cpu_to_be32(0))
     587           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     588             :         }
     589             : 
     590             :         /* Check all the entries. */
     591       51661 :         xfs_dir2_free_hdr_from_disk(sc->ip->i_mount, &freehdr, bp->b_addr);
     592      796514 :         for (i = 0; i < freehdr.nvalid; i++) {
     593      693192 :                 best = be16_to_cpu(freehdr.bests[i]);
     594      693192 :                 if (best == NULLDATAOFF) {
     595       18497 :                         stale++;
     596       18497 :                         continue;
     597             :                 }
     598     1349388 :                 error = xfs_dir3_data_read(sc->tp, sc->ip,
     599      674695 :                                 (freehdr.firstdb + i) * args->geo->fsbcount,
     600             :                                 0, &dbp);
     601      674693 :                 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
     602             :                                 &error))
     603           0 :                         goto out;
     604      674694 :                 xchk_directory_check_freesp(sc, lblk, dbp, best);
     605      674694 :                 xfs_trans_brelse(sc->tp, dbp);
     606             :         }
     607             : 
     608       51661 :         if (freehdr.nused + stale != freehdr.nvalid)
     609           0 :                 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     610       51661 : out:
     611       51661 :         xfs_trans_brelse(sc->tp, bp);
     612       51661 :         return error;
     613             : }
     614             : 
     615             : /* Check free space information in directories. */
     616             : STATIC int
     617    89519144 : xchk_directory_blocks(
     618             :         struct xfs_scrub        *sc)
     619             : {
     620    89519144 :         struct xfs_bmbt_irec    got;
     621    89519144 :         struct xfs_da_args      args = {
     622    89519144 :                 .dp             = sc ->ip,
     623             :                 .whichfork      = XFS_DATA_FORK,
     624    89519144 :                 .geo            = sc->mp->m_dir_geo,
     625    89519144 :                 .trans          = sc->tp,
     626             :         };
     627    89519144 :         struct xfs_ifork        *ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
     628    89519144 :         struct xfs_mount        *mp = sc->mp;
     629    89519144 :         xfs_fileoff_t           leaf_lblk;
     630    89519144 :         xfs_fileoff_t           free_lblk;
     631    89519144 :         xfs_fileoff_t           lblk;
     632    89519144 :         struct xfs_iext_cursor  icur;
     633    89519144 :         xfs_dablk_t             dabno;
     634    89519144 :         xfs_dir2_db_t           last_data_db = 0;
     635    89519144 :         bool                    found;
     636    89519144 :         bool                    is_block = false;
     637    89519144 :         int                     error;
     638             : 
     639             :         /* Ignore local format directories. */
     640    89519144 :         if (ifp->if_format != XFS_DINODE_FMT_EXTENTS &&
     641             :             ifp->if_format != XFS_DINODE_FMT_BTREE)
     642             :                 return 0;
     643             : 
     644      273834 :         lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET);
     645      273834 :         leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET);
     646      273834 :         free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET);
     647             : 
     648             :         /* Is this a block dir? */
     649      273834 :         error = xfs_dir2_isblock(&args, &is_block);
     650      273829 :         if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))
     651           0 :                 goto out;
     652             : 
     653             :         /* Iterate all the data extents in the directory... */
     654      273825 :         found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
     655     1032131 :         while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
     656             :                 /* No more data blocks... */
     657      839687 :                 if (got.br_startoff >= leaf_lblk)
     658             :                         break;
     659             : 
     660             :                 /*
     661             :                  * Check each data block's bestfree data.
     662             :                  *
     663             :                  * Iterate all the fsbcount-aligned block offsets in
     664             :                  * this directory.  The directory block reading code is
     665             :                  * smart enough to do its own bmap lookups to handle
     666             :                  * discontiguous directory blocks.  When we're done
     667             :                  * with the extent record, re-query the bmap at the
     668             :                  * next fsbcount-aligned offset to avoid redundant
     669             :                  * block checks.
     670             :                  */
     671      758306 :                 for (lblk = roundup((xfs_dablk_t)got.br_startoff,
     672             :                                 args.geo->fsbcount);
     673     1688815 :                      lblk < got.br_startoff + got.br_blockcount;
     674      930509 :                      lblk += args.geo->fsbcount) {
     675      930507 :                         last_data_db = xfs_dir2_da_to_db(args.geo, lblk);
     676      930504 :                         error = xchk_directory_data_bestfree(sc, lblk,
     677             :                                         is_block);
     678      930509 :                         if (error)
     679           0 :                                 goto out;
     680             :                 }
     681      758308 :                 dabno = got.br_startoff + got.br_blockcount;
     682      758308 :                 lblk = roundup(dabno, args.geo->fsbcount);
     683      758308 :                 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
     684             :         }
     685             : 
     686      273829 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     687           0 :                 goto out;
     688             : 
     689             :         /* Look for a leaf1 block, which has free info. */
     690      273829 :         if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &icur, &got) &&
     691       81381 :             got.br_startoff == leaf_lblk &&
     692      162761 :             got.br_blockcount == args.geo->fsbcount &&
     693       81380 :             !xfs_iext_next_extent(ifp, &icur, &got)) {
     694       29750 :                 if (is_block) {
     695           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     696           0 :                         goto out;
     697             :                 }
     698       29750 :                 error = xchk_directory_leaf1_bestfree(sc, &args, last_data_db,
     699             :                                 leaf_lblk);
     700       29750 :                 if (error)
     701           0 :                         goto out;
     702             :         }
     703             : 
     704      273828 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     705           0 :                 goto out;
     706             : 
     707             :         /* Scan for free blocks */
     708      273828 :         lblk = free_lblk;
     709      273828 :         found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
     710      325487 :         while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
     711             :                 /*
     712             :                  * Dirs can't have blocks mapped above 2^32.
     713             :                  * Single-block dirs shouldn't even be here.
     714             :                  */
     715       51661 :                 lblk = got.br_startoff;
     716       51661 :                 if (lblk & ~0xFFFFFFFFULL) {
     717           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     718           0 :                         goto out;
     719             :                 }
     720       51661 :                 if (is_block) {
     721           0 :                         xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
     722           0 :                         goto out;
     723             :                 }
     724             : 
     725             :                 /*
     726             :                  * Check each dir free block's bestfree data.
     727             :                  *
     728             :                  * Iterate all the fsbcount-aligned block offsets in
     729             :                  * this directory.  The directory block reading code is
     730             :                  * smart enough to do its own bmap lookups to handle
     731             :                  * discontiguous directory blocks.  When we're done
     732             :                  * with the extent record, re-query the bmap at the
     733             :                  * next fsbcount-aligned offset to avoid redundant
     734             :                  * block checks.
     735             :                  */
     736       51661 :                 for (lblk = roundup((xfs_dablk_t)got.br_startoff,
     737             :                                 args.geo->fsbcount);
     738      103322 :                      lblk < got.br_startoff + got.br_blockcount;
     739       51661 :                      lblk += args.geo->fsbcount) {
     740       51661 :                         error = xchk_directory_free_bestfree(sc, &args,
     741             :                                         lblk);
     742       51661 :                         if (error)
     743           0 :                                 goto out;
     744             :                 }
     745       51661 :                 dabno = got.br_startoff + got.br_blockcount;
     746       51661 :                 lblk = roundup(dabno, args.geo->fsbcount);
     747       51661 :                 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got);
     748             :         }
     749      273829 : out:
     750      273829 :         return error;
     751             : }
     752             : 
     753             : /* Scrub a whole directory. */
     754             : int
     755    89487882 : xchk_directory(
     756             :         struct xfs_scrub        *sc)
     757             : {
     758    89487882 :         int                     error;
     759             : 
     760    89487882 :         if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
     761             :                 return -ENOENT;
     762             : 
     763             :         /* Plausible size? */
     764    89487882 :         if (sc->ip->i_disk_size < xfs_dir2_sf_hdr_size(0)) {
     765           0 :                 xchk_ino_set_corrupt(sc, sc->ip->i_ino);
     766           0 :                 return 0;
     767             :         }
     768             : 
     769             :         /* Check directory tree structure */
     770    89487882 :         error = xchk_da_btree(sc, XFS_DATA_FORK, xchk_dir_rec, NULL);
     771    89472335 :         if (error)
     772             :                 return error;
     773             : 
     774    89472320 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     775             :                 return 0;
     776             : 
     777             :         /* Check the freespace. */
     778    89473803 :         error = xchk_directory_blocks(sc);
     779    89441756 :         if (error)
     780             :                 return error;
     781             : 
     782    89441756 :         if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
     783             :                 return 0;
     784             : 
     785             :         /* Look up every name in this directory by hash. */
     786    89443205 :         error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, NULL);
     787    89555994 :         if (error == -ECANCELED)
     788          11 :                 error = 0;
     789             :         return error;
     790             : }

Generated by: LCOV version 1.14