linux/fs/xfs/scrub/attr_repair.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2018-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_btree.h"
#include "xfs_bit.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_inode.h"
#include "xfs_da_format.h"
#include "xfs_da_btree.h"
#include "xfs_dir2.h"
#include "xfs_attr.h"
#include "xfs_attr_leaf.h"
#include "xfs_attr_sf.h"
#include "xfs_attr_remote.h"
#include "xfs_bmap.h"
#include "xfs_bmap_util.h"
#include "xfs_exchmaps.h"
#include "xfs_exchrange.h"
#include "xfs_acl.h"
#include "xfs_parent.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
#include "scrub/repair.h"
#include "scrub/tempfile.h"
#include "scrub/tempexch.h"
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
#include "scrub/xfblob.h"
#include "scrub/attr.h"
#include "scrub/reap.h"
#include "scrub/attr_repair.h"

/*
 * Extended Attribute Repair
 * =========================
 *
 * We repair extended attributes by reading the attr leaf blocks looking for
 * attributes entries that look salvageable (name passes verifiers, value can
 * be retrieved, etc).  Each extended attribute worth salvaging is stashed in
 * memory, and the stashed entries are periodically replayed into a temporary
 * file to constrain memory use.  Batching the construction of the temporary
 * extended attribute structure in this fashion reduces lock cycling of the
 * file being repaired and the temporary file.
 *
 * When salvaging completes, the remaining stashed attributes are replayed to
 * the temporary file.  An atomic file contents exchange is used to commit the
 * new xattr blocks to the file being repaired.  This will disrupt attrmulti
 * cursors.
 */

struct xrep_xattr_key {};

/*
 * Stash up to 8 pages of attrs in xattr_records/xattr_blobs before we write
 * them to the temp file.
 */
#define XREP_XATTR_MAX_STASH_BYTES

struct xrep_xattr {};

/* Create a parent pointer in the tempfile. */
#define XREP_XATTR_PPTR_ADD

/* Remove a parent pointer from the tempfile. */
#define XREP_XATTR_PPTR_REMOVE

/* A stashed parent pointer update. */
struct xrep_xattr_pptr {};

/* Set up to recreate the extended attributes. */
int
xrep_setup_xattr(
	struct xfs_scrub	*sc)
{}

/*
 * Decide if we want to salvage this attribute.  We don't bother with
 * incomplete or oversized keys or values.  The @value parameter can be null
 * for remote attrs.
 */
STATIC int
xrep_xattr_want_salvage(
	struct xrep_xattr	*rx,
	unsigned int		attr_flags,
	const void		*name,
	int			namelen,
	const void		*value,
	int			valuelen)
{}

/* Allocate an in-core record to hold xattrs while we rebuild the xattr data. */
STATIC int
xrep_xattr_salvage_key(
	struct xrep_xattr	*rx,
	int			flags,
	unsigned char		*name,
	int			namelen,
	unsigned char		*value,
	int			valuelen)
{}

/*
 * Record a shortform extended attribute key & value for later reinsertion
 * into the inode.
 */
STATIC int
xrep_xattr_salvage_sf_attr(
	struct xrep_xattr		*rx,
	struct xfs_attr_sf_hdr		*hdr,
	struct xfs_attr_sf_entry	*sfe)
{}

/*
 * Record a local format extended attribute key & value for later reinsertion
 * into the inode.
 */
STATIC int
xrep_xattr_salvage_local_attr(
	struct xrep_xattr		*rx,
	struct xfs_attr_leaf_entry	*ent,
	unsigned int			nameidx,
	const char			*buf_end,
	struct xfs_attr_leaf_name_local	*lentry)
{}

/*
 * Record a remote format extended attribute key & value for later reinsertion
 * into the inode.
 */
STATIC int
xrep_xattr_salvage_remote_attr(
	struct xrep_xattr		*rx,
	struct xfs_attr_leaf_entry	*ent,
	unsigned int			nameidx,
	const char			*buf_end,
	struct xfs_attr_leaf_name_remote *rentry,
	unsigned int			ent_idx,
	struct xfs_buf			*leaf_bp)
{}

/* Extract every xattr key that we can from this attr fork block. */
STATIC int
xrep_xattr_recover_leaf(
	struct xrep_xattr		*rx,
	struct xfs_buf			*bp)
{}

/* Try to recover shortform attrs. */
STATIC int
xrep_xattr_recover_sf(
	struct xrep_xattr		*rx)
{}

/*
 * Try to return a buffer of xattr data for a given physical extent.
 *
 * Because the buffer cache get function complains if it finds a buffer
 * matching the block number but not matching the length, we must be careful to
 * look for incore buffers (up to the maximum length of a remote value) that
 * could be hiding anywhere in the physical range.  If we find an incore
 * buffer, we can pass that to the caller.  Optionally, read a single block and
 * pass that back.
 *
 * Note the subtlety that remote attr value blocks for which there is no incore
 * buffer will be passed to the callback one block at a time.  These buffers
 * will not have any ops attached and must be staled to prevent aliasing with
 * multiblock buffers once we drop the ILOCK.
 */
STATIC int
xrep_xattr_find_buf(
	struct xfs_mount	*mp,
	xfs_fsblock_t		fsbno,
	xfs_extlen_t		max_len,
	bool			can_read,
	struct xfs_buf		**bpp)
{}

