822 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2023-2024 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_trans_space.h"12#include "xfs_mount.h"13#include "xfs_log_format.h"14#include "xfs_trans.h"15#include "xfs_inode.h"16#include "xfs_icache.h"17#include "xfs_dir2.h"18#include "xfs_dir2_priv.h"19#include "xfs_attr.h"20#include "xfs_parent.h"21#include "scrub/scrub.h"22#include "scrub/common.h"23#include "scrub/bitmap.h"24#include "scrub/ino_bitmap.h"25#include "scrub/xfile.h"26#include "scrub/xfarray.h"27#include "scrub/xfblob.h"28#include "scrub/listxattr.h"29#include "scrub/trace.h"30#include "scrub/repair.h"31#include "scrub/orphanage.h"32#include "scrub/dirtree.h"33#include "scrub/readdir.h"34 35/*36 * Directory Tree Structure Repairs37 * ================================38 *39 * If we decide that the directory being scanned is participating in a40 * directory loop, the only change we can make is to remove directory entries41 * pointing down to @sc->ip. If that leaves it with no parents, the directory42 * should be adopted by the orphanage.43 */44 45/* Set up to repair directory loops. */46int47xrep_setup_dirtree(48 struct xfs_scrub *sc)49{50 return xrep_orphanage_try_create(sc);51}52 53/* Change the outcome of this path. */54static inline void55xrep_dirpath_set_outcome(56 struct xchk_dirtree *dl,57 struct xchk_dirpath *path,58 enum xchk_dirpath_outcome outcome)59{60 trace_xrep_dirpath_set_outcome(dl->sc, path->path_nr, path->nr_steps,61 outcome);62 63 path->outcome = outcome;64}65 66/* Delete all paths. */67STATIC void68xrep_dirtree_delete_all_paths(69 struct xchk_dirtree *dl,70 struct xchk_dirtree_outcomes *oc)71{72 struct xchk_dirpath *path;73 74 xchk_dirtree_for_each_path(dl, path) {75 switch (path->outcome) {76 case XCHK_DIRPATH_CORRUPT:77 case XCHK_DIRPATH_LOOP:78 oc->suspect--;79 oc->bad++;80 xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);81 break;82 case XCHK_DIRPATH_OK:83 oc->good--;84 oc->bad++;85 xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);86 break;87 default:88 break;89 }90 }91 92 ASSERT(oc->suspect == 0);93 ASSERT(oc->good == 0);94}95 96/* Since this is the surviving path, set the dotdot entry to this value. */97STATIC void98xrep_dirpath_retain_parent(99 struct xchk_dirtree *dl,100 struct xchk_dirpath *path)101{102 struct xchk_dirpath_step step;103 int error;104 105 error = xfarray_load(dl->path_steps, path->first_step, &step);106 if (error)107 return;108 109 dl->parent_ino = be64_to_cpu(step.pptr_rec.p_ino);110}111 112/* Find the one surviving path so we know how to set dotdot. */113STATIC void114xrep_dirtree_find_surviving_path(115 struct xchk_dirtree *dl,116 struct xchk_dirtree_outcomes *oc)117{118 struct xchk_dirpath *path;119 bool foundit = false;120 121 xchk_dirtree_for_each_path(dl, path) {122 switch (path->outcome) {123 case XCHK_DIRPATH_CORRUPT:124 case XCHK_DIRPATH_LOOP:125 case XCHK_DIRPATH_OK:126 if (!foundit) {127 xrep_dirpath_retain_parent(dl, path);128 foundit = true;129 continue;130 }131 ASSERT(foundit == false);132 break;133 default:134 break;135 }136 }137 138 ASSERT(oc->suspect + oc->good == 1);139}140 141/* Delete all paths except for the one good one. */142STATIC void143xrep_dirtree_keep_one_good_path(144 struct xchk_dirtree *dl,145 struct xchk_dirtree_outcomes *oc)146{147 struct xchk_dirpath *path;148 bool foundit = false;149 150 xchk_dirtree_for_each_path(dl, path) {151 switch (path->outcome) {152 case XCHK_DIRPATH_CORRUPT:153 case XCHK_DIRPATH_LOOP:154 oc->suspect--;155 oc->bad++;156 xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);157 break;158 case XCHK_DIRPATH_OK:159 if (!foundit) {160 xrep_dirpath_retain_parent(dl, path);161 foundit = true;162 continue;163 }164 oc->good--;165 oc->bad++;166 xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);167 break;168 default:169 break;170 }171 }172 173 ASSERT(oc->suspect == 0);174 ASSERT(oc->good < 2);175}176 177/* Delete all paths except for one suspect one. */178STATIC void179xrep_dirtree_keep_one_suspect_path(180 struct xchk_dirtree *dl,181 struct xchk_dirtree_outcomes *oc)182{183 struct xchk_dirpath *path;184 bool foundit = false;185 186 xchk_dirtree_for_each_path(dl, path) {187 switch (path->outcome) {188 case XCHK_DIRPATH_CORRUPT:189 case XCHK_DIRPATH_LOOP:190 if (!foundit) {191 xrep_dirpath_retain_parent(dl, path);192 foundit = true;193 continue;194 }195 oc->suspect--;196 oc->bad++;197 xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);198 break;199 case XCHK_DIRPATH_OK:200 ASSERT(0);201 break;202 default:203 break;204 }205 }206 207 ASSERT(oc->suspect == 1);208 ASSERT(oc->good == 0);209}210 211/*212 * Figure out what to do with the paths we tried to find. Returns -EDEADLOCK213 * if the scan results have become stale.214 */215STATIC void216xrep_dirtree_decide_fate(217 struct xchk_dirtree *dl,218 struct xchk_dirtree_outcomes *oc)219{220 xchk_dirtree_evaluate(dl, oc);221 222 /* Parentless directories should not have any paths at all. */223 if (xchk_dirtree_parentless(dl)) {224 xrep_dirtree_delete_all_paths(dl, oc);225 return;226 }227 228 /* One path is exactly the number of paths we want. */229 if (oc->good + oc->suspect == 1) {230 xrep_dirtree_find_surviving_path(dl, oc);231 return;232 }233 234 /* Zero paths means we should reattach the subdir to the orphanage. */235 if (oc->good + oc->suspect == 0) {236 if (dl->sc->orphanage)237 oc->needs_adoption = true;238 return;239 }240 241 /*242 * Otherwise, this subdirectory has too many parents. If there's at243 * least one good path, keep it and delete the others.244 */245 if (oc->good > 0) {246 xrep_dirtree_keep_one_good_path(dl, oc);247 return;248 }249 250 /*251 * There are no good paths and there are too many suspect paths.252 * Keep the first suspect path and delete the rest.253 */254 xrep_dirtree_keep_one_suspect_path(dl, oc);255}256 257/*258 * Load the first step of this path into @step and @dl->xname/pptr259 * for later repair work.260 */261STATIC int262xrep_dirtree_prep_path(263 struct xchk_dirtree *dl,264 struct xchk_dirpath *path,265 struct xchk_dirpath_step *step)266{267 int error;268 269 error = xfarray_load(dl->path_steps, path->first_step, step);270 if (error)271 return error;272 273 error = xfblob_loadname(dl->path_names, step->name_cookie, &dl->xname,274 step->name_len);275 if (error)276 return error;277 278 dl->pptr_rec = step->pptr_rec; /* struct copy */279 return 0;280}281 282/* Delete the VFS dentry for a removed child. */283STATIC int284xrep_dirtree_purge_dentry(285 struct xchk_dirtree *dl,286 struct xfs_inode *dp,287 const struct xfs_name *name)288{289 struct qstr qname = QSTR_INIT(name->name, name->len);290 struct dentry *parent_dentry, *child_dentry;291 int error = 0;292 293 /*294 * Find the dentry for the parent directory. If there isn't one, we're295 * done. Caller already holds i_rwsem for parent and child.296 */297 parent_dentry = d_find_alias(VFS_I(dp));298 if (!parent_dentry)299 return 0;300 301 /* The VFS thinks the parent is a directory, right? */302 if (!d_is_dir(parent_dentry)) {303 ASSERT(d_is_dir(parent_dentry));304 error = -EFSCORRUPTED;305 goto out_dput_parent;306 }307 308 /*309 * Try to find the dirent pointing to the child. If there isn't one,310 * we're done.311 */312 qname.hash = full_name_hash(parent_dentry, name->name, name->len);313 child_dentry = d_lookup(parent_dentry, &qname);314 if (!child_dentry) {315 error = 0;316 goto out_dput_parent;317 }318 319 trace_xrep_dirtree_delete_child(dp->i_mount, child_dentry);320 321 /* Child is not a directory? We're screwed. */322 if (!d_is_dir(child_dentry)) {323 ASSERT(d_is_dir(child_dentry));324 error = -EFSCORRUPTED;325 goto out_dput_child;326 }327 328 /* Replace the child dentry with a negative one. */329 d_delete(child_dentry);330 331out_dput_child:332 dput(child_dentry);333out_dput_parent:334 dput(parent_dentry);335 return error;336}337 338/*339 * Prepare to delete a link by taking the IOLOCK of the parent and the child340 * (scrub target). Caller must hold IOLOCK_EXCL on @sc->ip. Returns 0 if we341 * took both locks, or a negative errno if we couldn't lock the parent in time.342 */343static inline int344xrep_dirtree_unlink_iolock(345 struct xfs_scrub *sc,346 struct xfs_inode *dp)347{348 int error;349 350 ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);351 352 if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL))353 return 0;354 355 xchk_iunlock(sc, XFS_IOLOCK_EXCL);356 do {357 xfs_ilock(dp, XFS_IOLOCK_EXCL);358 if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))359 break;360 xfs_iunlock(dp, XFS_IOLOCK_EXCL);361 362 if (xchk_should_terminate(sc, &error)) {363 xchk_ilock(sc, XFS_IOLOCK_EXCL);364 return error;365 }366 367 delay(1);368 } while (1);369 370 return 0;371}372 373/*374 * Remove a link from the directory tree and update the dcache. Returns375 * -ESTALE if the scan data are now out of date.376 */377STATIC int378xrep_dirtree_unlink(379 struct xchk_dirtree *dl,380 struct xfs_inode *dp,381 struct xchk_dirpath *path,382 struct xchk_dirpath_step *step)383{384 struct xfs_scrub *sc = dl->sc;385 struct xfs_mount *mp = sc->mp;386 xfs_ino_t dotdot_ino;387 xfs_ino_t parent_ino = dl->parent_ino;388 unsigned int resblks;389 int dontcare;390 int error;391 392 /* Take IOLOCK_EXCL of the parent and child. */393 error = xrep_dirtree_unlink_iolock(sc, dp);394 if (error)395 return error;396 397 /*398 * Create the transaction that we need to sever the path. Ignore399 * EDQUOT and ENOSPC being returned via nospace_error because the400 * directory code can handle a reservationless update.401 */402 resblks = xfs_remove_space_res(mp, step->name_len);403 error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip,404 &resblks, &sc->tp, &dontcare);405 if (error)406 goto out_iolock;407 408 /*409 * Cancel if someone invalidate the paths while we were trying to get410 * the ILOCK.411 */412 mutex_lock(&dl->lock);413 if (dl->stale) {414 mutex_unlock(&dl->lock);415 error = -ESTALE;416 goto out_trans_cancel;417 }418 xrep_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETING);419 mutex_unlock(&dl->lock);420 421 trace_xrep_dirtree_delete_path(dl->sc, sc->ip, path->path_nr,422 &dl->xname, &dl->pptr_rec);423 424 /*425 * Decide if we need to reset the dotdot entry. Rules:426 *427 * - If there's a surviving parent, we want dotdot to point there.428 * - If we don't have any surviving parents, then point dotdot at the429 * root dir.430 * - If dotdot is already set to the value we want, pass in NULLFSINO431 * for no change necessary.432 *433 * Do this /before/ we dirty anything, in case the dotdot lookup434 * fails.435 */436 error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &dotdot_ino);437 if (error)438 goto out_trans_cancel;439 if (parent_ino == NULLFSINO)440 parent_ino = dl->root_ino;441 if (dotdot_ino == parent_ino)442 parent_ino = NULLFSINO;443 444 /* Drop the link from sc->ip's dotdot entry. */445 error = xfs_droplink(sc->tp, dp);446 if (error)447 goto out_trans_cancel;448 449 /* Reset the dotdot entry to a surviving parent. */450 if (parent_ino != NULLFSINO) {451 error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,452 parent_ino, 0);453 if (error)454 goto out_trans_cancel;455 }456 457 /* Drop the link from dp to sc->ip. */458 error = xfs_droplink(sc->tp, sc->ip);459 if (error)460 goto out_trans_cancel;461 462 error = xfs_dir_removename(sc->tp, dp, &dl->xname, sc->ip->i_ino,463 resblks);464 if (error) {465 ASSERT(error != -ENOENT);466 goto out_trans_cancel;467 }468 469 if (xfs_has_parent(sc->mp)) {470 error = xfs_parent_removename(sc->tp, &dl->ppargs, dp,471 &dl->xname, sc->ip);472 if (error)473 goto out_trans_cancel;474 }475 476 /*477 * Notify dirent hooks that we removed the bad link, invalidate the478 * dcache, and commit the repair.479 */480 xfs_dir_update_hook(dp, sc->ip, -1, &dl->xname);481 error = xrep_dirtree_purge_dentry(dl, dp, &dl->xname);482 if (error)483 goto out_trans_cancel;484 485 error = xrep_trans_commit(sc);486 goto out_ilock;487 488out_trans_cancel:489 xchk_trans_cancel(sc);490out_ilock:491 xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);492 xfs_iunlock(dp, XFS_ILOCK_EXCL);493out_iolock:494 xfs_iunlock(dp, XFS_IOLOCK_EXCL);495 return error;496}497 498/*499 * Delete a directory entry that points to this directory. Returns -ESTALE500 * if the scan data are now out of date.501 */502STATIC int503xrep_dirtree_delete_path(504 struct xchk_dirtree *dl,505 struct xchk_dirpath *path)506{507 struct xchk_dirpath_step step;508 struct xfs_scrub *sc = dl->sc;509 struct xfs_inode *dp;510 int error;511 512 /*513 * Load the parent pointer and directory inode for this path, then514 * drop the scan lock, the ILOCK, and the transaction so that515 * _delete_path can reserve the proper transaction. This sets up516 * @dl->xname for the deletion.517 */518 error = xrep_dirtree_prep_path(dl, path, &step);519 if (error)520 return error;521 522 error = xchk_iget(sc, be64_to_cpu(step.pptr_rec.p_ino), &dp);523 if (error)524 return error;525 526 mutex_unlock(&dl->lock);527 xchk_trans_cancel(sc);528 xchk_iunlock(sc, XFS_ILOCK_EXCL);529 530 /* Delete the directory link and release the parent. */531 error = xrep_dirtree_unlink(dl, dp, path, &step);532 xchk_irele(sc, dp);533 534 /*535 * Retake all the resources we had at the beginning even if the repair536 * failed or the scan data are now stale. This keeps things simple for537 * the caller.538 */539 xchk_trans_alloc_empty(sc);540 xchk_ilock(sc, XFS_ILOCK_EXCL);541 mutex_lock(&dl->lock);542 543 if (!error && dl->stale)544 error = -ESTALE;545 return error;546}547 548/* Add a new path to represent our in-progress adoption. */549STATIC int550xrep_dirtree_create_adoption_path(551 struct xchk_dirtree *dl)552{553 struct xfs_scrub *sc = dl->sc;554 struct xchk_dirpath *path;555 int error;556 557 /*558 * We should have capped the number of paths at XFS_MAXLINK-1 in the559 * scanner.560 */561 if (dl->nr_paths > XFS_MAXLINK) {562 ASSERT(dl->nr_paths <= XFS_MAXLINK);563 return -EFSCORRUPTED;564 }565 566 /*567 * Create a new xchk_path structure to remember this parent pointer568 * and record the first name step.569 */570 path = kmalloc(sizeof(struct xchk_dirpath), XCHK_GFP_FLAGS);571 if (!path)572 return -ENOMEM;573 574 INIT_LIST_HEAD(&path->list);575 xino_bitmap_init(&path->seen_inodes);576 path->nr_steps = 0;577 path->outcome = XREP_DIRPATH_ADOPTING;578 579 /*580 * Record the new link that we just created in the orphanage. Because581 * adoption is the last repair that we perform, we don't bother filling582 * in the path all the way back to the root.583 */584 xfs_inode_to_parent_rec(&dl->pptr_rec, sc->orphanage);585 586 error = xino_bitmap_set(&path->seen_inodes, sc->orphanage->i_ino);587 if (error)588 goto out_path;589 590 trace_xrep_dirtree_create_adoption(sc, sc->ip, dl->nr_paths,591 &dl->xname, &dl->pptr_rec);592 593 error = xchk_dirpath_append(dl, sc->ip, path, &dl->xname,594 &dl->pptr_rec);595 if (error)596 goto out_path;597 598 path->first_step = xfarray_length(dl->path_steps) - 1;599 path->second_step = XFARRAY_NULLIDX;600 path->path_nr = dl->nr_paths;601 602 list_add_tail(&path->list, &dl->path_list);603 dl->nr_paths++;604 return 0;605 606out_path:607 kfree(path);608 return error;609}610 611/*612 * Prepare to move a file to the orphanage by taking the IOLOCK of the613 * orphanage and the child (scrub target). Caller must hold IOLOCK_EXCL on614 * @sc->ip. Returns 0 if we took both locks, or a negative errno if we615 * couldn't lock the orphanage in time.616 */617static inline int618xrep_dirtree_adopt_iolock(619 struct xfs_scrub *sc)620{621 int error;622 623 ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);624 625 if (xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL))626 return 0;627 628 xchk_iunlock(sc, XFS_IOLOCK_EXCL);629 do {630 xrep_orphanage_ilock(sc, XFS_IOLOCK_EXCL);631 if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))632 break;633 xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);634 635 if (xchk_should_terminate(sc, &error)) {636 xchk_ilock(sc, XFS_IOLOCK_EXCL);637 return error;638 }639 640 delay(1);641 } while (1);642 643 return 0;644}645 646/*647 * Reattach this orphaned directory to the orphanage. Do not call this with648 * any resources held. Returns -ESTALE if the scan data have become out of649 * date.650 */651STATIC int652xrep_dirtree_adopt(653 struct xchk_dirtree *dl)654{655 struct xfs_scrub *sc = dl->sc;656 int error;657 658 /* Take the IOLOCK of the orphanage and the scrub target. */659 error = xrep_dirtree_adopt_iolock(sc);660 if (error)661 return error;662 663 /*664 * Set up for an adoption. The directory tree fixer runs after the665 * link counts have been corrected. Therefore, we must bump the666 * child's link count since there will be no further opportunity to fix667 * errors.668 */669 error = xrep_adoption_trans_alloc(sc, &dl->adoption);670 if (error)671 goto out_iolock;672 dl->adoption.bump_child_nlink = true;673 674 /* Figure out what name we're going to use here. */675 error = xrep_adoption_compute_name(&dl->adoption, &dl->xname);676 if (error)677 goto out_trans;678 679 /*680 * Now that we have a proposed name for the orphanage entry, create681 * a faux path so that the live update hook will see it.682 */683 mutex_lock(&dl->lock);684 if (dl->stale) {685 mutex_unlock(&dl->lock);686 error = -ESTALE;687 goto out_trans;688 }689 error = xrep_dirtree_create_adoption_path(dl);690 mutex_unlock(&dl->lock);691 if (error)692 goto out_trans;693 694 /* Reparent the directory. */695 error = xrep_adoption_move(&dl->adoption);696 if (error)697 goto out_trans;698 699 /*700 * Commit the name and release all inode locks except for the scrub701 * target's IOLOCK.702 */703 error = xrep_trans_commit(sc);704 goto out_ilock;705 706out_trans:707 xchk_trans_cancel(sc);708out_ilock:709 xchk_iunlock(sc, XFS_ILOCK_EXCL);710 xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);711out_iolock:712 xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);713 return error;714}715 716/*717 * This newly orphaned directory needs to be adopted by the orphanage.718 * Make this happen.719 */720STATIC int721xrep_dirtree_move_to_orphanage(722 struct xchk_dirtree *dl)723{724 struct xfs_scrub *sc = dl->sc;725 int error;726 727 /*728 * Start by dropping all the resources that we hold so that we can grab729 * all the resources that we need for the adoption.730 */731 mutex_unlock(&dl->lock);732 xchk_trans_cancel(sc);733 xchk_iunlock(sc, XFS_ILOCK_EXCL);734 735 /* Perform the adoption. */736 error = xrep_dirtree_adopt(dl);737 738 /*739 * Retake all the resources we had at the beginning even if the repair740 * failed or the scan data are now stale. This keeps things simple for741 * the caller.742 */743 xchk_trans_alloc_empty(sc);744 xchk_ilock(sc, XFS_ILOCK_EXCL);745 mutex_lock(&dl->lock);746 747 if (!error && dl->stale)748 error = -ESTALE;749 return error;750}751 752/*753 * Try to fix all the problems. Returns -ESTALE if the scan data have become754 * out of date.755 */756STATIC int757xrep_dirtree_fix_problems(758 struct xchk_dirtree *dl,759 struct xchk_dirtree_outcomes *oc)760{761 struct xchk_dirpath *path;762 int error;763 764 /* Delete all the paths we don't want. */765 xchk_dirtree_for_each_path(dl, path) {766 if (path->outcome != XCHK_DIRPATH_DELETE)767 continue;768 769 error = xrep_dirtree_delete_path(dl, path);770 if (error)771 return error;772 }773 774 /* Reparent this directory to the orphanage. */775 if (oc->needs_adoption) {776 if (xrep_orphanage_can_adopt(dl->sc))777 return xrep_dirtree_move_to_orphanage(dl);778 return -EFSCORRUPTED;779 }780 781 return 0;782}783 784/* Fix directory loops involving this directory. */785int786xrep_dirtree(787 struct xfs_scrub *sc)788{789 struct xchk_dirtree *dl = sc->buf;790 struct xchk_dirtree_outcomes oc;791 int error;792 793 /*794 * Prepare to fix the directory tree by retaking the scan lock. The795 * order of resource acquisition is still IOLOCK -> transaction ->796 * ILOCK -> scan lock.797 */798 mutex_lock(&dl->lock);799 do {800 /*801 * Decide what we're going to do, then do it. An -ESTALE802 * return here means the scan results are invalid and we have803 * to walk again.804 */805 if (!dl->stale) {806 xrep_dirtree_decide_fate(dl, &oc);807 808 trace_xrep_dirtree_decided_fate(dl, &oc);809 810 error = xrep_dirtree_fix_problems(dl, &oc);811 if (!error || error != -ESTALE)812 break;813 }814 error = xchk_dirtree_find_paths_to_root(dl);815 if (error == -ELNRNG || error == -ENOSR)816 error = -EFSCORRUPTED;817 } while (!error);818 mutex_unlock(&dl->lock);819 820 return error;821}822