LCOV - code coverage report
Current view: top level - fs/xfs/scrub - readdir.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsx @ Mon Jul 31 20:08:34 PDT 2023 Lines: 178 178 100.0 %
Date: 2023-07-31 20:08:34 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2022-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_log_format.h"
      11             : #include "xfs_trans_resv.h"
      12             : #include "xfs_mount.h"
      13             : #include "xfs_inode.h"
      14             : #include "xfs_dir2.h"
      15             : #include "xfs_dir2_priv.h"
      16             : #include "xfs_trace.h"
      17             : #include "xfs_bmap.h"
      18             : #include "xfs_trans.h"
      19             : #include "xfs_error.h"
      20             : #include "scrub/scrub.h"
      21             : #include "scrub/readdir.h"
      22             : 
      23             : /* Call a function for every entry in a shortform directory. */
      24             : STATIC int
      25  5863055192 : xchk_dir_walk_sf(
      26             :         struct xfs_scrub        *sc,
      27             :         struct xfs_inode        *dp,
      28             :         xchk_dirent_fn          dirent_fn,
      29             :         void                    *priv)
      30             : {
      31  5863055192 :         struct xfs_name         name = {
      32             :                 .name           = ".",
      33             :                 .len            = 1,
      34             :                 .type           = XFS_DIR3_FT_DIR,
      35             :         };
      36  5863055192 :         struct xfs_mount        *mp = dp->i_mount;
      37  5863055192 :         struct xfs_da_geometry  *geo = mp->m_dir_geo;
      38  5863055192 :         struct xfs_dir2_sf_entry *sfep;
      39  5863055192 :         struct xfs_dir2_sf_hdr  *sfp;
      40  5863055192 :         xfs_ino_t               ino;
      41  5863055192 :         xfs_dir2_dataptr_t      dapos;
      42  5863055192 :         unsigned int            i;
      43  5863055192 :         int                     error;
      44             : 
      45  5863055192 :         ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
      46  5863055192 :         ASSERT(dp->i_df.if_u1.if_data != NULL);
      47             : 
      48  5863055192 :         sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
      49             : 
      50             :         /* dot entry */
      51  5863055192 :         dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
      52  5863055192 :                         geo->data_entry_offset);
      53             : 
      54  5862752245 :         error = dirent_fn(sc, dp, dapos, &name, dp->i_ino, priv);
      55  5863466662 :         if (error)
      56             :                 return error;
      57             : 
      58             :         /* dotdot entry */
      59  5865199844 :         dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
      60  5865199844 :                         geo->data_entry_offset +
      61             :                         xfs_dir2_data_entsize(mp, sizeof(".") - 1));
      62  5864044288 :         ino = xfs_dir2_sf_get_parent_ino(sfp);
      63  5858793216 :         name.name = "..";
      64  5858793216 :         name.len = 2;
      65             : 
      66  5858793216 :         error = dirent_fn(sc, dp, dapos, &name, ino, priv);
      67  5861582908 :         if (error)
      68             :                 return error;
      69             : 
      70             :         /* iterate everything else */
      71  5861582907 :         sfep = xfs_dir2_sf_firstentry(sfp);
      72 25531239843 :         for (i = 0; i < sfp->count; i++) {
      73 19665141191 :                 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
      74             :                                 xfs_dir2_sf_get_offset(sfep));
      75 19682783193 :                 ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
      76 19673970516 :                 name.name = sfep->name;
      77 19673970516 :                 name.len = sfep->namelen;
      78 19673970516 :                 name.type = xfs_dir2_sf_get_ftype(mp, sfep);
      79             : 
      80 19667861378 :                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
      81 19673681891 :                 if (error)
      82         133 :                         return error;
      83             : 
      84 19673681758 :                 sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
      85             :         }
      86             : 
      87             :         return 0;
      88             : }
      89             : 
      90             : /* Call a function for every entry in a block directory. */
      91             : STATIC int
      92   195423555 : xchk_dir_walk_block(
      93             :         struct xfs_scrub        *sc,
      94             :         struct xfs_inode        *dp,
      95             :         xchk_dirent_fn          dirent_fn,
      96             :         void                    *priv)
      97             : {
      98   195423555 :         struct xfs_mount        *mp = dp->i_mount;
      99   195423555 :         struct xfs_da_geometry  *geo = mp->m_dir_geo;
     100   195423555 :         struct xfs_buf          *bp;
     101   195423555 :         unsigned int            off, next_off, end;
     102   195423555 :         int                     error;
     103             : 
     104   195423555 :         error = xfs_dir3_block_read(sc->tp, dp, dp->i_ino, &bp);
     105   195430483 :         if (error)
     106             :                 return error;
     107             : 
     108             :         /* Walk each directory entry. */
     109   195431193 :         end = xfs_dir3_data_end_offset(geo, bp->b_addr);
     110  5812236114 :         for (off = geo->data_entry_offset; off < end; off = next_off) {
     111  5616804529 :                 struct xfs_name                 name = { };
     112  5616804529 :                 struct xfs_dir2_data_unused     *dup = bp->b_addr + off;
     113  5616804529 :                 struct xfs_dir2_data_entry      *dep = bp->b_addr + off;
     114  5616804529 :                 xfs_ino_t                       ino;
     115  5616804529 :                 xfs_dir2_dataptr_t              dapos;
     116             : 
     117             :                 /* Skip an empty entry. */
     118  5616804529 :                 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
     119   469009790 :                         next_off = off + be16_to_cpu(dup->length);
     120   469009790 :                         continue;
     121             :                 }
     122             : 
     123             :                 /* Otherwise, find the next entry and report it. */
     124  5147794739 :                 next_off = off + xfs_dir2_data_entsize(mp, dep->namelen);
     125  5147794739 :                 if (next_off > end)
     126             :                         break;
     127             : 
     128  5147794739 :                 dapos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, off);
     129  5147718103 :                 ino = be64_to_cpu(dep->inumber);
     130  5147718103 :                 name.name = dep->name;
     131  5147718103 :                 name.len = dep->namelen;
     132  5147718103 :                 name.type = xfs_dir2_data_get_ftype(mp, dep);
     133             : 
     134  5147783254 :                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
     135  5147801490 :                 if (error)
     136             :                         break;
     137             :         }
     138             : 
     139   195431585 :         xfs_trans_brelse(sc->tp, bp);
     140   195431585 :         return error;
     141             : }
     142             : 
     143             : /* Read a leaf-format directory buffer. */
     144             : STATIC int
     145   287254525 : xchk_read_leaf_dir_buf(
     146             :         struct xfs_trans        *tp,
     147             :         struct xfs_inode        *dp,
     148             :         struct xfs_da_geometry  *geo,
     149             :         xfs_dir2_off_t          *curoff,
     150             :         struct xfs_buf          **bpp)
     151             : {
     152   287254525 :         struct xfs_iext_cursor  icur;
     153   287254525 :         struct xfs_bmbt_irec    map;
     154   287254525 :         struct xfs_ifork        *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK);
     155   287254525 :         xfs_dablk_t             last_da;
     156   287254525 :         xfs_dablk_t             map_off;
     157   287254525 :         xfs_dir2_off_t          new_off;
     158             : 
     159   287254525 :         *bpp = NULL;
     160             : 
     161             :         /*
     162             :          * Look for mapped directory blocks at or above the current offset.
     163             :          * Truncate down to the nearest directory block to start the scanning
     164             :          * operation.
     165             :          */
     166   287254525 :         last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
     167   286690535 :         map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *curoff));
     168             : 
     169   286910281 :         if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
     170             :                 return 0;
     171   287339411 :         if (map.br_startoff >= last_da)
     172             :                 return 0;
     173   262004134 :         xfs_trim_extent(&map, map_off, last_da - map_off);
     174             : 
     175             :         /* Read the directory block of that first mapping. */
     176   261898608 :         new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
     177   261790981 :         if (new_off > *curoff)
     178       12740 :                 *curoff = new_off;
     179             : 
     180   261790981 :         return xfs_dir3_data_read(tp, dp, dp->i_ino, map.br_startoff, 0, bpp);
     181             : }
     182             : 
     183             : /* Call a function for every entry in a leaf directory. */
     184             : STATIC int
     185    25368607 : xchk_dir_walk_leaf(
     186             :         struct xfs_scrub        *sc,
     187             :         struct xfs_inode        *dp,
     188             :         xchk_dirent_fn          dirent_fn,
     189             :         void                    *priv)
     190             : {
     191    25368607 :         struct xfs_mount        *mp = dp->i_mount;
     192    25368607 :         struct xfs_da_geometry  *geo = mp->m_dir_geo;
     193    25368607 :         struct xfs_buf          *bp = NULL;
     194    25368607 :         xfs_dir2_off_t          curoff = 0;
     195    25368607 :         unsigned int            offset = 0;
     196    25368607 :         int                     error;
     197             : 
     198             :         /* Iterate every directory offset in this directory. */
     199 32389110408 :         while (curoff < XFS_DIR2_LEAF_OFFSET) {
     200 32389110408 :                 struct xfs_name                 name = { };
     201 32389110408 :                 struct xfs_dir2_data_unused     *dup;
     202 32389110408 :                 struct xfs_dir2_data_entry      *dep;
     203 32389110408 :                 xfs_ino_t                       ino;
     204 32389110408 :                 unsigned int                    length;
     205 32389110408 :                 xfs_dir2_dataptr_t              dapos;
     206             : 
     207             :                 /*
     208             :                  * If we have no buffer, or we're off the end of the
     209             :                  * current buffer, need to get another one.
     210             :                  */
     211 32389110408 :                 if (!bp || offset >= geo->blksize) {
     212   287425502 :                         if (bp) {
     213   262057290 :                                 xfs_trans_brelse(sc->tp, bp);
     214   262078683 :                                 bp = NULL;
     215             :                         }
     216             : 
     217   287446895 :                         error = xchk_read_leaf_dir_buf(sc->tp, dp, geo, &curoff,
     218             :                                         &bp);
     219   287373750 :                         if (error || !bp)
     220             :                                 break;
     221             : 
     222             :                         /*
     223             :                          * Find our position in the block.
     224             :                          */
     225   262004287 :                         offset = geo->data_entry_offset;
     226   262004287 :                         curoff += geo->data_entry_offset;
     227             :                 }
     228             : 
     229             :                 /* Skip an empty entry. */
     230 32363689193 :                 dup = bp->b_addr + offset;
     231 32363689193 :                 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
     232   733697297 :                         length = be16_to_cpu(dup->length);
     233   733697297 :                         offset += length;
     234   733697297 :                         curoff += length;
     235   733697297 :                         continue;
     236             :                 }
     237             : 
     238             :                 /* Otherwise, find the next entry and report it. */
     239 31629991896 :                 dep = bp->b_addr + offset;
     240 31629991896 :                 length = xfs_dir2_data_entsize(mp, dep->namelen);
     241             : 
     242 31629991896 :                 dapos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
     243 31629991896 :                 ino = be64_to_cpu(dep->inumber);
     244 31629991896 :                 name.name = dep->name;
     245 31629991896 :                 name.len = dep->namelen;
     246 31629991896 :                 name.type = xfs_dir2_data_get_ftype(mp, dep);
     247             : 
     248 31651089720 :                 error = dirent_fn(sc, dp, dapos, &name, ino, priv);
     249 31630044511 :                 if (error)
     250             :                         break;
     251             : 
     252             :                 /* Advance to the next entry. */
     253 31630044504 :                 offset += length;
     254 31630044504 :                 curoff += length;
     255             :         }
     256             : 
     257    25369470 :         if (bp)
     258           7 :                 xfs_trans_brelse(sc->tp, bp);
     259    25369470 :         return error;
     260             : }
     261             : 
     262             : /*
     263             :  * Call a function for every entry in a directory.
     264             :  *
     265             :  * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.
     266             :  */
     267             : int
     268  6083919935 : xchk_dir_walk(
     269             :         struct xfs_scrub        *sc,
     270             :         struct xfs_inode        *dp,
     271             :         xchk_dirent_fn          dirent_fn,
     272             :         void                    *priv)
     273             : {
     274  6083919935 :         struct xfs_da_args      args = {
     275             :                 .dp             = dp,
     276  6083919935 :                 .geo            = dp->i_mount->m_dir_geo,
     277  6083919935 :                 .trans          = sc->tp,
     278  6083919935 :                 .owner          = dp->i_ino,
     279             :         };
     280  6083919935 :         bool                    isblock;
     281  6083919935 :         int                     error;
     282             : 
     283 12167839870 :         if (xfs_is_shutdown(dp->i_mount))
     284             :                 return -EIO;
     285             : 
     286  6083919935 :         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
     287  6083919935 :         ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
     288             : 
     289  6085949269 :         if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
     290  5865153687 :                 return xchk_dir_walk_sf(sc, dp, dirent_fn, priv);
     291             : 
     292             :         /* dir2 functions require that the data fork is loaded */
     293   220795582 :         error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
     294   220792010 :         if (error)
     295             :                 return error;
     296             : 
     297   220792305 :         error = xfs_dir2_isblock(&args, &isblock);
     298   220806178 :         if (error)
     299             :                 return error;
     300             : 
     301   220806178 :         if (isblock)
     302   195437305 :                 return xchk_dir_walk_block(sc, dp, dirent_fn, priv);
     303             : 
     304    25368873 :         return xchk_dir_walk_leaf(sc, dp, dirent_fn, priv);
     305             : }
     306             : 
     307             : /*
     308             :  * Look up the inode number for an exact name in a directory.
     309             :  *
     310             :  * Callers must hold the ILOCK.  File types are XFS_DIR3_FT_*.  Names are not
     311             :  * checked for correctness.
     312             :  */
     313             : int
     314   306115048 : xchk_dir_lookup(
     315             :         struct xfs_scrub        *sc,
     316             :         struct xfs_inode        *dp,
     317             :         const struct xfs_name   *name,
     318             :         xfs_ino_t               *ino)
     319             : {
     320   612330601 :         struct xfs_da_args      args = {
     321             :                 .dp             = dp,
     322   306115048 :                 .geo            = dp->i_mount->m_dir_geo,
     323   306215553 :                 .trans          = sc->tp,
     324   306115048 :                 .name           = name->name,
     325   306115048 :                 .namelen        = name->len,
     326   306115048 :                 .filetype       = name->type,
     327   306115048 :                 .hashval        = xfs_dir2_hashname(dp->i_mount, name),
     328             :                 .whichfork      = XFS_DATA_FORK,
     329             :                 .op_flags       = XFS_DA_OP_OKNOENT,
     330   306215553 :                 .owner          = dp->i_ino,
     331             :         };
     332   306215553 :         bool                    isblock, isleaf;
     333   306215553 :         int                     error;
     334             : 
     335   612431106 :         if (xfs_is_shutdown(dp->i_mount))
     336             :                 return -EIO;
     337             : 
     338             :         /*
     339             :          * A temporary directory's block headers are written with the owner
     340             :          * set to sc->ip, so we must switch the owner here for the lookup.
     341             :          */
     342   306215553 :         if (dp == sc->tempip)
     343     2489303 :                 args.owner = sc->ip->i_ino;
     344             : 
     345   306215553 :         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
     346   306215553 :         ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
     347             : 
     348   305802988 :         if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
     349   199472583 :                 error = xfs_dir2_sf_lookup(&args);
     350   199779168 :                 goto out_check_rval;
     351             :         }
     352             : 
     353             :         /* dir2 functions require that the data fork is loaded */
     354   106330405 :         error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
     355   106286869 :         if (error)
     356             :                 return error;
     357             : 
     358   106299959 :         error = xfs_dir2_isblock(&args, &isblock);
     359   106377427 :         if (error)
     360             :                 return error;
     361             : 
     362   106377427 :         if (isblock) {
     363    11536440 :                 error = xfs_dir2_block_lookup(&args);
     364    11539395 :                 goto out_check_rval;
     365             :         }
     366             : 
     367    94840987 :         error = xfs_dir2_isleaf(&args, &isleaf);
     368    95273236 :         if (error)
     369             :                 return error;
     370             : 
     371    95273236 :         if (isleaf) {
     372     6798104 :                 error = xfs_dir2_leaf_lookup(&args);
     373     6799728 :                 goto out_check_rval;
     374             :         }
     375             : 
     376    88475132 :         error = xfs_dir2_node_lookup(&args);
     377             : 
     378   306625212 : out_check_rval:
     379   306625212 :         if (error == -EEXIST)
     380             :                 error = 0;
     381     2489362 :         if (!error)
     382   304135850 :                 *ino = args.inumber;
     383             :         return error;
     384             : }

Generated by: LCOV version 1.14