628 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2021-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_mount.h"12#include "xfs_log_format.h"13#include "xfs_trans.h"14#include "xfs_inode.h"15#include "xfs_ialloc.h"16#include "xfs_quota.h"17#include "xfs_trans_space.h"18#include "xfs_dir2.h"19#include "xfs_icache.h"20#include "xfs_bmap.h"21#include "xfs_bmap_btree.h"22#include "xfs_parent.h"23#include "xfs_attr_sf.h"24#include "scrub/scrub.h"25#include "scrub/common.h"26#include "scrub/repair.h"27#include "scrub/trace.h"28#include "scrub/orphanage.h"29#include "scrub/readdir.h"30 31#include <linux/namei.h>32 33/*34 * The Orphanage35 * =============36 *37 * If the directory tree is damaged, children of that directory become38 * inaccessible via that file path. If a child has no other parents, the file39 * is said to be orphaned. xfs_repair fixes this situation by creating a40 * orphanage directory (specifically, /lost+found) and creating a directory41 * entry pointing to the orphaned file.42 *43 * Online repair follows this tactic by creating a root-owned /lost+found44 * directory if one does not exist. If an orphan is found, it will move that45 * files into orphanage.46 */47 48/* Make the orphanage owned by root. */49STATIC int50xrep_chown_orphanage(51 struct xfs_scrub *sc,52 struct xfs_inode *dp)53{54 struct xfs_trans *tp;55 struct xfs_mount *mp = sc->mp;56 struct xfs_dquot *udqp = NULL, *gdqp = NULL, *pdqp = NULL;57 struct xfs_dquot *oldu = NULL, *oldg = NULL, *oldp = NULL;58 struct inode *inode = VFS_I(dp);59 int error;60 61 error = xfs_qm_vop_dqalloc(dp, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0,62 XFS_QMOPT_QUOTALL, &udqp, &gdqp, &pdqp);63 if (error)64 return error;65 66 error = xfs_trans_alloc_ichange(dp, udqp, gdqp, pdqp, true, &tp);67 if (error)68 goto out_dqrele;69 70 /*71 * Always clear setuid/setgid/sticky on the orphanage since we don't72 * normally want that functionality on this directory and xfs_repair73 * doesn't create it this way either. Leave the other access bits74 * unchanged.75 */76 inode->i_mode &= ~(S_ISUID | S_ISGID | S_ISVTX);77 78 /*79 * Change the ownerships and register quota modifications80 * in the transaction.81 */82 if (!uid_eq(inode->i_uid, GLOBAL_ROOT_UID)) {83 if (XFS_IS_UQUOTA_ON(mp))84 oldu = xfs_qm_vop_chown(tp, dp, &dp->i_udquot, udqp);85 inode->i_uid = GLOBAL_ROOT_UID;86 }87 if (!gid_eq(inode->i_gid, GLOBAL_ROOT_GID)) {88 if (XFS_IS_GQUOTA_ON(mp))89 oldg = xfs_qm_vop_chown(tp, dp, &dp->i_gdquot, gdqp);90 inode->i_gid = GLOBAL_ROOT_GID;91 }92 if (dp->i_projid != 0) {93 if (XFS_IS_PQUOTA_ON(mp))94 oldp = xfs_qm_vop_chown(tp, dp, &dp->i_pdquot, pdqp);95 dp->i_projid = 0;96 }97 98 dp->i_diflags &= ~(XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT);99 xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);100 101 XFS_STATS_INC(mp, xs_ig_attrchg);102 103 if (xfs_has_wsync(mp))104 xfs_trans_set_sync(tp);105 error = xfs_trans_commit(tp);106 107 xfs_qm_dqrele(oldu);108 xfs_qm_dqrele(oldg);109 xfs_qm_dqrele(oldp);110 111out_dqrele:112 xfs_qm_dqrele(udqp);113 xfs_qm_dqrele(gdqp);114 xfs_qm_dqrele(pdqp);115 return error;116}117 118#define ORPHANAGE "lost+found"119 120/* Create the orphanage directory, and set sc->orphanage to it. */121int122xrep_orphanage_create(123 struct xfs_scrub *sc)124{125 struct xfs_mount *mp = sc->mp;126 struct dentry *root_dentry, *orphanage_dentry;127 struct inode *root_inode = VFS_I(sc->mp->m_rootip);128 struct inode *orphanage_inode;129 int error;130 131 if (xfs_is_shutdown(mp))132 return -EIO;133 if (xfs_is_readonly(mp)) {134 sc->orphanage = NULL;135 return 0;136 }137 138 ASSERT(sc->tp == NULL);139 ASSERT(sc->orphanage == NULL);140 141 /* Find the dentry for the root directory... */142 root_dentry = d_find_alias(root_inode);143 if (!root_dentry) {144 error = -EFSCORRUPTED;145 goto out;146 }147 148 /* ...which is a directory, right? */149 if (!d_is_dir(root_dentry)) {150 error = -EFSCORRUPTED;151 goto out_dput_root;152 }153 154 /* Try to find the orphanage directory. */155 inode_lock_nested(root_inode, I_MUTEX_PARENT);156 orphanage_dentry = lookup_one_len(ORPHANAGE, root_dentry,157 strlen(ORPHANAGE));158 if (IS_ERR(orphanage_dentry)) {159 error = PTR_ERR(orphanage_dentry);160 goto out_unlock_root;161 }162 163 /*164 * Nothing found? Call mkdir to create the orphanage. Create the165 * directory without other-user access because we're live and someone166 * could have been relying partly on minimal access to a parent167 * directory to control access to a file we put in here.168 */169 if (d_really_is_negative(orphanage_dentry)) {170 error = vfs_mkdir(&nop_mnt_idmap, root_inode, orphanage_dentry,171 0750);172 if (error)173 goto out_dput_orphanage;174 }175 176 /* Not a directory? Bail out. */177 if (!d_is_dir(orphanage_dentry)) {178 error = -ENOTDIR;179 goto out_dput_orphanage;180 }181 182 /*183 * Grab a reference to the orphanage. This /should/ succeed since184 * we hold the root directory locked and therefore nobody can delete185 * the orphanage.186 */187 orphanage_inode = igrab(d_inode(orphanage_dentry));188 if (!orphanage_inode) {189 error = -ENOENT;190 goto out_dput_orphanage;191 }192 193 /* Make sure the orphanage is owned by root. */194 error = xrep_chown_orphanage(sc, XFS_I(orphanage_inode));195 if (error)196 goto out_dput_orphanage;197 198 /* Stash the reference for later and bail out. */199 sc->orphanage = XFS_I(orphanage_inode);200 sc->orphanage_ilock_flags = 0;201 202out_dput_orphanage:203 dput(orphanage_dentry);204out_unlock_root:205 inode_unlock(VFS_I(sc->mp->m_rootip));206out_dput_root:207 dput(root_dentry);208out:209 return error;210}211 212void213xrep_orphanage_ilock(214 struct xfs_scrub *sc,215 unsigned int ilock_flags)216{217 sc->orphanage_ilock_flags |= ilock_flags;218 xfs_ilock(sc->orphanage, ilock_flags);219}220 221bool222xrep_orphanage_ilock_nowait(223 struct xfs_scrub *sc,224 unsigned int ilock_flags)225{226 if (xfs_ilock_nowait(sc->orphanage, ilock_flags)) {227 sc->orphanage_ilock_flags |= ilock_flags;228 return true;229 }230 231 return false;232}233 234void235xrep_orphanage_iunlock(236 struct xfs_scrub *sc,237 unsigned int ilock_flags)238{239 xfs_iunlock(sc->orphanage, ilock_flags);240 sc->orphanage_ilock_flags &= ~ilock_flags;241}242 243/* Grab the IOLOCK of the orphanage and sc->ip. */244int245xrep_orphanage_iolock_two(246 struct xfs_scrub *sc)247{248 int error = 0;249 250 while (true) {251 if (xchk_should_terminate(sc, &error))252 return error;253 254 /*255 * Normal XFS takes the IOLOCK before grabbing a transaction.256 * Scrub holds a transaction, which means that we can't block257 * on either IOLOCK.258 */259 if (xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL)) {260 if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))261 break;262 xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);263 }264 delay(1);265 }266 267 return 0;268}269 270/* Release the orphanage. */271void272xrep_orphanage_rele(273 struct xfs_scrub *sc)274{275 if (!sc->orphanage)276 return;277 278 if (sc->orphanage_ilock_flags)279 xfs_iunlock(sc->orphanage, sc->orphanage_ilock_flags);280 281 xchk_irele(sc, sc->orphanage);282 sc->orphanage = NULL;283}284 285/* Adoption moves a file into /lost+found */286 287/* Can the orphanage adopt @sc->ip? */288bool289xrep_orphanage_can_adopt(290 struct xfs_scrub *sc)291{292 ASSERT(sc->ip != NULL);293 294 if (!sc->orphanage)295 return false;296 if (sc->ip == sc->orphanage)297 return false;298 if (xfs_internal_inum(sc->mp, sc->ip->i_ino))299 return false;300 return true;301}302 303/*304 * Create a new transaction to send a child to the orphanage.305 *306 * Allocate a new transaction with sufficient disk space to handle the307 * adoption, take ILOCK_EXCL of the orphanage and sc->ip, joins them to the308 * transaction, and reserve quota to reparent the latter. Caller must hold the309 * IOLOCK of the orphanage and sc->ip.310 */311int312xrep_adoption_trans_alloc(313 struct xfs_scrub *sc,314 struct xrep_adoption *adopt)315{316 struct xfs_mount *mp = sc->mp;317 unsigned int child_blkres = 0;318 int error;319 320 ASSERT(sc->tp == NULL);321 ASSERT(sc->ip != NULL);322 ASSERT(sc->orphanage != NULL);323 ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);324 ASSERT(sc->orphanage_ilock_flags & XFS_IOLOCK_EXCL);325 ASSERT(!(sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)));326 ASSERT(!(sc->orphanage_ilock_flags &327 (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)));328 329 /* Compute the worst case space reservation that we need. */330 adopt->sc = sc;331 adopt->orphanage_blkres = xfs_link_space_res(mp, MAXNAMELEN);332 if (S_ISDIR(VFS_I(sc->ip)->i_mode))333 child_blkres = xfs_rename_space_res(mp, 0, false,334 xfs_name_dotdot.len, false);335 if (xfs_has_parent(mp))336 child_blkres += XFS_ADDAFORK_SPACE_RES(mp);337 adopt->child_blkres = child_blkres;338 339 /*340 * Allocate a transaction to link the child into the parent, along with341 * enough disk space to handle expansion of both the orphanage and the342 * dotdot entry of a child directory.343 */344 error = xfs_trans_alloc(mp, &M_RES(mp)->tr_link,345 adopt->orphanage_blkres + adopt->child_blkres, 0, 0,346 &sc->tp);347 if (error)348 return error;349 350 xfs_lock_two_inodes(sc->orphanage, XFS_ILOCK_EXCL,351 sc->ip, XFS_ILOCK_EXCL);352 sc->ilock_flags |= XFS_ILOCK_EXCL;353 sc->orphanage_ilock_flags |= XFS_ILOCK_EXCL;354 355 xfs_trans_ijoin(sc->tp, sc->orphanage, 0);356 xfs_trans_ijoin(sc->tp, sc->ip, 0);357 358 /*359 * Reserve enough quota in the orphan directory to add the new name.360 * Normally the orphanage should have user/group/project ids of zero361 * and hence is not subject to quota enforcement, but we're allowed to362 * exceed quota to reattach disconnected parts of the directory tree.363 */364 error = xfs_trans_reserve_quota_nblks(sc->tp, sc->orphanage,365 adopt->orphanage_blkres, 0, true);366 if (error)367 goto out_cancel;368 369 /*370 * Reserve enough quota in the child directory to change dotdot.371 * Here we're also allowed to exceed file quota to repair inconsistent372 * metadata.373 */374 if (adopt->child_blkres) {375 error = xfs_trans_reserve_quota_nblks(sc->tp, sc->ip,376 adopt->child_blkres, 0, true);377 if (error)378 goto out_cancel;379 }380 381 return 0;382out_cancel:383 xchk_trans_cancel(sc);384 xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);385 xchk_iunlock(sc, XFS_ILOCK_EXCL);386 return error;387}388 389/*390 * Compute the xfs_name for the directory entry that we're adding to the391 * orphanage. Caller must hold ILOCKs of sc->ip and the orphanage and must not392 * reuse namebuf until the adoption completes or is dissolved.393 */394int395xrep_adoption_compute_name(396 struct xrep_adoption *adopt,397 struct xfs_name *xname)398{399 struct xfs_scrub *sc = adopt->sc;400 char *namebuf = (void *)xname->name;401 xfs_ino_t ino;402 unsigned int incr = 0;403 int error = 0;404 405 adopt->xname = xname;406 xname->len = snprintf(namebuf, MAXNAMELEN, "%llu", sc->ip->i_ino);407 xname->type = xfs_mode_to_ftype(VFS_I(sc->ip)->i_mode);408 409 /* Make sure the filename is unique in the lost+found. */410 error = xchk_dir_lookup(sc, sc->orphanage, xname, &ino);411 while (error == 0 && incr < 10000) {412 xname->len = snprintf(namebuf, MAXNAMELEN, "%llu.%u",413 sc->ip->i_ino, ++incr);414 error = xchk_dir_lookup(sc, sc->orphanage, xname, &ino);415 }416 if (error == 0) {417 /* We already have 10,000 entries in the orphanage? */418 return -EFSCORRUPTED;419 }420 421 if (error != -ENOENT)422 return error;423 return 0;424}425 426/*427 * Make sure the dcache does not have a positive dentry for the name we've428 * chosen. The caller should have checked with the ondisk directory, so any429 * discrepancy is a sign that something is seriously wrong.430 */431static int432xrep_adoption_check_dcache(433 struct xrep_adoption *adopt)434{435 struct qstr qname = QSTR_INIT(adopt->xname->name,436 adopt->xname->len);437 struct xfs_scrub *sc = adopt->sc;438 struct dentry *d_orphanage, *d_child;439 int error = 0;440 441 d_orphanage = d_find_alias(VFS_I(sc->orphanage));442 if (!d_orphanage)443 return 0;444 445 d_child = d_hash_and_lookup(d_orphanage, &qname);446 if (d_child) {447 trace_xrep_adoption_check_child(sc->mp, d_child);448 449 if (d_is_positive(d_child)) {450 ASSERT(d_is_negative(d_child));451 error = -EFSCORRUPTED;452 }453 454 dput(d_child);455 }456 457 dput(d_orphanage);458 return error;459}460 461/*462 * Invalidate all dentries for the name that was added to the orphanage463 * directory, and all dentries pointing to the child inode that was moved.464 *465 * There should not be any positive entries for the name, since we've466 * maintained our lock on the orphanage directory.467 */468static void469xrep_adoption_zap_dcache(470 struct xrep_adoption *adopt)471{472 struct qstr qname = QSTR_INIT(adopt->xname->name,473 adopt->xname->len);474 struct xfs_scrub *sc = adopt->sc;475 struct dentry *d_orphanage, *d_child;476 477 /* Invalidate all dentries for the adoption name */478 d_orphanage = d_find_alias(VFS_I(sc->orphanage));479 if (!d_orphanage)480 return;481 482 d_child = d_hash_and_lookup(d_orphanage, &qname);483 while (d_child != NULL) {484 trace_xrep_adoption_invalidate_child(sc->mp, d_child);485 486 ASSERT(d_is_negative(d_child));487 d_invalidate(d_child);488 dput(d_child);489 d_child = d_lookup(d_orphanage, &qname);490 }491 492 dput(d_orphanage);493 494 /* Invalidate all the dentries pointing down to this file. */495 while ((d_child = d_find_alias(VFS_I(sc->ip))) != NULL) {496 trace_xrep_adoption_invalidate_child(sc->mp, d_child);497 498 d_invalidate(d_child);499 dput(d_child);500 }501}502 503/*504 * If we have to add an attr fork ahead of a parent pointer update, how much505 * space should we ask for?506 */507static inline int508xrep_adoption_attr_sizeof(509 const struct xrep_adoption *adopt)510{511 return sizeof(struct xfs_attr_sf_hdr) +512 xfs_attr_sf_entsize_byname(sizeof(struct xfs_parent_rec),513 adopt->xname->len);514}515 516/*517 * Move the current file to the orphanage under the computed name.518 *519 * Returns with a dirty transaction so that the caller can handle any other520 * work, such as fixing up unlinked lists or resetting link counts.521 */522int523xrep_adoption_move(524 struct xrep_adoption *adopt)525{526 struct xfs_scrub *sc = adopt->sc;527 bool isdir = S_ISDIR(VFS_I(sc->ip)->i_mode);528 int error;529 530 trace_xrep_adoption_reparent(sc->orphanage, adopt->xname,531 sc->ip->i_ino);532 533 error = xrep_adoption_check_dcache(adopt);534 if (error)535 return error;536 537 /*538 * If this filesystem has parent pointers, ensure that the file being539 * moved to the orphanage has an attribute fork. This is required540 * because the parent pointer code does not itself add attr forks.541 */542 if (!xfs_inode_has_attr_fork(sc->ip) && xfs_has_parent(sc->mp)) {543 int sf_size = xrep_adoption_attr_sizeof(adopt);544 545 error = xfs_bmap_add_attrfork(sc->tp, sc->ip, sf_size, true);546 if (error)547 return error;548 }549 550 /* Create the new name in the orphanage. */551 error = xfs_dir_createname(sc->tp, sc->orphanage, adopt->xname,552 sc->ip->i_ino, adopt->orphanage_blkres);553 if (error)554 return error;555 556 /*557 * Bump the link count of the orphanage if we just added a558 * subdirectory, and update its timestamps.559 */560 xfs_trans_ichgtime(sc->tp, sc->orphanage,561 XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);562 if (isdir)563 xfs_bumplink(sc->tp, sc->orphanage);564 xfs_trans_log_inode(sc->tp, sc->orphanage, XFS_ILOG_CORE);565 566 /* Bump the link count of the child. */567 if (adopt->bump_child_nlink) {568 xfs_bumplink(sc->tp, sc->ip);569 xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);570 }571 572 /* Replace the dotdot entry if the child is a subdirectory. */573 if (isdir) {574 error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,575 sc->orphanage->i_ino, adopt->child_blkres);576 if (error)577 return error;578 }579 580 /* Add a parent pointer from the file back to the lost+found. */581 if (xfs_has_parent(sc->mp)) {582 error = xfs_parent_addname(sc->tp, &adopt->ppargs,583 sc->orphanage, adopt->xname, sc->ip);584 if (error)585 return error;586 }587 588 /*589 * Notify dirent hooks that we moved the file to /lost+found, and590 * finish all the deferred work so that we know the adoption is fully591 * recorded in the log.592 */593 xfs_dir_update_hook(sc->orphanage, sc->ip, 1, adopt->xname);594 595 /* Remove negative dentries from the lost+found's dcache */596 xrep_adoption_zap_dcache(adopt);597 return 0;598}599 600/*601 * Roll to a clean scrub transaction so that we can release the orphanage,602 * even if xrep_adoption_move was not called.603 *604 * Commits all the work and deferred ops attached to an adoption request and605 * rolls to a clean scrub transaction. On success, returns 0 with the scrub606 * context holding a clean transaction with no inodes joined. On failure,607 * returns negative errno with no scrub transaction. All inode locks are608 * still held after this function returns.609 */610int611xrep_adoption_trans_roll(612 struct xrep_adoption *adopt)613{614 struct xfs_scrub *sc = adopt->sc;615 int error;616 617 trace_xrep_adoption_trans_roll(sc->orphanage, sc->ip,618 !!(sc->tp->t_flags & XFS_TRANS_DIRTY));619 620 /* Finish all the deferred ops to commit all repairs. */621 error = xrep_defer_finish(sc);622 if (error)623 return error;624 625 /* Roll the transaction once more to detach the inodes. */626 return xfs_trans_roll(&sc->tp);627}628