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