// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020-2024 Oracle. All Rights Reserved. * Author: Darrick J. Wong <[email protected]> */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_defer.h" #include "xfs_bit.h" #include "xfs_log_format.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" #include "xfs_icache.h" #include "xfs_da_format.h" #include "xfs_da_btree.h" #include "xfs_dir2.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_priv.h" #include "xfs_trans_space.h" #include "xfs_health.h" #include "xfs_exchmaps.h" #include "xfs_parent.h" #include "xfs_attr.h" #include "xfs_bmap.h" #include "xfs_ag.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" #include "scrub/repair.h" #include "scrub/iscan.h" #include "scrub/findparent.h" #include "scrub/readdir.h" #include "scrub/tempfile.h" #include "scrub/tempexch.h" #include "scrub/orphanage.h" #include "scrub/xfile.h" #include "scrub/xfarray.h" #include "scrub/xfblob.h" #include "scrub/attr_repair.h" #include "scrub/listxattr.h" /* * Repairing The Directory Parent Pointer * ====================================== * * Currently, only directories support parent pointers (in the form of '..' * entries), so we simply scan the filesystem and update the '..' entry. * * Note that because the only parent pointer is the dotdot entry, we won't * touch an unhealthy directory, since the directory repair code is perfectly * capable of rebuilding a directory with the proper parent inode. * * See the section on locking issues in dir_repair.c for more information about * conflicts with the VFS. The findparent code wll keep our incore parent * inode up to date. * * If parent pointers are enabled, we instead reconstruct the parent pointer * information by visiting every directory entry of every directory in the * system and translating the relevant dirents into parent pointers. In this * case, it is advantageous to stash all parent pointers created from dirents * from a single parent file before replaying them into the temporary file. To * save memory, the live filesystem scan reuses the findparent object. Parent * pointer repair chooses either directory scanning or findparent, but not * both. * * When salvaging completes, the remaining stashed entries are replayed to the * temporary file. All non-parent pointer extended attributes are copied to * the temporary file's extended attributes. An atomic file mapping exchange * is used to commit the new xattr blocks to the file being repaired. This * will disrupt attrmulti cursors. */ /* Create a parent pointer in the tempfile. */ #define XREP_PPTR_ADD … /* Remove a parent pointer from the tempfile. */ #define XREP_PPTR_REMOVE … /* A stashed parent pointer update. */ struct xrep_pptr { … }; /* * Stash up to 8 pages of recovered parent pointers in pptr_recs and * pptr_names before we write them to the temp file. */ #define XREP_PARENT_MAX_STASH_BYTES … struct xrep_parent { … }; struct xrep_parent_xattr { … }; /* * Stash up to 8 pages of attrs in xattr_records/xattr_blobs before we write * them to the temp file. */ #define XREP_PARENT_XATTR_MAX_STASH_BYTES … /* Tear down all the incore stuff we created. */ static void xrep_parent_teardown( struct xrep_parent *rp) { … } /* Set up for a parent repair. */ int xrep_setup_parent( struct xfs_scrub *sc) { … } /* * Scan all files in the filesystem for a child dirent that we can turn into * the dotdot entry for this directory. */ STATIC int xrep_parent_find_dotdot( struct xrep_parent *rp) { … } /* * Add this stashed incore parent pointer to the temporary file. * The caller must hold the tempdir's IOLOCK, must not hold any ILOCKs, and * must not be in transaction context. */ STATIC int xrep_parent_replay_update( struct xrep_parent *rp, const struct xfs_name *xname, struct xrep_pptr *pptr) { … } /* * Flush stashed parent pointer updates that have been recorded by the scanner. * This is done to reduce the memory requirements of the parent pointer * rebuild, since files can have a lot of hardlinks and the fs can be busy. * * Caller must not hold transactions or ILOCKs. Caller must hold the tempfile * IOLOCK. */ STATIC int xrep_parent_replay_updates( struct xrep_parent *rp) { … } /* * Remember that we want to create a parent pointer in the tempfile. These * stashed actions will be replayed later. */ STATIC int xrep_parent_stash_parentadd( struct xrep_parent *rp, const struct xfs_name *name, const struct xfs_inode *dp) { … } /* * Remember that we want to remove a parent pointer from the tempfile. These * stashed actions will be replayed later. */ STATIC int xrep_parent_stash_parentremove( struct xrep_parent *rp, const struct xfs_name *name, const struct xfs_inode *dp) { … } /* * Examine an entry of a directory. If this dirent leads us back to the file * whose parent pointers we're rebuilding, add a pptr to the temporary * directory. */ STATIC int xrep_parent_scan_dirent( struct xfs_scrub *sc, struct xfs_inode *dp, xfs_dir2_dataptr_t dapos, const struct xfs_name *name, xfs_ino_t ino, void *priv) { … } /* * Decide if we want to look for dirents in this directory. Skip the file * being repaired and any files being used to stage repairs. */ static inline bool xrep_parent_want_scan( struct xrep_parent *rp, const struct xfs_inode *ip) { … } /* * Take ILOCK on a file that we want to scan. * * Select ILOCK_EXCL if the file is a directory with an unloaded data bmbt. * Otherwise, take ILOCK_SHARED. */ static inline unsigned int xrep_parent_scan_ilock( struct xrep_parent *rp, struct xfs_inode *ip) { … } /* * Scan this file for relevant child dirents that point to the file whose * parent pointers we're rebuilding. */ STATIC int xrep_parent_scan_file( struct xrep_parent *rp, struct xfs_inode *ip) { … } /* Decide if we've stashed too much pptr data in memory. */ static inline bool xrep_parent_want_flush_stashed( struct xrep_parent *rp) { … } /* * Scan all directories in the filesystem to look for dirents that we can turn * into parent pointers. */ STATIC int xrep_parent_scan_dirtree( struct xrep_parent *rp) { … } /* * Capture dirent updates being made by other threads which are relevant to the * file being repaired. */ STATIC int xrep_parent_live_update( struct notifier_block *nb, unsigned long action, void *data) { … } /* Reset a directory's dotdot entry, if needed. */ STATIC int xrep_parent_reset_dotdot( struct xrep_parent *rp) { … } /* Pass back the parent inumber if this a parent pointer */ STATIC int xrep_parent_lookup_pptr( struct xfs_scrub *sc, struct xfs_inode *ip, unsigned int attr_flags, const unsigned char *name, unsigned int namelen, const void *value, unsigned int valuelen, void *priv) { … } /* * Find the first parent of the scrub target by walking parent pointers for * the purpose of deciding if we're going to move it to the orphanage. * We don't care if the attr fork is zapped. */ STATIC int xrep_parent_lookup_pptrs( struct xfs_scrub *sc, xfs_ino_t *inop) { … } /* * Move the current file to the orphanage. * * Caller must hold IOLOCK_EXCL on @sc->ip, and no other inode locks. Upon * successful return, the scrub transaction will have enough extra reservation * to make the move; it will hold IOLOCK_EXCL and ILOCK_EXCL of @sc->ip and the * orphanage; and both inodes will be ijoined. */ STATIC int xrep_parent_move_to_orphanage( struct xrep_parent *rp) { … } /* Ensure that the xattr value buffer is large enough. */ STATIC int xrep_parent_alloc_xattr_value( struct xrep_parent *rp, size_t bufsize) { … } /* Retrieve the (remote) value of a non-pptr xattr. */ STATIC int xrep_parent_fetch_xattr_remote( struct xrep_parent *rp, struct xfs_inode *ip, unsigned int attr_flags, const unsigned char *name, unsigned int namelen, unsigned int valuelen) { … } /* Stash non-pptr attributes for later replay into the temporary file. */ STATIC int xrep_parent_stash_xattr( struct xfs_scrub *sc, struct xfs_inode *ip, unsigned int attr_flags, const unsigned char *name, unsigned int namelen, const void *value, unsigned int valuelen, void *priv) { … } /* Insert one xattr key/value. */ STATIC int xrep_parent_insert_xattr( struct xrep_parent *rp, const struct xrep_parent_xattr *key) { … } /* * Periodically flush salvaged attributes to the temporary file. This is done * to reduce the memory requirements of the xattr rebuild because files can * contain millions of attributes. */ STATIC int xrep_parent_flush_xattrs( struct xrep_parent *rp) { … } /* Decide if we've stashed too much xattr data in memory. */ static inline bool xrep_parent_want_flush_xattrs( struct xrep_parent *rp) { … } /* Flush staged attributes to the temporary file if we're over the limit. */ STATIC int xrep_parent_try_flush_xattrs( struct xfs_scrub *sc, void *priv) { … } /* Copy all the non-pptr extended attributes into the temporary file. */ STATIC int xrep_parent_copy_xattrs( struct xrep_parent *rp) { … } /* * Ensure that @sc->ip and @sc->tempip both have attribute forks before we head * into the attr fork exchange transaction. All files on a filesystem with * parent pointers must have an attr fork because the parent pointer code does * not itself add attribute forks. * * Note: Unlinkable unlinked files don't need one, but the overhead of having * an unnecessary attr fork is not justified by the additional code complexity * that would be needed to track that state correctly. */ STATIC int xrep_parent_ensure_attr_fork( struct xrep_parent *rp) { … } /* * Finish replaying stashed parent pointer updates, allocate a transaction for * exchanging extent mappings, and take the ILOCKs of both files before we * commit the new attribute structure. */ STATIC int xrep_parent_finalize_tempfile( struct xrep_parent *rp) { … } /* * Replay all the stashed parent pointers into the temporary file, copy all * the non-pptr xattrs from the file being repaired into the temporary file, * and exchange the attr fork contents atomically. */ STATIC int xrep_parent_rebuild_pptrs( struct xrep_parent *rp) { … } /* * Commit the new parent pointer structure (currently only the dotdot entry) to * the file that we're repairing. */ STATIC int xrep_parent_rebuild_tree( struct xrep_parent *rp) { … } /* Count the number of parent pointers. */ STATIC int xrep_parent_count_pptr( struct xfs_scrub *sc, struct xfs_inode *ip, unsigned int attr_flags, const unsigned char *name, unsigned int namelen, const void *value, unsigned int valuelen, void *priv) { … } /* * After all parent pointer rebuilding and adoption activity completes, reset * the link count of this nondirectory, having scanned the fs to rebuild all * parent pointers. */ STATIC int xrep_parent_set_nondir_nlink( struct xrep_parent *rp) { … } /* Set up the filesystem scan so we can look for parents. */ STATIC int xrep_parent_setup_scan( struct xrep_parent *rp) { … } int xrep_parent( struct xfs_scrub *sc) { … }