// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2021-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_log_format.h" #include "xfs_trans.h" #include "xfs_inode.h" #include "xfs_icache.h" #include "xfs_iwalk.h" #include "xfs_ialloc.h" #include "xfs_dir2.h" #include "xfs_dir2_priv.h" #include "xfs_ag.h" #include "xfs_parent.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/repair.h" #include "scrub/xfile.h" #include "scrub/xfarray.h" #include "scrub/iscan.h" #include "scrub/orphanage.h" #include "scrub/nlinks.h" #include "scrub/trace.h" #include "scrub/readdir.h" #include "scrub/tempfile.h" #include "scrub/listxattr.h" /* * Live Inode Link Count Checking * ============================== * * Inode link counts are "summary" metadata, in the sense that they are * computed as the number of directory entries referencing each file on the * filesystem. Therefore, we compute the correct link counts by creating a * shadow link count structure and walking every inode. */ /* Set us up to scrub inode link counts. */ int xchk_setup_nlinks( struct xfs_scrub *sc) { … } /* * Part 1: Collecting file link counts. For each file, we create a shadow link * counting structure, then walk the entire directory tree, incrementing parent * and child link counts for each directory entry seen. * * To avoid false corruption reports in part 2, any failure in this part must * set the INCOMPLETE flag even when a negative errno is returned. This care * must be taken with certain errno values (i.e. EFSBADCRC, EFSCORRUPTED, * ECANCELED) that are absorbed into a scrub state flag update by * xchk_*_process_error. Scrub and repair share the same incore data * structures, so the INCOMPLETE flag is critical to prevent a repair based on * insufficient information. * * Because we are scanning a live filesystem, it's possible that another thread * will try to update the link counts for an inode that we've already scanned. * This will cause our counts to be incorrect. Therefore, we hook all * directory entry updates because that is when link count updates occur. By * shadowing transaction updates in this manner, live nlink check can ensure by * locking the inode and the shadow structure that its own copies are not out * of date. Because the hook code runs in a different process context from the * scrub code and the scrub state flags are not accessed atomically, failures * in the hook code must abort the iscan and the scrubber must notice the * aborted scan and set the incomplete flag. * * Note that we use jump labels and srcu notifier hooks to minimize the * overhead when live nlinks is /not/ running. Locking order for nlink * observations is inode ILOCK -> iscan_lock/xchk_nlink_ctrs lock. */ /* * Add a delta to an nlink counter, clamping the value to U32_MAX. Because * XFS_MAXLINK < U32_MAX, the checking code will produce the correct results * even if we lose some precision. */ static inline void careful_add( xfs_nlink_t *nlinkp, int delta) { … } /* Update incore link count information. Caller must hold the nlinks lock. */ STATIC int xchk_nlinks_update_incore( struct xchk_nlink_ctrs *xnc, xfs_ino_t ino, int parents_delta, int backrefs_delta, int children_delta) { … } /* * Apply a link count change from the regular filesystem into our shadow link * count structure based on a directory update in progress. */ STATIC int xchk_nlinks_live_update( struct notifier_block *nb, unsigned long action, void *data) { … } /* Bump the observed link count for the inode referenced by this entry. */ STATIC int xchk_nlinks_collect_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) { … } /* Bump the backref count for the inode referenced by this parent pointer. */ STATIC int xchk_nlinks_collect_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) { … } /* Walk a directory to bump the observed link counts of the children. */ STATIC int xchk_nlinks_collect_dir( struct xchk_nlink_ctrs *xnc, struct xfs_inode *dp) { … } /* If this looks like a valid pointer, count it. */ static inline int xchk_nlinks_collect_metafile( struct xchk_nlink_ctrs *xnc, xfs_ino_t ino) { … } /* Bump the link counts of metadata files rooted in the superblock. */ STATIC int xchk_nlinks_collect_metafiles( struct xchk_nlink_ctrs *xnc) { … } /* Advance the collection scan cursor for this non-directory file. */ static inline int xchk_nlinks_collect_file( struct xchk_nlink_ctrs *xnc, struct xfs_inode *ip) { … } /* Walk all directories and count inode links. */ STATIC int xchk_nlinks_collect( struct xchk_nlink_ctrs *xnc) { … } /* * Part 2: Comparing file link counters. Walk each inode and compare the link * counts against our shadow information; and then walk each shadow link count * structure (that wasn't covered in the first part), comparing it against the * file. */ /* Read the observed link count for comparison with the actual inode. */ STATIC int xchk_nlinks_comparison_read( struct xchk_nlink_ctrs *xnc, xfs_ino_t ino, struct xchk_nlink *obs) { … } /* Check our link count against an inode. */ STATIC int xchk_nlinks_compare_inode( struct xchk_nlink_ctrs *xnc, struct xfs_inode *ip) { … } /* * Check our link count against an inode that wasn't checked previously. This * is intended to catch directories with dangling links, though we could be * racing with inode allocation in other threads. */ STATIC int xchk_nlinks_compare_inum( struct xchk_nlink_ctrs *xnc, xfs_ino_t ino) { … } /* * Try to visit every inode in the filesystem to compare the link count. Move * on if we can't grab an inode, since we'll revisit unchecked nlink records in * the second part. */ static int xchk_nlinks_compare_iter( struct xchk_nlink_ctrs *xnc, struct xfs_inode **ipp) { … } /* Compare the link counts we observed against the live information. */ STATIC int xchk_nlinks_compare( struct xchk_nlink_ctrs *xnc) { … } /* Tear down everything associated with a nlinks check. */ static void xchk_nlinks_teardown_scan( void *priv) { … } /* * Scan all inodes in the entire filesystem to generate link count data. If * the scan is successful, the counts will be left alive for a repair. If any * error occurs, we'll tear everything down. */ STATIC int xchk_nlinks_setup_scan( struct xfs_scrub *sc, struct xchk_nlink_ctrs *xnc) { … } /* Scrub the link count of all inodes on the filesystem. */ int xchk_nlinks( struct xfs_scrub *sc) { … }