/*
 * Deal with a buffer that we found during our walk of the attr fork.
 *
 * Attribute leaf and node blocks are simple -- they're a single block, so we
 * can walk them one at a time and we never have to worry about discontiguous
 * multiblock buffers like we do for directories.
 *
 * Unfortunately, remote attr blocks add a lot of complexity here.  Each disk
 * block is totally self contained, in the sense that the v5 header provides no
 * indication that there could be more data in the next block.  The incore
 * buffers can span multiple blocks, though they never cross extent records.
 * However, they don't necessarily start or end on an extent record boundary.
 * Therefore, we need a special buffer find function to walk the buffer cache
 * for us.
 *
 * The caller must hold the ILOCK on the file being repaired.  We use
 * XBF_TRYLOCK here to skip any locked buffer on the assumption that we don't
 * own the block and don't want to hang the system on a potentially garbage
 * buffer.
 */
STATIC int
xrep_xattr_recover_block(
	struct xrep_xattr	*rx,
	xfs_dablk_t		dabno,
	xfs_fsblock_t		fsbno,
	xfs_extlen_t		max_len,
	xfs_extlen_t		*actual_len)
{}

/* Insert one xattr key/value. */
STATIC int
xrep_xattr_insert_rec(
	struct xrep_xattr		*rx,
	const struct xrep_xattr_key	*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_xattr_flush_stashed(
	struct xrep_xattr	*rx)
{}

/* Decide if we've stashed too much xattr data in memory. */
static inline bool
xrep_xattr_want_flush_stashed(
	struct xrep_xattr	*rx)
{}

/*
 * Did we observe rename changing parent pointer xattrs while we were flushing
 * salvaged attrs?
 */
static inline bool
xrep_xattr_saw_pptr_conflict(
	struct xrep_xattr	*rx)
{}

/*
 * Reset the entire repair state back to initial conditions, now that we've
 * detected a parent pointer update to the attr structure while we were
 * flushing salvaged attrs.  See the locking notes in dir_repair.c for more
 * information on why this is all necessary.
 */
STATIC int
xrep_xattr_full_reset(
	struct xrep_xattr	*rx)
{}

/* Extract as many attribute keys and values as we can. */
STATIC int
xrep_xattr_recover(
	struct xrep_xattr	*rx)
{}

/*
 * Reset the extended attribute fork to a state where we can start re-adding
 * the salvaged attributes.
 */
STATIC int
xrep_xattr_fork_remove(
	struct xfs_scrub	*sc,
	struct xfs_inode	*ip)
{}

/*
 * Free all the attribute fork blocks of the file being repaired and delete the
 * fork.  The caller must ILOCK the scrub file and join it to the transaction.
 * This function returns with the inode joined to a clean transaction.
 */
int
xrep_xattr_reset_fork(
	struct xfs_scrub	*sc)
{}

/*
 * Free all the attribute fork blocks of the temporary file and delete the attr
 * fork.  The caller must ILOCK the tempfile and join it to the transaction.
 * This function returns with the inode joined to a clean scrub transaction.
 */
int
xrep_xattr_reset_tempfile_fork(
	struct xfs_scrub	*sc)
{}

/*
 * Find all the extended attributes for this inode by scraping them out of the
 * attribute key blocks by hand, and flushing them into the temp file.
 * When we're done, free the staging memory before exchanging the xattr
 * structures to reduce memory usage.
 */
STATIC int
xrep_xattr_salvage_attributes(
	struct xrep_xattr	*rx)
{}

/*
 * 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_xattr_replay_pptr_update(
	struct xrep_xattr		*rx,
	const struct xfs_name		*xname,
	struct xrep_xattr_pptr		*pptr)
{}

/*
 * Flush stashed parent pointer updates that have been recorded by the scanner.
 * This is done to reduce the memory requirements of the xattr 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_xattr_replay_pptr_updates(
	struct xrep_xattr	*rx)
{}

/*
 * Remember that we want to create a parent pointer in the tempfile.  These
 * stashed actions will be replayed later.
 */
STATIC int
xrep_xattr_stash_parentadd(
	struct xrep_xattr	*rx,
	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_xattr_stash_parentremove(
	struct xrep_xattr	*rx,
	const struct xfs_name	*name,
	const struct xfs_inode	*dp)
{}

/*
 * Capture dirent updates being made by other threads.  We will have to replay
 * the parent pointer updates before exchanging attr forks.
 */
STATIC int
xrep_xattr_live_dirent_update(
	struct notifier_block		*nb,
	unsigned long			action,
	void				*data)
{}

/*
 * Prepare both inodes' attribute forks for an exchange.  Promote the tempfile
 * from short format to leaf format, and if the file being repaired has a short
 * format attr fork, turn it into an empty extent list.
 */
STATIC int
xrep_xattr_swap_prep(
	struct xfs_scrub	*sc,
	bool			temp_local,
	bool			ip_local)
{}

/* Exchange the temporary file's attribute fork with the one being repaired. */
int
xrep_xattr_swap(
	struct xfs_scrub	*sc,
	struct xrep_tempexch	*tx)
{}

/*
 * 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 extended attribute structure.
 */
STATIC int
xrep_xattr_finalize_tempfile(
	struct xrep_xattr	*rx)
{}

/*
 * Exchange the new extended attribute data (which we created in the tempfile)
 * with the file being repaired.
 */
STATIC int
xrep_xattr_rebuild_tree(
	struct xrep_xattr	*rx)
{}

/* Tear down all the incore scan stuff we created. */
STATIC void
xrep_xattr_teardown(
	struct xrep_xattr	*rx)
{}

/* Set up the filesystem scan so we can regenerate extended attributes. */
STATIC int
xrep_xattr_setup_scan(
	struct xfs_scrub	*sc,
	struct xrep_xattr	**rxp)
{}

/*
 * Repair the extended attribute metadata.
 *
 * XXX: Remote attribute value buffers encompass the entire (up to 64k) buffer.
 * The buffer cache in XFS can't handle aliased multiblock buffers, so this
 * might misbehave if the attr fork is crosslinked with other filesystem
 * metadata.
 */
int
xrep_xattr(
	struct xfs_scrub	*sc)
{}