linux/fs/xfs/scrub/parent_repair.c

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