1189251Ssam// SPDX-License-Identifier: GPL-2.0-or-later 2252726Srpaulo/* 3252726Srpaulo * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 4189251Ssam * Author: Darrick J. Wong <djwong@kernel.org> 5252726Srpaulo */ 6252726Srpaulo#include "xfs.h" 7189251Ssam#include "xfs_fs.h" 8189251Ssam#include "xfs_shared.h" 9189251Ssam#include "xfs_format.h" 10189251Ssam#include "xfs_trans_resv.h" 11189251Ssam#include "xfs_mount.h" 12214734Srpaulo#include "xfs_log_format.h" 13214734Srpaulo#include "xfs_trans.h" 14189251Ssam#include "xfs_inode.h" 15189251Ssam#include "xfs_icache.h" 16189251Ssam#include "xfs_dir2.h" 17189251Ssam#include "xfs_dir2_priv.h" 18189251Ssam#include "xfs_health.h" 19189251Ssam#include "xfs_attr.h" 20189251Ssam#include "xfs_parent.h" 21189251Ssam#include "scrub/scrub.h" 22189251Ssam#include "scrub/common.h" 23189251Ssam#include "scrub/dabtree.h" 24189251Ssam#include "scrub/readdir.h" 25189251Ssam#include "scrub/health.h" 26189251Ssam#include "scrub/repair.h" 27189251Ssam#include "scrub/trace.h" 28189251Ssam#include "scrub/xfile.h" 29189251Ssam#include "scrub/xfarray.h" 30189251Ssam#include "scrub/xfblob.h" 31189251Ssam 32189251Ssam/* Set us up to scrub directories. */ 33189251Ssamint 34189251Ssamxchk_setup_directory( 35189251Ssam struct xfs_scrub *sc) 36189251Ssam{ 37189251Ssam int error; 38189251Ssam 39189251Ssam if (xchk_could_repair(sc)) { 40189251Ssam error = xrep_setup_directory(sc); 41189251Ssam if (error) 42189251Ssam return error; 43189251Ssam } 44189251Ssam 45189251Ssam return xchk_setup_inode_contents(sc, 0); 46189251Ssam} 47189251Ssam 48189251Ssam/* Directories */ 49189251Ssam 50189251Ssam/* Deferred directory entry that we saved for later. */ 51189251Ssamstruct xchk_dirent { 52189251Ssam /* Cookie for retrieval of the dirent name. */ 53189251Ssam xfblob_cookie name_cookie; 54189251Ssam 55189251Ssam /* Child inode number. */ 56189251Ssam xfs_ino_t ino; 57189251Ssam 58189251Ssam /* Length of the pptr name. */ 59189251Ssam uint8_t namelen; 60189251Ssam}; 61189251Ssam 62189251Ssamstruct xchk_dir { 63189251Ssam struct xfs_scrub *sc; 64252726Srpaulo 65252726Srpaulo /* information for parent pointer validation. */ 66189251Ssam struct xfs_parent_rec pptr_rec; 67189251Ssam struct xfs_da_args pptr_args; 68189251Ssam 69189251Ssam /* Fixed-size array of xchk_dirent structures. */ 70189251Ssam struct xfarray *dir_entries; 71189251Ssam 72189251Ssam /* Blobs containing dirent names. */ 73189251Ssam struct xfblob *dir_names; 74189251Ssam 75189251Ssam /* If we've cycled the ILOCK, we must revalidate deferred dirents. */ 76189251Ssam bool need_revalidate; 77189251Ssam 78252726Srpaulo /* Name buffer for dirent revalidation. */ 79252726Srpaulo struct xfs_name xname; 80252726Srpaulo uint8_t namebuf[MAXNAMELEN]; 81252726Srpaulo}; 82252726Srpaulo 83189251Ssam/* Scrub a directory entry. */ 84189251Ssam 85189251Ssam/* Check that an inode's mode matches a given XFS_DIR3_FT_* type. */ 86189251SsamSTATIC void 87189251Ssamxchk_dir_check_ftype( 88189251Ssam struct xfs_scrub *sc, 89189251Ssam xfs_fileoff_t offset, 90189251Ssam struct xfs_inode *ip, 91189251Ssam int ftype) 92189251Ssam{ 93189251Ssam struct xfs_mount *mp = sc->mp; 94189251Ssam 95189251Ssam if (!xfs_has_ftype(mp)) { 96189251Ssam if (ftype != XFS_DIR3_FT_UNKNOWN && ftype != XFS_DIR3_FT_DIR) 97189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 98189251Ssam return; 99189251Ssam } 100189251Ssam 101189251Ssam if (xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype) 102189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 103189251Ssam} 104189251Ssam 105189251Ssam/* 106189251Ssam * Try to lock a child file for checking parent pointers. Returns the inode 107252726Srpaulo * flags for the locks we now hold, or zero if we failed. 108252726Srpaulo */ 109252726SrpauloSTATIC unsigned int 110252726Srpauloxchk_dir_lock_child( 111252726Srpaulo struct xfs_scrub *sc, 112252726Srpaulo struct xfs_inode *ip) 113252726Srpaulo{ 114252726Srpaulo if (!xfs_ilock_nowait(ip, XFS_IOLOCK_SHARED)) 115252726Srpaulo return 0; 116252726Srpaulo 117252726Srpaulo if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) { 118252726Srpaulo xfs_iunlock(ip, XFS_IOLOCK_SHARED); 119252726Srpaulo return 0; 120252726Srpaulo } 121252726Srpaulo 122189251Ssam if (!xfs_inode_has_attr_fork(ip) || !xfs_need_iread_extents(&ip->i_af)) 123189251Ssam return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED; 124189251Ssam 125189251Ssam xfs_iunlock(ip, XFS_ILOCK_SHARED); 126189251Ssam 127189251Ssam if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) { 128189251Ssam xfs_iunlock(ip, XFS_IOLOCK_SHARED); 129189251Ssam return 0; 130189251Ssam } 131189251Ssam 132189251Ssam return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL; 133189251Ssam} 134189251Ssam 135252726Srpaulo/* Check the backwards link (parent pointer) associated with this dirent. */ 136252726SrpauloSTATIC int 137189251Ssamxchk_dir_parent_pointer( 138189251Ssam struct xchk_dir *sd, 139189251Ssam const struct xfs_name *name, 140189251Ssam struct xfs_inode *ip) 141189251Ssam{ 142252726Srpaulo struct xfs_scrub *sc = sd->sc; 143189251Ssam int error; 144189251Ssam 145252726Srpaulo xfs_inode_to_parent_rec(&sd->pptr_rec, sc->ip); 146189251Ssam error = xfs_parent_lookup(sc->tp, ip, name, &sd->pptr_rec, 147189251Ssam &sd->pptr_args); 148252726Srpaulo if (error == -ENOATTR) 149189251Ssam xchk_fblock_xref_set_corrupt(sc, XFS_DATA_FORK, 0); 150252726Srpaulo 151252726Srpaulo return 0; 152252726Srpaulo} 153189251Ssam 154189251Ssam/* Look for a parent pointer matching this dirent, if the child isn't busy. */ 155189251SsamSTATIC int 156189251Ssamxchk_dir_check_pptr_fast( 157189251Ssam struct xchk_dir *sd, 158189251Ssam xfs_dir2_dataptr_t dapos, 159252726Srpaulo const struct xfs_name *name, 160252726Srpaulo struct xfs_inode *ip) 161252726Srpaulo{ 162252726Srpaulo struct xfs_scrub *sc = sd->sc; 163252726Srpaulo unsigned int lockmode; 164252726Srpaulo int error; 165252726Srpaulo 166252726Srpaulo /* dot and dotdot entries do not have parent pointers */ 167252726Srpaulo if (xfs_dir2_samename(name, &xfs_name_dot) || 168252726Srpaulo xfs_dir2_samename(name, &xfs_name_dotdot)) 169252726Srpaulo return 0; 170252726Srpaulo 171252726Srpaulo /* No self-referential non-dot or dotdot dirents. */ 172189251Ssam if (ip == sc->ip) { 173189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 174189251Ssam return -ECANCELED; 175189251Ssam } 176189251Ssam 177189251Ssam /* Try to lock the inode. */ 178189251Ssam lockmode = xchk_dir_lock_child(sc, ip); 179189251Ssam if (!lockmode) { 180189251Ssam struct xchk_dirent save_de = { 181189251Ssam .namelen = name->len, 182189251Ssam .ino = ip->i_ino, 183189251Ssam }; 184252726Srpaulo 185252726Srpaulo /* Couldn't lock the inode, so save the dirent for later. */ 186252726Srpaulo trace_xchk_dir_defer(sc->ip, name, ip->i_ino); 187189251Ssam 188189251Ssam error = xfblob_storename(sd->dir_names, &save_de.name_cookie, 189189251Ssam name); 190189251Ssam if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, 191189251Ssam &error)) 192252726Srpaulo return error; 193252726Srpaulo 194252726Srpaulo error = xfarray_append(sd->dir_entries, &save_de); 195252726Srpaulo if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, 196252726Srpaulo &error)) 197252726Srpaulo return error; 198252726Srpaulo 199252726Srpaulo return 0; 200252726Srpaulo } 201252726Srpaulo 202252726Srpaulo error = xchk_dir_parent_pointer(sd, name, ip); 203252726Srpaulo xfs_iunlock(ip, lockmode); 204252726Srpaulo return error; 205252726Srpaulo} 206252726Srpaulo 207252726Srpaulo/* 208252726Srpaulo * Scrub a single directory entry. 209252726Srpaulo * 210252726Srpaulo * Check the inode number to make sure it's sane, then we check that we can 211189251Ssam * look up this filename. Finally, we check the ftype. 212189251Ssam */ 213189251SsamSTATIC int 214189251Ssamxchk_dir_actor( 215189251Ssam struct xfs_scrub *sc, 216189251Ssam struct xfs_inode *dp, 217189251Ssam xfs_dir2_dataptr_t dapos, 218189251Ssam const struct xfs_name *name, 219189251Ssam xfs_ino_t ino, 220189251Ssam void *priv) 221189251Ssam{ 222189251Ssam struct xfs_mount *mp = dp->i_mount; 223189251Ssam struct xfs_inode *ip; 224189251Ssam struct xchk_dir *sd = priv; 225189251Ssam xfs_ino_t lookup_ino; 226189251Ssam xfs_dablk_t offset; 227189251Ssam int error = 0; 228252726Srpaulo 229189251Ssam offset = xfs_dir2_db_to_da(mp->m_dir_geo, 230189251Ssam xfs_dir2_dataptr_to_db(mp->m_dir_geo, dapos)); 231189251Ssam 232189251Ssam if (xchk_should_terminate(sc, &error)) 233189251Ssam return error; 234189251Ssam 235189251Ssam /* Does this inode number make sense? */ 236189251Ssam if (!xfs_verify_dir_ino(mp, ino)) { 237189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 238189251Ssam return -ECANCELED; 239189251Ssam } 240252726Srpaulo 241252726Srpaulo /* Does this name make sense? */ 242189251Ssam if (!xfs_dir2_namecheck(name->name, name->len)) { 243189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 244189251Ssam return -ECANCELED; 245189251Ssam } 246189251Ssam 247189251Ssam if (xfs_dir2_samename(name, &xfs_name_dot)) { 248189251Ssam /* If this is "." then check that the inum matches the dir. */ 249189251Ssam if (ino != dp->i_ino) 250189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 251189251Ssam } else if (xfs_dir2_samename(name, &xfs_name_dotdot)) { 252252726Srpaulo /* 253252726Srpaulo * If this is ".." in the root inode, check that the inum 254252726Srpaulo * matches this dir. 255252726Srpaulo */ 256252726Srpaulo if (dp->i_ino == mp->m_sb.sb_rootino && ino != dp->i_ino) 257189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 258189251Ssam } 259189251Ssam 260189251Ssam /* Verify that we can look up this name by hash. */ 261189251Ssam error = xchk_dir_lookup(sc, dp, name, &lookup_ino); 262189251Ssam /* ENOENT means the hash lookup failed and the dir is corrupt */ 263189251Ssam if (error == -ENOENT) 264189251Ssam error = -EFSCORRUPTED; 265189251Ssam if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error)) 266189251Ssam goto out; 267189251Ssam if (lookup_ino != ino) { 268189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 269189251Ssam return -ECANCELED; 270189251Ssam } 271189251Ssam 272189251Ssam /* 273189251Ssam * Grab the inode pointed to by the dirent. We release the inode 274189251Ssam * before we cancel the scrub transaction. 275189251Ssam * 276189251Ssam * If _iget returns -EINVAL or -ENOENT then the child inode number is 277189251Ssam * garbage and the directory is corrupt. If the _iget returns 278189251Ssam * -EFSCORRUPTED or -EFSBADCRC then the child is corrupt which is a 279189251Ssam * cross referencing error. Any other error is an operational error. 280189251Ssam */ 281189251Ssam error = xchk_iget(sc, ino, &ip); 282189251Ssam if (error == -EINVAL || error == -ENOENT) { 283252726Srpaulo error = -EFSCORRUPTED; 284189251Ssam xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error); 285189251Ssam goto out; 286189251Ssam } 287189251Ssam if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, offset, &error)) 288189251Ssam goto out; 289189251Ssam 290189251Ssam xchk_dir_check_ftype(sc, offset, ip, name->type); 291189251Ssam 292189251Ssam if (xfs_has_parent(mp)) { 293189251Ssam error = xchk_dir_check_pptr_fast(sd, dapos, name, ip); 294189251Ssam if (error) 295189251Ssam goto out_rele; 296189251Ssam } 297189251Ssam 298189251Ssamout_rele: 299252726Srpaulo xchk_irele(sc, ip); 300252726Srpauloout: 301252726Srpaulo if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 302189251Ssam return -ECANCELED; 303189251Ssam return error; 304189251Ssam} 305189251Ssam 306252726Srpaulo/* Scrub a directory btree record. */ 307252726SrpauloSTATIC int 308252726Srpauloxchk_dir_rec( 309189251Ssam struct xchk_da_btree *ds, 310189251Ssam int level) 311252726Srpaulo{ 312252726Srpaulo struct xfs_name dname = { }; 313189251Ssam struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 314252726Srpaulo struct xfs_mount *mp = ds->state->mp; 315189251Ssam struct xfs_inode *dp = ds->dargs.dp; 316252726Srpaulo struct xfs_da_geometry *geo = mp->m_dir_geo; 317252726Srpaulo struct xfs_dir2_data_entry *dent; 318252726Srpaulo struct xfs_buf *bp; 319252726Srpaulo struct xfs_dir2_leaf_entry *ent; 320252726Srpaulo unsigned int end; 321252726Srpaulo unsigned int iter_off; 322252726Srpaulo xfs_ino_t ino; 323252726Srpaulo xfs_dablk_t rec_bno; 324252726Srpaulo xfs_dir2_db_t db; 325252726Srpaulo xfs_dir2_data_aoff_t off; 326252726Srpaulo xfs_dir2_dataptr_t ptr; 327252726Srpaulo xfs_dahash_t calc_hash; 328252726Srpaulo xfs_dahash_t hash; 329252726Srpaulo struct xfs_dir3_icleaf_hdr hdr; 330252726Srpaulo unsigned int tag; 331189251Ssam int error; 332189251Ssam 333189251Ssam ASSERT(blk->magic == XFS_DIR2_LEAF1_MAGIC || 334189251Ssam blk->magic == XFS_DIR2_LEAFN_MAGIC); 335252726Srpaulo 336252726Srpaulo xfs_dir2_leaf_hdr_from_disk(mp, &hdr, blk->bp->b_addr); 337252726Srpaulo ent = hdr.ents + blk->index; 338252726Srpaulo 339189251Ssam /* Check the hash of the entry. */ 340252726Srpaulo error = xchk_da_btree_hash(ds, level, &ent->hashval); 341252726Srpaulo if (error) 342252726Srpaulo goto out; 343252726Srpaulo 344252726Srpaulo /* Valid hash pointer? */ 345189251Ssam ptr = be32_to_cpu(ent->address); 346189251Ssam if (ptr == 0) 347252726Srpaulo return 0; 348189251Ssam 349252726Srpaulo /* Find the directory entry's location. */ 350252726Srpaulo db = xfs_dir2_dataptr_to_db(geo, ptr); 351252726Srpaulo off = xfs_dir2_dataptr_to_off(geo, ptr); 352252726Srpaulo rec_bno = xfs_dir2_db_to_da(geo, db); 353252726Srpaulo 354252726Srpaulo if (rec_bno >= geo->leafblk) { 355252726Srpaulo xchk_da_set_corrupt(ds, level); 356252726Srpaulo goto out; 357252726Srpaulo } 358252726Srpaulo error = xfs_dir3_data_read(ds->dargs.trans, dp, ds->dargs.owner, 359252726Srpaulo rec_bno, XFS_DABUF_MAP_HOLE_OK, &bp); 360252726Srpaulo if (!xchk_fblock_process_error(ds->sc, XFS_DATA_FORK, rec_bno, 361252726Srpaulo &error)) 362252726Srpaulo goto out; 363252726Srpaulo if (!bp) { 364252726Srpaulo xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); 365189251Ssam goto out; 366189251Ssam } 367252726Srpaulo xchk_buffer_recheck(ds->sc, bp); 368252726Srpaulo 369252726Srpaulo if (ds->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 370252726Srpaulo goto out_relse; 371252726Srpaulo 372252726Srpaulo dent = bp->b_addr + off; 373252726Srpaulo 374252726Srpaulo /* Make sure we got a real directory entry. */ 375252726Srpaulo iter_off = geo->data_entry_offset; 376252726Srpaulo end = xfs_dir3_data_end_offset(geo, bp->b_addr); 377252726Srpaulo if (!end) { 378252726Srpaulo xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); 379252726Srpaulo goto out_relse; 380252726Srpaulo } 381252726Srpaulo for (;;) { 382252726Srpaulo struct xfs_dir2_data_entry *dep = bp->b_addr + iter_off; 383252726Srpaulo struct xfs_dir2_data_unused *dup = bp->b_addr + iter_off; 384252726Srpaulo 385252726Srpaulo if (iter_off >= end) { 386252726Srpaulo xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); 387252726Srpaulo goto out_relse; 388252726Srpaulo } 389252726Srpaulo 390252726Srpaulo if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 391252726Srpaulo iter_off += be16_to_cpu(dup->length); 392252726Srpaulo continue; 393252726Srpaulo } 394252726Srpaulo if (dep == dent) 395252726Srpaulo break; 396252726Srpaulo iter_off += xfs_dir2_data_entsize(mp, dep->namelen); 397189251Ssam } 398189251Ssam 399252726Srpaulo /* Retrieve the entry, sanity check it, and compare hashes. */ 400252726Srpaulo ino = be64_to_cpu(dent->inumber); 401252726Srpaulo hash = be32_to_cpu(ent->hashval); 402252726Srpaulo tag = be16_to_cpup(xfs_dir2_data_entry_tag_p(mp, dent)); 403252726Srpaulo if (!xfs_verify_dir_ino(mp, ino) || tag != off) 404252726Srpaulo xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); 405252726Srpaulo if (dent->namelen == 0) { 406252726Srpaulo xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); 407252726Srpaulo goto out_relse; 408252726Srpaulo } 409189251Ssam 410189251Ssam /* Does the directory hash match? */ 411189251Ssam dname.name = dent->name; 412189251Ssam dname.len = dent->namelen; 413189251Ssam calc_hash = xfs_dir2_hashname(mp, &dname); 414189251Ssam if (calc_hash != hash) 415189251Ssam xchk_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno); 416189251Ssam 417189251Ssamout_relse: 418189251Ssam xfs_trans_brelse(ds->dargs.trans, bp); 419189251Ssamout: 420189251Ssam return error; 421189251Ssam} 422189251Ssam 423189251Ssam/* 424189251Ssam * Is this unused entry either in the bestfree or smaller than all of 425189251Ssam * them? We've already checked that the bestfrees are sorted longest to 426189251Ssam * shortest, and that there aren't any bogus entries. 427189251Ssam */ 428189251SsamSTATIC void 429189251Ssamxchk_directory_check_free_entry( 430189251Ssam struct xfs_scrub *sc, 431189251Ssam xfs_dablk_t lblk, 432189251Ssam struct xfs_dir2_data_free *bf, 433189251Ssam struct xfs_dir2_data_unused *dup) 434189251Ssam{ 435189251Ssam struct xfs_dir2_data_free *dfp; 436189251Ssam unsigned int dup_length; 437189251Ssam 438189251Ssam dup_length = be16_to_cpu(dup->length); 439189251Ssam 440189251Ssam /* Unused entry is shorter than any of the bestfrees */ 441189251Ssam if (dup_length < be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length)) 442189251Ssam return; 443189251Ssam 444189251Ssam for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT - 1]; dfp >= bf; dfp--) 445189251Ssam if (dup_length == be16_to_cpu(dfp->length)) 446189251Ssam return; 447189251Ssam 448189251Ssam /* Unused entry should be in the bestfrees but wasn't found. */ 449189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 450189251Ssam} 451189251Ssam 452189251Ssam/* Check free space info in a directory data block. */ 453189251SsamSTATIC int 454189251Ssamxchk_directory_data_bestfree( 455189251Ssam struct xfs_scrub *sc, 456189251Ssam xfs_dablk_t lblk, 457189251Ssam bool is_block) 458189251Ssam{ 459189251Ssam struct xfs_dir2_data_unused *dup; 460189251Ssam struct xfs_dir2_data_free *dfp; 461189251Ssam struct xfs_buf *bp; 462252726Srpaulo struct xfs_dir2_data_free *bf; 463189251Ssam struct xfs_mount *mp = sc->mp; 464252726Srpaulo u16 tag; 465189251Ssam unsigned int nr_bestfrees = 0; 466189251Ssam unsigned int nr_frees = 0; 467189251Ssam unsigned int smallest_bestfree; 468189251Ssam int newlen; 469189251Ssam unsigned int offset; 470189251Ssam unsigned int end; 471252726Srpaulo int error; 472252726Srpaulo 473189251Ssam if (is_block) { 474189251Ssam /* dir block format */ 475189251Ssam if (lblk != XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET)) 476189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 477189251Ssam error = xfs_dir3_block_read(sc->tp, sc->ip, sc->ip->i_ino, &bp); 478189251Ssam } else { 479189251Ssam /* dir data format */ 480189251Ssam error = xfs_dir3_data_read(sc->tp, sc->ip, sc->ip->i_ino, lblk, 481189251Ssam 0, &bp); 482189251Ssam } 483189251Ssam if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) 484189251Ssam goto out; 485189251Ssam xchk_buffer_recheck(sc, bp); 486189251Ssam 487189251Ssam /* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */ 488189251Ssam 489189251Ssam if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 490189251Ssam goto out_buf; 491252726Srpaulo 492189251Ssam /* Do the bestfrees correspond to actual free space? */ 493189251Ssam bf = xfs_dir2_data_bestfree_p(mp, bp->b_addr); 494189251Ssam smallest_bestfree = UINT_MAX; 495189251Ssam for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) { 496189251Ssam offset = be16_to_cpu(dfp->offset); 497189251Ssam if (offset == 0) 498189251Ssam continue; 499189251Ssam if (offset >= mp->m_dir_geo->blksize) { 500189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 501189251Ssam goto out_buf; 502189251Ssam } 503189251Ssam dup = bp->b_addr + offset; 504189251Ssam tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)); 505189251Ssam 506189251Ssam /* bestfree doesn't match the entry it points at? */ 507189251Ssam if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG) || 508189251Ssam be16_to_cpu(dup->length) != be16_to_cpu(dfp->length) || 509189251Ssam tag != offset) { 510189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 511189251Ssam goto out_buf; 512189251Ssam } 513189251Ssam 514189251Ssam /* bestfree records should be ordered largest to smallest */ 515189251Ssam if (smallest_bestfree < be16_to_cpu(dfp->length)) { 516189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 517189251Ssam goto out_buf; 518189251Ssam } 519189251Ssam 520189251Ssam smallest_bestfree = be16_to_cpu(dfp->length); 521189251Ssam nr_bestfrees++; 522189251Ssam } 523189251Ssam 524189251Ssam /* Make sure the bestfrees are actually the best free spaces. */ 525189251Ssam offset = mp->m_dir_geo->data_entry_offset; 526189251Ssam end = xfs_dir3_data_end_offset(mp->m_dir_geo, bp->b_addr); 527189251Ssam 528189251Ssam /* Iterate the entries, stopping when we hit or go past the end. */ 529189251Ssam while (offset < end) { 530189251Ssam dup = bp->b_addr + offset; 531189251Ssam 532189251Ssam /* Skip real entries */ 533189251Ssam if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) { 534189251Ssam struct xfs_dir2_data_entry *dep = bp->b_addr + offset; 535252726Srpaulo 536252726Srpaulo newlen = xfs_dir2_data_entsize(mp, dep->namelen); 537189251Ssam if (newlen <= 0) { 538189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 539189251Ssam lblk); 540189251Ssam goto out_buf; 541189251Ssam } 542189251Ssam offset += newlen; 543189251Ssam continue; 544189251Ssam } 545189251Ssam 546189251Ssam /* Spot check this free entry */ 547189251Ssam tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)); 548189251Ssam if (tag != offset) { 549189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 550189251Ssam goto out_buf; 551189251Ssam } 552189251Ssam 553189251Ssam /* 554189251Ssam * Either this entry is a bestfree or it's smaller than 555189251Ssam * any of the bestfrees. 556189251Ssam */ 557189251Ssam xchk_directory_check_free_entry(sc, lblk, bf, dup); 558189251Ssam if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 559189251Ssam goto out_buf; 560189251Ssam 561189251Ssam /* Move on. */ 562189251Ssam newlen = be16_to_cpu(dup->length); 563189251Ssam if (newlen <= 0) { 564189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 565189251Ssam goto out_buf; 566189251Ssam } 567189251Ssam offset += newlen; 568252726Srpaulo if (offset <= end) 569252726Srpaulo nr_frees++; 570252726Srpaulo } 571189251Ssam 572189251Ssam /* We're required to fill all the space. */ 573189251Ssam if (offset != end) 574189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 575189251Ssam 576189251Ssam /* Did we see at least as many free slots as there are bestfrees? */ 577252726Srpaulo if (nr_frees < nr_bestfrees) 578252726Srpaulo xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 579252726Srpauloout_buf: 580189251Ssam xfs_trans_brelse(sc->tp, bp); 581189251Ssamout: 582189251Ssam return error; 583252726Srpaulo} 584252726Srpaulo 585252726Srpaulo/* 586189251Ssam * Does the free space length in the free space index block ($len) match 587189251Ssam * the longest length in the directory data block's bestfree array? 588189251Ssam * Assume that we've already checked that the data block's bestfree 589189251Ssam * array is in order. 590189251Ssam */ 591189251SsamSTATIC void 592189251Ssamxchk_directory_check_freesp( 593189251Ssam struct xfs_scrub *sc, 594189251Ssam xfs_dablk_t lblk, 595189251Ssam struct xfs_buf *dbp, 596189251Ssam unsigned int len) 597189251Ssam{ 598189251Ssam struct xfs_dir2_data_free *dfp; 599189251Ssam 600189251Ssam dfp = xfs_dir2_data_bestfree_p(sc->mp, dbp->b_addr); 601189251Ssam 602189251Ssam if (len != be16_to_cpu(dfp->length)) 603189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 604189251Ssam 605189251Ssam if (len > 0 && be16_to_cpu(dfp->offset) == 0) 606189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 607189251Ssam} 608189251Ssam 609189251Ssam/* Check free space info in a directory leaf1 block. */ 610189251SsamSTATIC int 611189251Ssamxchk_directory_leaf1_bestfree( 612189251Ssam struct xfs_scrub *sc, 613189251Ssam struct xfs_da_args *args, 614189251Ssam xfs_dir2_db_t last_data_db, 615189251Ssam xfs_dablk_t lblk) 616189251Ssam{ 617189251Ssam struct xfs_dir3_icleaf_hdr leafhdr; 618189251Ssam struct xfs_dir2_leaf_tail *ltp; 619189251Ssam struct xfs_dir2_leaf *leaf; 620189251Ssam struct xfs_buf *dbp; 621189251Ssam struct xfs_buf *bp; 622189251Ssam struct xfs_da_geometry *geo = sc->mp->m_dir_geo; 623189251Ssam __be16 *bestp; 624189251Ssam __u16 best; 625189251Ssam __u32 hash; 626189251Ssam __u32 lasthash = 0; 627189251Ssam __u32 bestcount; 628189251Ssam unsigned int stale = 0; 629189251Ssam int i; 630189251Ssam int error; 631189251Ssam 632189251Ssam /* Read the free space block. */ 633189251Ssam error = xfs_dir3_leaf_read(sc->tp, sc->ip, sc->ip->i_ino, lblk, &bp); 634189251Ssam if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) 635189251Ssam return error; 636189251Ssam xchk_buffer_recheck(sc, bp); 637189251Ssam 638189251Ssam leaf = bp->b_addr; 639189251Ssam xfs_dir2_leaf_hdr_from_disk(sc->ip->i_mount, &leafhdr, leaf); 640189251Ssam ltp = xfs_dir2_leaf_tail_p(geo, leaf); 641189251Ssam bestcount = be32_to_cpu(ltp->bestcount); 642189251Ssam bestp = xfs_dir2_leaf_bests_p(ltp); 643189251Ssam 644189251Ssam if (xfs_has_crc(sc->mp)) { 645189251Ssam struct xfs_dir3_leaf_hdr *hdr3 = bp->b_addr; 646189251Ssam 647189251Ssam if (hdr3->pad != cpu_to_be32(0)) 648189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 649189251Ssam } 650189251Ssam 651189251Ssam /* 652189251Ssam * There must be enough bestfree slots to cover all the directory data 653189251Ssam * blocks that we scanned. It is possible for there to be a hole 654189251Ssam * between the last data block and i_disk_size. This seems like an 655189251Ssam * oversight to the scrub author, but as we have been writing out 656189251Ssam * directories like this (and xfs_repair doesn't mind them) for years, 657189251Ssam * that's what we have to check. 658189251Ssam */ 659189251Ssam if (bestcount != last_data_db + 1) { 660189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 661189251Ssam goto out; 662189251Ssam } 663189251Ssam 664189251Ssam /* Is the leaf count even remotely sane? */ 665189251Ssam if (leafhdr.count > geo->leaf_max_ents) { 666189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 667189251Ssam goto out; 668189251Ssam } 669189251Ssam 670189251Ssam /* Leaves and bests don't overlap in leaf format. */ 671189251Ssam if ((char *)&leafhdr.ents[leafhdr.count] > (char *)bestp) { 672189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 673189251Ssam goto out; 674189251Ssam } 675189251Ssam 676189251Ssam /* Check hash value order, count stale entries. */ 677189251Ssam for (i = 0; i < leafhdr.count; i++) { 678189251Ssam hash = be32_to_cpu(leafhdr.ents[i].hashval); 679189251Ssam if (i > 0 && lasthash > hash) 680189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 681189251Ssam lasthash = hash; 682189251Ssam if (leafhdr.ents[i].address == 683189251Ssam cpu_to_be32(XFS_DIR2_NULL_DATAPTR)) 684189251Ssam stale++; 685189251Ssam } 686189251Ssam if (leafhdr.stale != stale) 687189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 688189251Ssam if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 689189251Ssam goto out; 690189251Ssam 691189251Ssam /* Check all the bestfree entries. */ 692189251Ssam for (i = 0; i < bestcount; i++, bestp++) { 693189251Ssam best = be16_to_cpu(*bestp); 694189251Ssam error = xfs_dir3_data_read(sc->tp, sc->ip, args->owner, 695189251Ssam xfs_dir2_db_to_da(args->geo, i), 696189251Ssam XFS_DABUF_MAP_HOLE_OK, &dbp); 697189251Ssam if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, 698189251Ssam &error)) 699189251Ssam break; 700189251Ssam 701189251Ssam if (!dbp) { 702189251Ssam if (best != NULLDATAOFF) { 703189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 704189251Ssam lblk); 705189251Ssam break; 706189251Ssam } 707189251Ssam continue; 708189251Ssam } 709189251Ssam 710189251Ssam if (best == NULLDATAOFF) 711189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 712189251Ssam else 713189251Ssam xchk_directory_check_freesp(sc, lblk, dbp, best); 714189251Ssam xfs_trans_brelse(sc->tp, dbp); 715189251Ssam if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 716189251Ssam break; 717189251Ssam } 718189251Ssamout: 719189251Ssam xfs_trans_brelse(sc->tp, bp); 720189251Ssam return error; 721189251Ssam} 722189251Ssam 723189251Ssam/* Check free space info in a directory freespace block. */ 724189251SsamSTATIC int 725189251Ssamxchk_directory_free_bestfree( 726189251Ssam struct xfs_scrub *sc, 727189251Ssam struct xfs_da_args *args, 728189251Ssam xfs_dablk_t lblk) 729189251Ssam{ 730189251Ssam struct xfs_dir3_icfree_hdr freehdr; 731189251Ssam struct xfs_buf *dbp; 732189251Ssam struct xfs_buf *bp; 733189251Ssam __u16 best; 734189251Ssam unsigned int stale = 0; 735189251Ssam int i; 736252726Srpaulo int error; 737189251Ssam 738252726Srpaulo /* Read the free space block */ 739189251Ssam error = xfs_dir2_free_read(sc->tp, sc->ip, sc->ip->i_ino, lblk, &bp); 740189251Ssam if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) 741189251Ssam return error; 742189251Ssam xchk_buffer_recheck(sc, bp); 743209158Srpaulo 744209158Srpaulo if (xfs_has_crc(sc->mp)) { 745209158Srpaulo struct xfs_dir3_free_hdr *hdr3 = bp->b_addr; 746209158Srpaulo 747209158Srpaulo if (hdr3->pad != cpu_to_be32(0)) 748209158Srpaulo xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 749209158Srpaulo } 750209158Srpaulo 751209158Srpaulo /* Check all the entries. */ 752209158Srpaulo xfs_dir2_free_hdr_from_disk(sc->ip->i_mount, &freehdr, bp->b_addr); 753209158Srpaulo for (i = 0; i < freehdr.nvalid; i++) { 754189251Ssam best = be16_to_cpu(freehdr.bests[i]); 755189251Ssam if (best == NULLDATAOFF) { 756189251Ssam stale++; 757189251Ssam continue; 758189251Ssam } 759189251Ssam error = xfs_dir3_data_read(sc->tp, sc->ip, args->owner, 760189251Ssam (freehdr.firstdb + i) * args->geo->fsbcount, 761189251Ssam 0, &dbp); 762189251Ssam if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, 763189251Ssam &error)) 764189251Ssam goto out; 765189251Ssam xchk_directory_check_freesp(sc, lblk, dbp, best); 766189251Ssam xfs_trans_brelse(sc->tp, dbp); 767189251Ssam } 768189251Ssam 769189251Ssam if (freehdr.nused + stale != freehdr.nvalid) 770189251Ssam xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 771189251Ssamout: 772189251Ssam xfs_trans_brelse(sc->tp, bp); 773189251Ssam return error; 774189251Ssam} 775189251Ssam 776189251Ssam/* Check free space information in directories. */ 777189251SsamSTATIC int 778189251Ssamxchk_directory_blocks( 779189251Ssam struct xfs_scrub *sc) 780252726Srpaulo{ 781252726Srpaulo struct xfs_bmbt_irec got; 782252726Srpaulo struct xfs_da_args args = { 783252726Srpaulo .dp = sc->ip, 784252726Srpaulo .whichfork = XFS_DATA_FORK, 785252726Srpaulo .geo = sc->mp->m_dir_geo, 786189251Ssam .trans = sc->tp, 787189251Ssam .owner = sc->ip->i_ino, 788189251Ssam }; 789189251Ssam struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK); 790189251Ssam struct xfs_mount *mp = sc->mp; 791189251Ssam xfs_fileoff_t leaf_lblk; 792189251Ssam xfs_fileoff_t free_lblk; 793189251Ssam xfs_fileoff_t lblk; 794189251Ssam struct xfs_iext_cursor icur; 795 xfs_dablk_t dabno; 796 xfs_dir2_db_t last_data_db = 0; 797 bool found; 798 bool is_block = false; 799 int error; 800 801 /* Ignore local format directories. */ 802 if (ifp->if_format != XFS_DINODE_FMT_EXTENTS && 803 ifp->if_format != XFS_DINODE_FMT_BTREE) 804 return 0; 805 806 lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET); 807 leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET); 808 free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET); 809 810 /* Is this a block dir? */ 811 if (xfs_dir2_format(&args, &error) == XFS_DIR2_FMT_BLOCK) 812 is_block = true; 813 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error)) 814 goto out; 815 816 /* Iterate all the data extents in the directory... */ 817 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); 818 while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) { 819 /* No more data blocks... */ 820 if (got.br_startoff >= leaf_lblk) 821 break; 822 823 /* 824 * Check each data block's bestfree data. 825 * 826 * Iterate all the fsbcount-aligned block offsets in 827 * this directory. The directory block reading code is 828 * smart enough to do its own bmap lookups to handle 829 * discontiguous directory blocks. When we're done 830 * with the extent record, re-query the bmap at the 831 * next fsbcount-aligned offset to avoid redundant 832 * block checks. 833 */ 834 for (lblk = roundup((xfs_dablk_t)got.br_startoff, 835 args.geo->fsbcount); 836 lblk < got.br_startoff + got.br_blockcount; 837 lblk += args.geo->fsbcount) { 838 last_data_db = xfs_dir2_da_to_db(args.geo, lblk); 839 error = xchk_directory_data_bestfree(sc, lblk, 840 is_block); 841 if (error) 842 goto out; 843 } 844 dabno = got.br_startoff + got.br_blockcount; 845 lblk = roundup(dabno, args.geo->fsbcount); 846 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); 847 } 848 849 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 850 goto out; 851 852 /* Look for a leaf1 block, which has free info. */ 853 if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &icur, &got) && 854 got.br_startoff == leaf_lblk && 855 got.br_blockcount == args.geo->fsbcount && 856 !xfs_iext_next_extent(ifp, &icur, &got)) { 857 if (is_block) { 858 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 859 goto out; 860 } 861 error = xchk_directory_leaf1_bestfree(sc, &args, last_data_db, 862 leaf_lblk); 863 if (error) 864 goto out; 865 } 866 867 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 868 goto out; 869 870 /* Scan for free blocks */ 871 lblk = free_lblk; 872 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); 873 while (found && !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) { 874 /* 875 * Dirs can't have blocks mapped above 2^32. 876 * Single-block dirs shouldn't even be here. 877 */ 878 lblk = got.br_startoff; 879 if (lblk & ~0xFFFFFFFFULL) { 880 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 881 goto out; 882 } 883 if (is_block) { 884 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk); 885 goto out; 886 } 887 888 /* 889 * Check each dir free block's bestfree data. 890 * 891 * Iterate all the fsbcount-aligned block offsets in 892 * this directory. The directory block reading code is 893 * smart enough to do its own bmap lookups to handle 894 * discontiguous directory blocks. When we're done 895 * with the extent record, re-query the bmap at the 896 * next fsbcount-aligned offset to avoid redundant 897 * block checks. 898 */ 899 for (lblk = roundup((xfs_dablk_t)got.br_startoff, 900 args.geo->fsbcount); 901 lblk < got.br_startoff + got.br_blockcount; 902 lblk += args.geo->fsbcount) { 903 error = xchk_directory_free_bestfree(sc, &args, 904 lblk); 905 if (error) 906 goto out; 907 } 908 dabno = got.br_startoff + got.br_blockcount; 909 lblk = roundup(dabno, args.geo->fsbcount); 910 found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &icur, &got); 911 } 912out: 913 return error; 914} 915 916/* 917 * Revalidate a dirent that we collected in the past but couldn't check because 918 * of lock contention. Returns 0 if the dirent is still valid, -ENOENT if it 919 * has gone away on us, or a negative errno. 920 */ 921STATIC int 922xchk_dir_revalidate_dirent( 923 struct xchk_dir *sd, 924 const struct xfs_name *xname, 925 xfs_ino_t ino) 926{ 927 struct xfs_scrub *sc = sd->sc; 928 xfs_ino_t child_ino; 929 int error; 930 931 /* 932 * Look up the directory entry. If we get -ENOENT, the directory entry 933 * went away and there's nothing to revalidate. Return any other 934 * error. 935 */ 936 error = xchk_dir_lookup(sc, sc->ip, xname, &child_ino); 937 if (error) 938 return error; 939 940 /* The inode number changed, nothing to revalidate. */ 941 if (ino != child_ino) 942 return -ENOENT; 943 944 return 0; 945} 946 947/* 948 * Check a directory entry's parent pointers the slow way, which means we cycle 949 * locks a bunch and put up with revalidation until we get it done. 950 */ 951STATIC int 952xchk_dir_slow_dirent( 953 struct xchk_dir *sd, 954 struct xchk_dirent *dirent, 955 const struct xfs_name *xname) 956{ 957 struct xfs_scrub *sc = sd->sc; 958 struct xfs_inode *ip; 959 unsigned int lockmode; 960 int error; 961 962 /* Check that the deferred dirent still exists. */ 963 if (sd->need_revalidate) { 964 error = xchk_dir_revalidate_dirent(sd, xname, dirent->ino); 965 if (error == -ENOENT) 966 return 0; 967 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, 968 &error)) 969 return error; 970 } 971 972 error = xchk_iget(sc, dirent->ino, &ip); 973 if (error == -EINVAL || error == -ENOENT) { 974 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 975 return 0; 976 } 977 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) 978 return error; 979 980 /* 981 * If we can grab both IOLOCK and ILOCK of the alleged child, we can 982 * proceed with the validation. 983 */ 984 lockmode = xchk_dir_lock_child(sc, ip); 985 if (lockmode) { 986 trace_xchk_dir_slowpath(sc->ip, xname, ip->i_ino); 987 goto check_pptr; 988 } 989 990 /* 991 * We couldn't lock the child file. Drop all the locks and try to 992 * get them again, one at a time. 993 */ 994 xchk_iunlock(sc, sc->ilock_flags); 995 sd->need_revalidate = true; 996 997 trace_xchk_dir_ultraslowpath(sc->ip, xname, ip->i_ino); 998 999 error = xchk_dir_trylock_for_pptrs(sc, ip, &lockmode); 1000 if (error) 1001 goto out_rele; 1002 1003 /* Revalidate, since we just cycled the locks. */ 1004 error = xchk_dir_revalidate_dirent(sd, xname, dirent->ino); 1005 if (error == -ENOENT) { 1006 error = 0; 1007 goto out_unlock; 1008 } 1009 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) 1010 goto out_unlock; 1011 1012check_pptr: 1013 error = xchk_dir_parent_pointer(sd, xname, ip); 1014out_unlock: 1015 xfs_iunlock(ip, lockmode); 1016out_rele: 1017 xchk_irele(sc, ip); 1018 return error; 1019} 1020 1021/* Check all the dirents that we deferred the first time around. */ 1022STATIC int 1023xchk_dir_finish_slow_dirents( 1024 struct xchk_dir *sd) 1025{ 1026 xfarray_idx_t array_cur; 1027 int error; 1028 1029 foreach_xfarray_idx(sd->dir_entries, array_cur) { 1030 struct xchk_dirent dirent; 1031 1032 if (sd->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 1033 return 0; 1034 1035 error = xfarray_load(sd->dir_entries, array_cur, &dirent); 1036 if (error) 1037 return error; 1038 1039 error = xfblob_loadname(sd->dir_names, dirent.name_cookie, 1040 &sd->xname, dirent.namelen); 1041 if (error) 1042 return error; 1043 1044 error = xchk_dir_slow_dirent(sd, &dirent, &sd->xname); 1045 if (error) 1046 return error; 1047 } 1048 1049 return 0; 1050} 1051 1052/* Scrub a whole directory. */ 1053int 1054xchk_directory( 1055 struct xfs_scrub *sc) 1056{ 1057 struct xchk_dir *sd; 1058 int error; 1059 1060 if (!S_ISDIR(VFS_I(sc->ip)->i_mode)) 1061 return -ENOENT; 1062 1063 if (xchk_file_looks_zapped(sc, XFS_SICK_INO_DIR_ZAPPED)) { 1064 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 1065 return 0; 1066 } 1067 1068 /* Plausible size? */ 1069 if (sc->ip->i_disk_size < xfs_dir2_sf_hdr_size(0)) { 1070 xchk_ino_set_corrupt(sc, sc->ip->i_ino); 1071 return 0; 1072 } 1073 1074 /* Check directory tree structure */ 1075 error = xchk_da_btree(sc, XFS_DATA_FORK, xchk_dir_rec, NULL); 1076 if (error) 1077 return error; 1078 1079 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 1080 return 0; 1081 1082 /* Check the freespace. */ 1083 error = xchk_directory_blocks(sc); 1084 if (error) 1085 return error; 1086 1087 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 1088 return 0; 1089 1090 sd = kvzalloc(sizeof(struct xchk_dir), XCHK_GFP_FLAGS); 1091 if (!sd) 1092 return -ENOMEM; 1093 sd->sc = sc; 1094 sd->xname.name = sd->namebuf; 1095 1096 if (xfs_has_parent(sc->mp)) { 1097 char *descr; 1098 1099 /* 1100 * Set up some staging memory for dirents that we can't check 1101 * due to locking contention. 1102 */ 1103 descr = xchk_xfile_ino_descr(sc, "slow directory entries"); 1104 error = xfarray_create(descr, 0, sizeof(struct xchk_dirent), 1105 &sd->dir_entries); 1106 kfree(descr); 1107 if (error) 1108 goto out_sd; 1109 1110 descr = xchk_xfile_ino_descr(sc, "slow directory entry names"); 1111 error = xfblob_create(descr, &sd->dir_names); 1112 kfree(descr); 1113 if (error) 1114 goto out_entries; 1115 } 1116 1117 /* Look up every name in this directory by hash. */ 1118 error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, sd); 1119 if (error == -ECANCELED) 1120 error = 0; 1121 if (error) 1122 goto out_names; 1123 1124 if (xfs_has_parent(sc->mp)) { 1125 error = xchk_dir_finish_slow_dirents(sd); 1126 if (error == -ETIMEDOUT) { 1127 /* Couldn't grab a lock, scrub was marked incomplete */ 1128 error = 0; 1129 goto out_names; 1130 } 1131 if (error) 1132 goto out_names; 1133 } 1134 1135out_names: 1136 if (sd->dir_names) 1137 xfblob_destroy(sd->dir_names); 1138out_entries: 1139 if (sd->dir_entries) 1140 xfarray_destroy(sd->dir_entries); 1141out_sd: 1142 kvfree(sd); 1143 if (error) 1144 return error; 1145 1146 /* If the dir is clean, it is clearly not zapped. */ 1147 xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_DIR_ZAPPED); 1148 return 0; 1149} 1150 1151/* 1152 * Decide if this directory has been zapped to satisfy the inode and ifork 1153 * verifiers. Checking and repairing should be postponed until the directory 1154 * is fixed. 1155 */ 1156bool 1157xchk_dir_looks_zapped( 1158 struct xfs_inode *dp) 1159{ 1160 /* Repair zapped this dir's data fork a short time ago */ 1161 if (xfs_ifork_zapped(dp, XFS_DATA_FORK)) 1162 return true; 1163 1164 /* 1165 * If the dinode repair found a bad data fork, it will reset the fork 1166 * to extents format with zero records and wait for the bmapbtd 1167 * scrubber to reconstruct the block mappings. Directories always 1168 * contain some content, so this is a clear sign of a zapped directory. 1169 * The state checked by xfs_ifork_zapped is not persisted, so this is 1170 * the secondary strategy if repairs are interrupted by a crash or an 1171 * unmount. 1172 */ 1173 return dp->i_df.if_format == XFS_DINODE_FMT_EXTENTS && 1174 dp->i_df.if_nextents == 0; 1175} 1176