// 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_ialloc.h" #include "xfs_quota.h" #include "xfs_bmap.h" #include "xfs_bmap_btree.h" #include "xfs_trans_space.h" #include "xfs_dir2.h" #include "xfs_exchrange.h" #include "xfs_exchmaps.h" #include "xfs_defer.h" #include "xfs_symlink_remote.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/repair.h" #include "scrub/trace.h" #include "scrub/tempfile.h" #include "scrub/tempexch.h" #include "scrub/xfile.h" /* * Create a temporary file for reconstructing metadata, with the intention of * atomically exchanging the temporary file's contents with the file that's * being repaired. */ int xrep_tempfile_create( struct xfs_scrub *sc, uint16_t mode) { … } /* Take IOLOCK_EXCL on the temporary file, maybe. */ bool xrep_tempfile_iolock_nowait( struct xfs_scrub *sc) { … } /* * Take the temporary file's IOLOCK while holding a different inode's IOLOCK. * In theory nobody else should hold the tempfile's IOLOCK, but we use trylock * to avoid deadlocks and lockdep complaints. */ int xrep_tempfile_iolock_polled( struct xfs_scrub *sc) { … } /* Release IOLOCK_EXCL on the temporary file. */ void xrep_tempfile_iounlock( struct xfs_scrub *sc) { … } /* Prepare the temporary file for metadata updates by grabbing ILOCK_EXCL. */ void xrep_tempfile_ilock( struct xfs_scrub *sc) { … } /* Try to grab ILOCK_EXCL on the temporary file. */ bool xrep_tempfile_ilock_nowait( struct xfs_scrub *sc) { … } /* Unlock ILOCK_EXCL on the temporary file after an update. */ void xrep_tempfile_iunlock( struct xfs_scrub *sc) { … } /* * Begin the process of making changes to both the file being scrubbed and * the temporary file by taking ILOCK_EXCL on both. */ void xrep_tempfile_ilock_both( struct xfs_scrub *sc) { … } /* Unlock ILOCK_EXCL on both files. */ void xrep_tempfile_iunlock_both( struct xfs_scrub *sc) { … } /* Release the temporary file. */ void xrep_tempfile_rele( struct xfs_scrub *sc) { … } /* * Make sure that the given range of the data fork of the temporary file is * mapped to written blocks. The caller must ensure that both inodes are * joined to the transaction. */ int xrep_tempfile_prealloc( struct xfs_scrub *sc, xfs_fileoff_t off, xfs_filblks_t len) { … } /* * Write data to each block of a file. The given range of the tempfile's data * fork must already be populated with written extents. */ int xrep_tempfile_copyin( struct xfs_scrub *sc, xfs_fileoff_t off, xfs_filblks_t len, xrep_tempfile_copyin_fn prep_fn, void *data) { … } /* * Set the temporary file's size. Caller must join the tempfile to the scrub * transaction and is responsible for adjusting block mappings as needed. */ int xrep_tempfile_set_isize( struct xfs_scrub *sc, unsigned long long isize) { … } /* * Roll a repair transaction involving the temporary file. Caller must join * both the temporary file and the file being scrubbed to the transaction. * This function return with both inodes joined to a new scrub transaction, * or the usual negative errno. */ int xrep_tempfile_roll_trans( struct xfs_scrub *sc) { … } /* * Fill out the mapping exchange request in preparation for atomically * committing the contents of a metadata file that we've rebuilt in the temp * file. */ STATIC int xrep_tempexch_prep_request( struct xfs_scrub *sc, int whichfork, struct xrep_tempexch *tx) { … } /* * Fill out the mapping exchange resource estimation structures in preparation * for exchanging the contents of a metadata file that we've rebuilt in the * temp file. Caller must hold IOLOCK_EXCL but not ILOCK_EXCL on both files. */ STATIC int xrep_tempexch_estimate( struct xfs_scrub *sc, struct xrep_tempexch *tx) { … } /* * Obtain a quota reservation to make sure we don't hit EDQUOT. We can skip * this if quota enforcement is disabled or if both inodes' dquots are the * same. The qretry structure must be initialized to zeroes before the first * call to this function. */ STATIC int xrep_tempexch_reserve_quota( struct xfs_scrub *sc, const struct xrep_tempexch *tx) { … } /* * Prepare an existing transaction for an atomic file contents exchange. * * This function fills out the mapping exchange request and resource estimation * structures in preparation for exchanging the contents of a metadata file * that has been rebuilt in the temp file. Next, it reserves space and quota * for the transaction. * * The caller must hold ILOCK_EXCL of the scrub target file and the temporary * file. The caller must join both inodes to the transaction with no unlock * flags, and is responsible for dropping both ILOCKs when appropriate. Only * use this when those ILOCKs cannot be dropped. */ int xrep_tempexch_trans_reserve( struct xfs_scrub *sc, int whichfork, struct xrep_tempexch *tx) { … } /* * Create a new transaction for a file contents exchange. * * This function fills out the mapping excahange request and resource * estimation structures in preparation for exchanging the contents of a * metadata file that has been rebuilt in the temp file. Next, it reserves * space, takes ILOCK_EXCL of both inodes, joins them to the transaction and * reserves quota for the transaction. * * The caller is responsible for dropping both ILOCKs when appropriate. */ int xrep_tempexch_trans_alloc( struct xfs_scrub *sc, int whichfork, struct xrep_tempexch *tx) { … } /* * Exchange file mappings (and hence file contents) between the file being * repaired and the temporary file. Returns with both inodes locked and joined * to a clean scrub transaction. */ int xrep_tempexch_contents( struct xfs_scrub *sc, struct xrep_tempexch *tx) { … } /* * Write local format data from one of the temporary file's forks into the same * fork of file being repaired, and exchange the file sizes, if appropriate. * Caller must ensure that the file being repaired has enough fork space to * hold all the bytes. */ void xrep_tempfile_copyout_local( struct xfs_scrub *sc, int whichfork) { … } /* Decide if a given XFS inode is a temporary file for a repair. */ bool xrep_is_tempfile( const struct xfs_inode *ip) { … }