linux/fs/xfs/scrub/nlinks.c

// 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)
{}