938 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2017-2023 Oracle. All Rights Reserved.4 * Author: Darrick J. Wong <djwong@kernel.org>5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_shared.h"9#include "xfs_format.h"10#include "xfs_trans_resv.h"11#include "xfs_mount.h"12#include "xfs_log_format.h"13#include "xfs_trans.h"14#include "xfs_inode.h"15#include "xfs_icache.h"16#include "xfs_dir2.h"17#include "xfs_dir2_priv.h"18#include "xfs_attr.h"19#include "xfs_parent.h"20#include "scrub/scrub.h"21#include "scrub/common.h"22#include "scrub/readdir.h"23#include "scrub/tempfile.h"24#include "scrub/repair.h"25#include "scrub/listxattr.h"26#include "scrub/xfile.h"27#include "scrub/xfarray.h"28#include "scrub/xfblob.h"29#include "scrub/trace.h"30 31/* Set us up to scrub parents. */32int33xchk_setup_parent(34 struct xfs_scrub *sc)35{36 int error;37 38 if (xchk_could_repair(sc)) {39 error = xrep_setup_parent(sc);40 if (error)41 return error;42 }43 44 return xchk_setup_inode_contents(sc, 0);45}46 47/* Parent pointers */48 49/* Look for an entry in a parent pointing to this inode. */50 51struct xchk_parent_ctx {52 struct xfs_scrub *sc;53 xfs_nlink_t nlink;54};55 56/* Look for a single entry in a directory pointing to an inode. */57STATIC int58xchk_parent_actor(59 struct xfs_scrub *sc,60 struct xfs_inode *dp,61 xfs_dir2_dataptr_t dapos,62 const struct xfs_name *name,63 xfs_ino_t ino,64 void *priv)65{66 struct xchk_parent_ctx *spc = priv;67 int error = 0;68 69 /* Does this name make sense? */70 if (!xfs_dir2_namecheck(name->name, name->len))71 error = -EFSCORRUPTED;72 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))73 return error;74 75 if (sc->ip->i_ino == ino)76 spc->nlink++;77 78 if (xchk_should_terminate(spc->sc, &error))79 return error;80 81 return 0;82}83 84/*85 * Try to lock a parent directory for checking dirents. Returns the inode86 * flags for the locks we now hold, or zero if we failed.87 */88STATIC unsigned int89xchk_parent_ilock_dir(90 struct xfs_inode *dp)91{92 if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED))93 return 0;94 95 if (!xfs_need_iread_extents(&dp->i_df))96 return XFS_ILOCK_SHARED;97 98 xfs_iunlock(dp, XFS_ILOCK_SHARED);99 100 if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL))101 return 0;102 103 return XFS_ILOCK_EXCL;104}105 106/*107 * Given the inode number of the alleged parent of the inode being scrubbed,108 * try to validate that the parent has exactly one directory entry pointing109 * back to the inode being scrubbed. Returns -EAGAIN if we need to revalidate110 * the dotdot entry.111 */112STATIC int113xchk_parent_validate(114 struct xfs_scrub *sc,115 xfs_ino_t parent_ino)116{117 struct xchk_parent_ctx spc = {118 .sc = sc,119 .nlink = 0,120 };121 struct xfs_mount *mp = sc->mp;122 struct xfs_inode *dp = NULL;123 xfs_nlink_t expected_nlink;124 unsigned int lock_mode;125 int error = 0;126 127 /* Is this the root dir? Then '..' must point to itself. */128 if (sc->ip == mp->m_rootip) {129 if (sc->ip->i_ino != mp->m_sb.sb_rootino ||130 sc->ip->i_ino != parent_ino)131 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);132 return 0;133 }134 135 /* '..' must not point to ourselves. */136 if (sc->ip->i_ino == parent_ino) {137 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);138 return 0;139 }140 141 /*142 * If we're an unlinked directory, the parent /won't/ have a link143 * to us. Otherwise, it should have one link.144 */145 expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;146 147 /*148 * Grab the parent directory inode. This must be released before we149 * cancel the scrub transaction.150 *151 * If _iget returns -EINVAL or -ENOENT then the parent inode number is152 * garbage and the directory is corrupt. If the _iget returns153 * -EFSCORRUPTED or -EFSBADCRC then the parent is corrupt which is a154 * cross referencing error. Any other error is an operational error.155 */156 error = xchk_iget(sc, parent_ino, &dp);157 if (error == -EINVAL || error == -ENOENT) {158 error = -EFSCORRUPTED;159 xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);160 return error;161 }162 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))163 return error;164 if (dp == sc->ip || xrep_is_tempfile(dp) ||165 !S_ISDIR(VFS_I(dp)->i_mode)) {166 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);167 goto out_rele;168 }169 170 lock_mode = xchk_parent_ilock_dir(dp);171 if (!lock_mode) {172 xchk_iunlock(sc, XFS_ILOCK_EXCL);173 xchk_ilock(sc, XFS_ILOCK_EXCL);174 error = -EAGAIN;175 goto out_rele;176 }177 178 /*179 * We cannot yet validate this parent pointer if the directory looks as180 * though it has been zapped by the inode record repair code.181 */182 if (xchk_dir_looks_zapped(dp)) {183 error = -EBUSY;184 xchk_set_incomplete(sc);185 goto out_unlock;186 }187 188 /* Look for a directory entry in the parent pointing to the child. */189 error = xchk_dir_walk(sc, dp, xchk_parent_actor, &spc);190 if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))191 goto out_unlock;192 193 /*194 * Ensure that the parent has as many links to the child as the child195 * thinks it has to the parent.196 */197 if (spc.nlink != expected_nlink)198 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);199 200out_unlock:201 xfs_iunlock(dp, lock_mode);202out_rele:203 xchk_irele(sc, dp);204 return error;205}206 207/*208 * Checking of Parent Pointers209 * ===========================210 *211 * On filesystems with directory parent pointers, we check the referential212 * integrity by visiting each parent pointer of a child file and checking that213 * the directory referenced by the pointer actually has a dirent pointing214 * forward to the child file.215 */216 217/* Deferred parent pointer entry that we saved for later. */218struct xchk_pptr {219 /* Cookie for retrieval of the pptr name. */220 xfblob_cookie name_cookie;221 222 /* Parent pointer record. */223 struct xfs_parent_rec pptr_rec;224 225 /* Length of the pptr name. */226 uint8_t namelen;227};228 229struct xchk_pptrs {230 struct xfs_scrub *sc;231 232 /* How many parent pointers did we find at the end? */233 unsigned long long pptrs_found;234 235 /* Parent of this directory. */236 xfs_ino_t parent_ino;237 238 /* Fixed-size array of xchk_pptr structures. */239 struct xfarray *pptr_entries;240 241 /* Blobs containing parent pointer names. */242 struct xfblob *pptr_names;243 244 /* Scratch buffer for scanning pptr xattrs */245 struct xfs_da_args pptr_args;246 247 /* If we've cycled the ILOCK, we must revalidate all deferred pptrs. */248 bool need_revalidate;249 250 /* Name buffer */251 struct xfs_name xname;252 char namebuf[MAXNAMELEN];253};254 255/* Does this parent pointer match the dotdot entry? */256STATIC int257xchk_parent_scan_dotdot(258 struct xfs_scrub *sc,259 struct xfs_inode *ip,260 unsigned int attr_flags,261 const unsigned char *name,262 unsigned int namelen,263 const void *value,264 unsigned int valuelen,265 void *priv)266{267 struct xchk_pptrs *pp = priv;268 xfs_ino_t parent_ino;269 int error;270 271 if (!(attr_flags & XFS_ATTR_PARENT))272 return 0;273 274 error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,275 valuelen, &parent_ino, NULL);276 if (error)277 return error;278 279 if (pp->parent_ino == parent_ino)280 return -ECANCELED;281 282 return 0;283}284 285/* Look up the dotdot entry so that we can check it as we walk the pptrs. */286STATIC int287xchk_parent_pptr_and_dotdot(288 struct xchk_pptrs *pp)289{290 struct xfs_scrub *sc = pp->sc;291 int error;292 293 /* Look up '..' */294 error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &pp->parent_ino);295 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))296 return error;297 if (!xfs_verify_dir_ino(sc->mp, pp->parent_ino)) {298 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);299 return 0;300 }301 302 /* Is this the root dir? Then '..' must point to itself. */303 if (sc->ip == sc->mp->m_rootip) {304 if (sc->ip->i_ino != pp->parent_ino)305 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);306 return 0;307 }308 309 /*310 * If this is now an unlinked directory, the dotdot value is311 * meaningless as long as it points to a valid inode.312 */313 if (VFS_I(sc->ip)->i_nlink == 0)314 return 0;315 316 if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)317 return 0;318 319 /* Otherwise, walk the pptrs again, and check. */320 error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_dotdot, NULL, pp);321 if (error == -ECANCELED) {322 /* Found a parent pointer that matches dotdot. */323 return 0;324 }325 if (!error || error == -EFSCORRUPTED) {326 /* Found a broken parent pointer or no match. */327 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);328 return 0;329 }330 return error;331}332 333/*334 * Try to lock a parent directory for checking dirents. Returns the inode335 * flags for the locks we now hold, or zero if we failed.336 */337STATIC unsigned int338xchk_parent_lock_dir(339 struct xfs_scrub *sc,340 struct xfs_inode *dp)341{342 if (!xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED))343 return 0;344 345 if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED)) {346 xfs_iunlock(dp, XFS_IOLOCK_SHARED);347 return 0;348 }349 350 if (!xfs_need_iread_extents(&dp->i_df))351 return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED;352 353 xfs_iunlock(dp, XFS_ILOCK_SHARED);354 355 if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {356 xfs_iunlock(dp, XFS_IOLOCK_SHARED);357 return 0;358 }359 360 return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;361}362 363/* Check the forward link (dirent) associated with this parent pointer. */364STATIC int365xchk_parent_dirent(366 struct xchk_pptrs *pp,367 const struct xfs_name *xname,368 struct xfs_inode *dp)369{370 struct xfs_scrub *sc = pp->sc;371 xfs_ino_t child_ino;372 int error;373 374 /*375 * Use the name attached to this parent pointer to look up the376 * directory entry in the alleged parent.377 */378 error = xchk_dir_lookup(sc, dp, xname, &child_ino);379 if (error == -ENOENT) {380 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);381 return 0;382 }383 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))384 return error;385 386 /* Does the inode number match? */387 if (child_ino != sc->ip->i_ino) {388 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);389 return 0;390 }391 392 return 0;393}394 395/* Try to grab a parent directory. */396STATIC int397xchk_parent_iget(398 struct xchk_pptrs *pp,399 const struct xfs_parent_rec *pptr,400 struct xfs_inode **dpp)401{402 struct xfs_scrub *sc = pp->sc;403 struct xfs_inode *ip;404 xfs_ino_t parent_ino = be64_to_cpu(pptr->p_ino);405 int error;406 407 /* Validate inode number. */408 error = xfs_dir_ino_validate(sc->mp, parent_ino);409 if (error) {410 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);411 return -ECANCELED;412 }413 414 error = xchk_iget(sc, parent_ino, &ip);415 if (error == -EINVAL || error == -ENOENT) {416 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);417 return -ECANCELED;418 }419 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))420 return error;421 422 /* The parent must be a directory. */423 if (!S_ISDIR(VFS_I(ip)->i_mode)) {424 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);425 goto out_rele;426 }427 428 /* Validate generation number. */429 if (VFS_I(ip)->i_generation != be32_to_cpu(pptr->p_gen)) {430 xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);431 goto out_rele;432 }433 434 *dpp = ip;435 return 0;436out_rele:437 xchk_irele(sc, ip);438 return 0;439}440 441/*442 * Walk an xattr of a file. If this xattr is a parent pointer, follow it up443 * to a parent directory and check that the parent has a dirent pointing back444 * to us.445 */446STATIC int447xchk_parent_scan_attr(448 struct xfs_scrub *sc,449 struct xfs_inode *ip,450 unsigned int attr_flags,451 const unsigned char *name,452 unsigned int namelen,453 const void *value,454 unsigned int valuelen,455 void *priv)456{457 struct xfs_name xname = {458 .name = name,459 .len = namelen,460 };461 struct xchk_pptrs *pp = priv;462 struct xfs_inode *dp = NULL;463 const struct xfs_parent_rec *pptr_rec = value;464 xfs_ino_t parent_ino;465 unsigned int lockmode;466 int error;467 468 if (!(attr_flags & XFS_ATTR_PARENT))469 return 0;470 471 error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,472 valuelen, &parent_ino, NULL);473 if (error) {474 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);475 return error;476 }477 478 /* No self-referential parent pointers. */479 if (parent_ino == sc->ip->i_ino) {480 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);481 return -ECANCELED;482 }483 484 pp->pptrs_found++;485 486 error = xchk_parent_iget(pp, pptr_rec, &dp);487 if (error)488 return error;489 if (!dp)490 return 0;491 492 /* Try to lock the inode. */493 lockmode = xchk_parent_lock_dir(sc, dp);494 if (!lockmode) {495 struct xchk_pptr save_pp = {496 .pptr_rec = *pptr_rec, /* struct copy */497 .namelen = namelen,498 };499 500 /* Couldn't lock the inode, so save the pptr for later. */501 trace_xchk_parent_defer(sc->ip, &xname, dp->i_ino);502 503 error = xfblob_storename(pp->pptr_names, &save_pp.name_cookie,504 &xname);505 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,506 &error))507 goto out_rele;508 509 error = xfarray_append(pp->pptr_entries, &save_pp);510 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,511 &error))512 goto out_rele;513 514 goto out_rele;515 }516 517 error = xchk_parent_dirent(pp, &xname, dp);518 if (error)519 goto out_unlock;520 521out_unlock:522 xfs_iunlock(dp, lockmode);523out_rele:524 xchk_irele(sc, dp);525 return error;526}527 528/*529 * Revalidate a parent pointer that we collected in the past but couldn't check530 * because of lock contention. Returns 0 if the parent pointer is still valid,531 * -ENOENT if it has gone away on us, or a negative errno.532 */533STATIC int534xchk_parent_revalidate_pptr(535 struct xchk_pptrs *pp,536 const struct xfs_name *xname,537 struct xfs_parent_rec *pptr)538{539 struct xfs_scrub *sc = pp->sc;540 int error;541 542 error = xfs_parent_lookup(sc->tp, sc->ip, xname, pptr, &pp->pptr_args);543 if (error == -ENOATTR) {544 /* Parent pointer went away, nothing to revalidate. */545 return -ENOENT;546 }547 548 return error;549}550 551/*552 * Check a parent pointer the slow way, which means we cycle locks a bunch553 * and put up with revalidation until we get it done.554 */555STATIC int556xchk_parent_slow_pptr(557 struct xchk_pptrs *pp,558 const struct xfs_name *xname,559 struct xfs_parent_rec *pptr)560{561 struct xfs_scrub *sc = pp->sc;562 struct xfs_inode *dp = NULL;563 unsigned int lockmode;564 int error;565 566 /* Check that the deferred parent pointer still exists. */567 if (pp->need_revalidate) {568 error = xchk_parent_revalidate_pptr(pp, xname, pptr);569 if (error == -ENOENT)570 return 0;571 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,572 &error))573 return error;574 }575 576 error = xchk_parent_iget(pp, pptr, &dp);577 if (error)578 return error;579 if (!dp)580 return 0;581 582 /*583 * If we can grab both IOLOCK and ILOCK of the alleged parent, we584 * can proceed with the validation.585 */586 lockmode = xchk_parent_lock_dir(sc, dp);587 if (lockmode) {588 trace_xchk_parent_slowpath(sc->ip, xname, dp->i_ino);589 goto check_dirent;590 }591 592 /*593 * We couldn't lock the parent dir. Drop all the locks and try to594 * get them again, one at a time.595 */596 xchk_iunlock(sc, sc->ilock_flags);597 pp->need_revalidate = true;598 599 trace_xchk_parent_ultraslowpath(sc->ip, xname, dp->i_ino);600 601 error = xchk_dir_trylock_for_pptrs(sc, dp, &lockmode);602 if (error)603 goto out_rele;604 605 /* Revalidate the parent pointer now that we cycled locks. */606 error = xchk_parent_revalidate_pptr(pp, xname, pptr);607 if (error == -ENOENT) {608 error = 0;609 goto out_unlock;610 }611 if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))612 goto out_unlock;613 614check_dirent:615 error = xchk_parent_dirent(pp, xname, dp);616out_unlock:617 xfs_iunlock(dp, lockmode);618out_rele:619 xchk_irele(sc, dp);620 return error;621}622 623/* Check all the parent pointers that we deferred the first time around. */624STATIC int625xchk_parent_finish_slow_pptrs(626 struct xchk_pptrs *pp)627{628 xfarray_idx_t array_cur;629 int error;630 631 foreach_xfarray_idx(pp->pptr_entries, array_cur) {632 struct xchk_pptr pptr;633 634 if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)635 return 0;636 637 error = xfarray_load(pp->pptr_entries, array_cur, &pptr);638 if (error)639 return error;640 641 error = xfblob_loadname(pp->pptr_names, pptr.name_cookie,642 &pp->xname, pptr.namelen);643 if (error)644 return error;645 646 error = xchk_parent_slow_pptr(pp, &pp->xname, &pptr.pptr_rec);647 if (error)648 return error;649 }650 651 /* Empty out both xfiles now that we've checked everything. */652 xfarray_truncate(pp->pptr_entries);653 xfblob_truncate(pp->pptr_names);654 return 0;655}656 657/* Count the number of parent pointers. */658STATIC int659xchk_parent_count_pptr(660 struct xfs_scrub *sc,661 struct xfs_inode *ip,662 unsigned int attr_flags,663 const unsigned char *name,664 unsigned int namelen,665 const void *value,666 unsigned int valuelen,667 void *priv)668{669 struct xchk_pptrs *pp = priv;670 int error;671 672 if (!(attr_flags & XFS_ATTR_PARENT))673 return 0;674 675 error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,676 valuelen, NULL, NULL);677 if (error)678 return error;679 680 pp->pptrs_found++;681 return 0;682}683 684/*685 * Compare the number of parent pointers to the link count. For686 * non-directories these should be the same. For unlinked directories the687 * count should be zero; for linked directories, it should be nonzero.688 */689STATIC int690xchk_parent_count_pptrs(691 struct xchk_pptrs *pp)692{693 struct xfs_scrub *sc = pp->sc;694 int error;695 696 /*697 * If we cycled the ILOCK while cross-checking parent pointers with698 * dirents, then we need to recalculate the number of parent pointers.699 */700 if (pp->need_revalidate) {701 pp->pptrs_found = 0;702 error = xchk_xattr_walk(sc, sc->ip, xchk_parent_count_pptr,703 NULL, pp);704 if (error == -EFSCORRUPTED) {705 /* Found a bad parent pointer */706 xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);707 return 0;708 }709 if (error)710 return error;711 }712 713 if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {714 if (sc->ip == sc->mp->m_rootip)715 pp->pptrs_found++;716 717 if (VFS_I(sc->ip)->i_nlink == 0 && pp->pptrs_found > 0)718 xchk_ino_set_corrupt(sc, sc->ip->i_ino);719 else if (VFS_I(sc->ip)->i_nlink > 0 &&720 pp->pptrs_found == 0)721 xchk_ino_set_corrupt(sc, sc->ip->i_ino);722 } else {723 if (VFS_I(sc->ip)->i_nlink != pp->pptrs_found)724 xchk_ino_set_corrupt(sc, sc->ip->i_ino);725 }726 727 return 0;728}729 730/* Check parent pointers of a file. */731STATIC int732xchk_parent_pptr(733 struct xfs_scrub *sc)734{735 struct xchk_pptrs *pp;736 char *descr;737 int error;738 739 pp = kvzalloc(sizeof(struct xchk_pptrs), XCHK_GFP_FLAGS);740 if (!pp)741 return -ENOMEM;742 pp->sc = sc;743 pp->xname.name = pp->namebuf;744 745 /*746 * Set up some staging memory for parent pointers that we can't check747 * due to locking contention.748 */749 descr = xchk_xfile_ino_descr(sc, "slow parent pointer entries");750 error = xfarray_create(descr, 0, sizeof(struct xchk_pptr),751 &pp->pptr_entries);752 kfree(descr);753 if (error)754 goto out_pp;755 756 descr = xchk_xfile_ino_descr(sc, "slow parent pointer names");757 error = xfblob_create(descr, &pp->pptr_names);758 kfree(descr);759 if (error)760 goto out_entries;761 762 error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, NULL, pp);763 if (error == -ECANCELED) {764 error = 0;765 goto out_names;766 }767 if (error)768 goto out_names;769 770 error = xchk_parent_finish_slow_pptrs(pp);771 if (error == -ETIMEDOUT) {772 /* Couldn't grab a lock, scrub was marked incomplete */773 error = 0;774 goto out_names;775 }776 if (error)777 goto out_names;778 779 if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)780 goto out_names;781 782 /*783 * For subdirectories, make sure the dotdot entry references the same784 * inode as the parent pointers.785 *786 * If we're scanning a /consistent/ directory, there should only be787 * one parent pointer, and it should point to the same directory as788 * the dotdot entry.789 *790 * However, a corrupt directory tree might feature a subdirectory with791 * multiple parents. The directory loop scanner is responsible for792 * correcting that kind of problem, so for now we only validate that793 * the dotdot entry matches /one/ of the parents.794 */795 if (S_ISDIR(VFS_I(sc->ip)->i_mode)) {796 error = xchk_parent_pptr_and_dotdot(pp);797 if (error)798 goto out_names;799 }800 801 if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)802 goto out_names;803 804 /*805 * Complain if the number of parent pointers doesn't match the link806 * count. This could be a sign of missing parent pointers (or an807 * incorrect link count).808 */809 error = xchk_parent_count_pptrs(pp);810 if (error)811 goto out_names;812 813out_names:814 xfblob_destroy(pp->pptr_names);815out_entries:816 xfarray_destroy(pp->pptr_entries);817out_pp:818 kvfree(pp);819 return error;820}821 822/* Scrub a parent pointer. */823int824xchk_parent(825 struct xfs_scrub *sc)826{827 struct xfs_mount *mp = sc->mp;828 xfs_ino_t parent_ino;829 int error = 0;830 831 if (xfs_has_parent(mp))832 return xchk_parent_pptr(sc);833 834 /*835 * If we're a directory, check that the '..' link points up to836 * a directory that has one entry pointing to us.837 */838 if (!S_ISDIR(VFS_I(sc->ip)->i_mode))839 return -ENOENT;840 841 /* We're not a special inode, are we? */842 if (!xfs_verify_dir_ino(mp, sc->ip->i_ino)) {843 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);844 return 0;845 }846 847 do {848 if (xchk_should_terminate(sc, &error))849 break;850 851 /* Look up '..' */852 error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot,853 &parent_ino);854 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))855 return error;856 if (!xfs_verify_dir_ino(mp, parent_ino)) {857 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);858 return 0;859 }860 861 /*862 * Check that the dotdot entry points to a parent directory863 * containing a dirent pointing to this subdirectory.864 */865 error = xchk_parent_validate(sc, parent_ino);866 } while (error == -EAGAIN);867 if (error == -EBUSY) {868 /*869 * We could not scan a directory, so we marked the check870 * incomplete. No further error return is necessary.871 */872 return 0;873 }874 875 return error;876}877 878/*879 * Decide if this file's extended attributes (and therefore its parent880 * pointers) have been zapped to satisfy the inode and ifork verifiers.881 * Checking and repairing should be postponed until the extended attribute882 * structure is fixed.883 */884bool885xchk_pptr_looks_zapped(886 struct xfs_inode *ip)887{888 struct xfs_mount *mp = ip->i_mount;889 struct inode *inode = VFS_I(ip);890 891 ASSERT(xfs_has_parent(mp));892 893 /*894 * Temporary files that cannot be linked into the directory tree do not895 * have attr forks because they cannot ever have parents.896 */897 if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))898 return false;899 900 /*901 * Directory tree roots do not have parents, so the expected outcome902 * of a parent pointer scan is always the empty set. It's safe to scan903 * them even if the attr fork was zapped.904 */905 if (ip == mp->m_rootip)906 return false;907 908 /*909 * Metadata inodes are all rooted in the superblock and do not have910 * any parents. Hence the attr fork will not be initialized, but911 * there are no parent pointers that might have been zapped.912 */913 if (xfs_is_metadata_inode(ip))914 return false;915 916 /*917 * Linked and linkable non-rootdir files should always have an918 * attribute fork because that is where parent pointers are919 * stored. If the fork is absent, something is amiss.920 */921 if (!xfs_inode_has_attr_fork(ip))922 return true;923 924 /* Repair zapped this file's attr fork a short time ago */925 if (xfs_ifork_zapped(ip, XFS_ATTR_FORK))926 return true;927 928 /*929 * If the dinode repair found a bad attr fork, it will reset the fork930 * to extents format with zero records and wait for the bmapbta931 * scrubber to reconstruct the block mappings. The extended attribute932 * structure always contain some content when parent pointers are933 * enabled, so this is a clear sign of a zapped attr fork.934 */935 return ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&936 ip->i_af.if_nextents == 0;937}938