853 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2021-2024 Oracle. All Rights Reserved.4 * Author: Darrick J. Wong <djwong@kernel.org>5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_shared.h"9#include "xfs_format.h"10#include "xfs_trans_resv.h"11#include "xfs_mount.h"12#include "xfs_log_format.h"13#include "xfs_trans.h"14#include "xfs_inode.h"15#include "xfs_ialloc.h"16#include "xfs_quota.h"17#include "xfs_bmap.h"18#include "xfs_bmap_btree.h"19#include "xfs_trans_space.h"20#include "xfs_dir2.h"21#include "xfs_exchrange.h"22#include "xfs_exchmaps.h"23#include "xfs_defer.h"24#include "xfs_symlink_remote.h"25#include "scrub/scrub.h"26#include "scrub/common.h"27#include "scrub/repair.h"28#include "scrub/trace.h"29#include "scrub/tempfile.h"30#include "scrub/tempexch.h"31#include "scrub/xfile.h"32 33/*34 * Create a temporary file for reconstructing metadata, with the intention of35 * atomically exchanging the temporary file's contents with the file that's36 * being repaired.37 */38int39xrep_tempfile_create(40 struct xfs_scrub *sc,41 uint16_t mode)42{43 struct xfs_icreate_args args = {44 .pip = sc->mp->m_rootip,45 .mode = mode,46 .flags = XFS_ICREATE_TMPFILE | XFS_ICREATE_UNLINKABLE,47 };48 struct xfs_mount *mp = sc->mp;49 struct xfs_trans *tp = NULL;50 struct xfs_dquot *udqp;51 struct xfs_dquot *gdqp;52 struct xfs_dquot *pdqp;53 struct xfs_trans_res *tres;54 struct xfs_inode *dp = mp->m_rootip;55 xfs_ino_t ino;56 unsigned int resblks;57 bool is_dir = S_ISDIR(mode);58 int error;59 60 if (xfs_is_shutdown(mp))61 return -EIO;62 if (xfs_is_readonly(mp))63 return -EROFS;64 65 ASSERT(sc->tp == NULL);66 ASSERT(sc->tempip == NULL);67 68 /*69 * Make sure that we have allocated dquot(s) on disk. The temporary70 * inode should be completely root owned so that we don't fail due to71 * quota limits.72 */73 error = xfs_icreate_dqalloc(&args, &udqp, &gdqp, &pdqp);74 if (error)75 return error;76 77 if (is_dir) {78 resblks = xfs_mkdir_space_res(mp, 0);79 tres = &M_RES(mp)->tr_mkdir;80 } else {81 resblks = XFS_IALLOC_SPACE_RES(mp);82 tres = &M_RES(mp)->tr_create_tmpfile;83 }84 85 error = xfs_trans_alloc_icreate(mp, tres, udqp, gdqp, pdqp, resblks,86 &tp);87 if (error)88 goto out_release_dquots;89 90 /* Allocate inode, set up directory. */91 error = xfs_dialloc(&tp, &args, &ino);92 if (error)93 goto out_trans_cancel;94 error = xfs_icreate(tp, ino, &args, &sc->tempip);95 if (error)96 goto out_trans_cancel;97 98 /* We don't touch file data, so drop the realtime flags. */99 sc->tempip->i_diflags &= ~(XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT);100 xfs_trans_log_inode(tp, sc->tempip, XFS_ILOG_CORE);101 102 /*103 * Mark our temporary file as private so that LSMs and the ACL code104 * don't try to add their own metadata or reason about these files.105 * The file should never be exposed to userspace.106 */107 VFS_I(sc->tempip)->i_flags |= S_PRIVATE;108 VFS_I(sc->tempip)->i_opflags &= ~IOP_XATTR;109 110 if (is_dir) {111 error = xfs_dir_init(tp, sc->tempip, dp);112 if (error)113 goto out_trans_cancel;114 } else if (S_ISLNK(VFS_I(sc->tempip)->i_mode)) {115 /*116 * Initialize the temporary symlink with a meaningless target117 * that won't trip the verifiers. Repair must rewrite the118 * target with meaningful content before swapping with the file119 * being repaired. A single-byte target will not write a120 * remote target block, so the owner is irrelevant.121 */122 error = xfs_symlink_write_target(tp, sc->tempip,123 sc->tempip->i_ino, ".", 1, 0, 0);124 if (error)125 goto out_trans_cancel;126 }127 128 /*129 * Attach the dquot(s) to the inodes and modify them incore.130 * These ids of the inode couldn't have changed since the new131 * inode has been locked ever since it was created.132 */133 xfs_qm_vop_create_dqattach(tp, sc->tempip, udqp, gdqp, pdqp);134 135 /*136 * Put our temp file on the unlinked list so it's purged automatically.137 * All file-based metadata being reconstructed using this file must be138 * atomically exchanged with the original file because the contents139 * here will be purged when the inode is dropped or log recovery cleans140 * out the unlinked list.141 */142 error = xfs_iunlink(tp, sc->tempip);143 if (error)144 goto out_trans_cancel;145 146 error = xfs_trans_commit(tp);147 if (error)148 goto out_release_inode;149 150 trace_xrep_tempfile_create(sc);151 152 xfs_qm_dqrele(udqp);153 xfs_qm_dqrele(gdqp);154 xfs_qm_dqrele(pdqp);155 156 /* Finish setting up the incore / vfs context. */157 xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);158 xfs_setup_iops(sc->tempip);159 xfs_finish_inode_setup(sc->tempip);160 161 sc->temp_ilock_flags = 0;162 return error;163 164out_trans_cancel:165 xfs_trans_cancel(tp);166out_release_inode:167 /*168 * Wait until after the current transaction is aborted to finish the169 * setup of the inode and release the inode. This prevents recursive170 * transactions and deadlocks from xfs_inactive.171 */172 if (sc->tempip) {173 xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);174 xfs_finish_inode_setup(sc->tempip);175 xchk_irele(sc, sc->tempip);176 }177out_release_dquots:178 xfs_qm_dqrele(udqp);179 xfs_qm_dqrele(gdqp);180 xfs_qm_dqrele(pdqp);181 182 return error;183}184 185/* Take IOLOCK_EXCL on the temporary file, maybe. */186bool187xrep_tempfile_iolock_nowait(188 struct xfs_scrub *sc)189{190 if (xfs_ilock_nowait(sc->tempip, XFS_IOLOCK_EXCL)) {191 sc->temp_ilock_flags |= XFS_IOLOCK_EXCL;192 return true;193 }194 195 return false;196}197 198/*199 * Take the temporary file's IOLOCK while holding a different inode's IOLOCK.200 * In theory nobody else should hold the tempfile's IOLOCK, but we use trylock201 * to avoid deadlocks and lockdep complaints.202 */203int204xrep_tempfile_iolock_polled(205 struct xfs_scrub *sc)206{207 int error = 0;208 209 while (!xrep_tempfile_iolock_nowait(sc)) {210 if (xchk_should_terminate(sc, &error))211 return error;212 delay(1);213 }214 215 return 0;216}217 218/* Release IOLOCK_EXCL on the temporary file. */219void220xrep_tempfile_iounlock(221 struct xfs_scrub *sc)222{223 xfs_iunlock(sc->tempip, XFS_IOLOCK_EXCL);224 sc->temp_ilock_flags &= ~XFS_IOLOCK_EXCL;225}226 227/* Prepare the temporary file for metadata updates by grabbing ILOCK_EXCL. */228void229xrep_tempfile_ilock(230 struct xfs_scrub *sc)231{232 sc->temp_ilock_flags |= XFS_ILOCK_EXCL;233 xfs_ilock(sc->tempip, XFS_ILOCK_EXCL);234}235 236/* Try to grab ILOCK_EXCL on the temporary file. */237bool238xrep_tempfile_ilock_nowait(239 struct xfs_scrub *sc)240{241 if (xfs_ilock_nowait(sc->tempip, XFS_ILOCK_EXCL)) {242 sc->temp_ilock_flags |= XFS_ILOCK_EXCL;243 return true;244 }245 246 return false;247}248 249/* Unlock ILOCK_EXCL on the temporary file after an update. */250void251xrep_tempfile_iunlock(252 struct xfs_scrub *sc)253{254 xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);255 sc->temp_ilock_flags &= ~XFS_ILOCK_EXCL;256}257 258/*259 * Begin the process of making changes to both the file being scrubbed and260 * the temporary file by taking ILOCK_EXCL on both.261 */262void263xrep_tempfile_ilock_both(264 struct xfs_scrub *sc)265{266 xfs_lock_two_inodes(sc->ip, XFS_ILOCK_EXCL, sc->tempip, XFS_ILOCK_EXCL);267 sc->ilock_flags |= XFS_ILOCK_EXCL;268 sc->temp_ilock_flags |= XFS_ILOCK_EXCL;269}270 271/* Unlock ILOCK_EXCL on both files. */272void273xrep_tempfile_iunlock_both(274 struct xfs_scrub *sc)275{276 xrep_tempfile_iunlock(sc);277 xchk_iunlock(sc, XFS_ILOCK_EXCL);278}279 280/* Release the temporary file. */281void282xrep_tempfile_rele(283 struct xfs_scrub *sc)284{285 if (!sc->tempip)286 return;287 288 if (sc->temp_ilock_flags) {289 xfs_iunlock(sc->tempip, sc->temp_ilock_flags);290 sc->temp_ilock_flags = 0;291 }292 293 xchk_irele(sc, sc->tempip);294 sc->tempip = NULL;295}296 297/*298 * Make sure that the given range of the data fork of the temporary file is299 * mapped to written blocks. The caller must ensure that both inodes are300 * joined to the transaction.301 */302int303xrep_tempfile_prealloc(304 struct xfs_scrub *sc,305 xfs_fileoff_t off,306 xfs_filblks_t len)307{308 struct xfs_bmbt_irec map;309 xfs_fileoff_t end = off + len;310 int error;311 312 ASSERT(sc->tempip != NULL);313 ASSERT(!XFS_NOT_DQATTACHED(sc->mp, sc->tempip));314 315 for (; off < end; off = map.br_startoff + map.br_blockcount) {316 int nmaps = 1;317 318 /*319 * If we have a real extent mapping this block then we're320 * in ok shape.321 */322 error = xfs_bmapi_read(sc->tempip, off, end - off, &map, &nmaps,323 XFS_DATA_FORK);324 if (error)325 return error;326 if (nmaps == 0) {327 ASSERT(nmaps != 0);328 return -EFSCORRUPTED;329 }330 331 if (xfs_bmap_is_written_extent(&map))332 continue;333 334 /*335 * If we find a delalloc reservation then something is very336 * very wrong. Bail out.337 */338 if (map.br_startblock == DELAYSTARTBLOCK)339 return -EFSCORRUPTED;340 341 /*342 * Make sure this block has a real zeroed extent allocated to343 * it.344 */345 nmaps = 1;346 error = xfs_bmapi_write(sc->tp, sc->tempip, off, end - off,347 XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO, 0, &map,348 &nmaps);349 if (error)350 return error;351 if (nmaps != 1)352 return -EFSCORRUPTED;353 354 trace_xrep_tempfile_prealloc(sc, XFS_DATA_FORK, &map);355 356 /* Commit new extent and all deferred work. */357 error = xfs_defer_finish(&sc->tp);358 if (error)359 return error;360 }361 362 return 0;363}364 365/*366 * Write data to each block of a file. The given range of the tempfile's data367 * fork must already be populated with written extents.368 */369int370xrep_tempfile_copyin(371 struct xfs_scrub *sc,372 xfs_fileoff_t off,373 xfs_filblks_t len,374 xrep_tempfile_copyin_fn prep_fn,375 void *data)376{377 LIST_HEAD(buffers_list);378 struct xfs_mount *mp = sc->mp;379 struct xfs_buf *bp;380 xfs_fileoff_t flush_mask;381 xfs_fileoff_t end = off + len;382 loff_t pos = XFS_FSB_TO_B(mp, off);383 int error = 0;384 385 ASSERT(S_ISREG(VFS_I(sc->tempip)->i_mode));386 387 /* Flush buffers to disk every 512K */388 flush_mask = XFS_B_TO_FSBT(mp, (1U << 19)) - 1;389 390 for (; off < end; off++, pos += mp->m_sb.sb_blocksize) {391 struct xfs_bmbt_irec map;392 int nmaps = 1;393 394 /* Read block mapping for this file block. */395 error = xfs_bmapi_read(sc->tempip, off, 1, &map, &nmaps, 0);396 if (error)397 goto out_err;398 if (nmaps == 0 || !xfs_bmap_is_written_extent(&map)) {399 error = -EFSCORRUPTED;400 goto out_err;401 }402 403 /* Get the metadata buffer for this offset in the file. */404 error = xfs_trans_get_buf(sc->tp, mp->m_ddev_targp,405 XFS_FSB_TO_DADDR(mp, map.br_startblock),406 mp->m_bsize, 0, &bp);407 if (error)408 goto out_err;409 410 trace_xrep_tempfile_copyin(sc, XFS_DATA_FORK, &map);411 412 /* Read in a block's worth of data from the xfile. */413 error = prep_fn(sc, bp, data);414 if (error) {415 xfs_trans_brelse(sc->tp, bp);416 goto out_err;417 }418 419 /* Queue buffer, and flush if we have too much dirty data. */420 xfs_buf_delwri_queue_here(bp, &buffers_list);421 xfs_trans_brelse(sc->tp, bp);422 423 if (!(off & flush_mask)) {424 error = xfs_buf_delwri_submit(&buffers_list);425 if (error)426 goto out_err;427 }428 }429 430 /*431 * Write the new blocks to disk. If the ordered list isn't empty after432 * that, then something went wrong and we have to fail. This should433 * never happen, but we'll check anyway.434 */435 error = xfs_buf_delwri_submit(&buffers_list);436 if (error)437 goto out_err;438 439 if (!list_empty(&buffers_list)) {440 ASSERT(list_empty(&buffers_list));441 error = -EIO;442 goto out_err;443 }444 445 return 0;446 447out_err:448 xfs_buf_delwri_cancel(&buffers_list);449 return error;450}451 452/*453 * Set the temporary file's size. Caller must join the tempfile to the scrub454 * transaction and is responsible for adjusting block mappings as needed.455 */456int457xrep_tempfile_set_isize(458 struct xfs_scrub *sc,459 unsigned long long isize)460{461 if (sc->tempip->i_disk_size == isize)462 return 0;463 464 sc->tempip->i_disk_size = isize;465 i_size_write(VFS_I(sc->tempip), isize);466 return xrep_tempfile_roll_trans(sc);467}468 469/*470 * Roll a repair transaction involving the temporary file. Caller must join471 * both the temporary file and the file being scrubbed to the transaction.472 * This function return with both inodes joined to a new scrub transaction,473 * or the usual negative errno.474 */475int476xrep_tempfile_roll_trans(477 struct xfs_scrub *sc)478{479 int error;480 481 xfs_trans_log_inode(sc->tp, sc->tempip, XFS_ILOG_CORE);482 error = xrep_roll_trans(sc);483 if (error)484 return error;485 486 xfs_trans_ijoin(sc->tp, sc->tempip, 0);487 return 0;488}489 490/*491 * Fill out the mapping exchange request in preparation for atomically492 * committing the contents of a metadata file that we've rebuilt in the temp493 * file.494 */495STATIC int496xrep_tempexch_prep_request(497 struct xfs_scrub *sc,498 int whichfork,499 struct xrep_tempexch *tx)500{501 struct xfs_exchmaps_req *req = &tx->req;502 503 memset(tx, 0, sizeof(struct xrep_tempexch));504 505 /* COW forks don't exist on disk. */506 if (whichfork == XFS_COW_FORK) {507 ASSERT(0);508 return -EINVAL;509 }510 511 /* Both files should have the relevant forks. */512 if (!xfs_ifork_ptr(sc->ip, whichfork) ||513 !xfs_ifork_ptr(sc->tempip, whichfork)) {514 ASSERT(xfs_ifork_ptr(sc->ip, whichfork) != NULL);515 ASSERT(xfs_ifork_ptr(sc->tempip, whichfork) != NULL);516 return -EINVAL;517 }518 519 /* Exchange all mappings in both forks. */520 req->ip1 = sc->tempip;521 req->ip2 = sc->ip;522 req->startoff1 = 0;523 req->startoff2 = 0;524 switch (whichfork) {525 case XFS_ATTR_FORK:526 req->flags |= XFS_EXCHMAPS_ATTR_FORK;527 break;528 case XFS_DATA_FORK:529 /* Always exchange sizes when exchanging data fork mappings. */530 req->flags |= XFS_EXCHMAPS_SET_SIZES;531 break;532 }533 req->blockcount = XFS_MAX_FILEOFF;534 535 return 0;536}537 538/*539 * Fill out the mapping exchange resource estimation structures in preparation540 * for exchanging the contents of a metadata file that we've rebuilt in the541 * temp file. Caller must hold IOLOCK_EXCL but not ILOCK_EXCL on both files.542 */543STATIC int544xrep_tempexch_estimate(545 struct xfs_scrub *sc,546 struct xrep_tempexch *tx)547{548 struct xfs_exchmaps_req *req = &tx->req;549 struct xfs_ifork *ifp;550 struct xfs_ifork *tifp;551 int whichfork = xfs_exchmaps_reqfork(req);552 int state = 0;553 554 /*555 * The exchmaps code only knows how to exchange file fork space556 * mappings. Any fork data in local format must be promoted to a557 * single block before the exchange can take place.558 */559 ifp = xfs_ifork_ptr(sc->ip, whichfork);560 if (ifp->if_format == XFS_DINODE_FMT_LOCAL)561 state |= 1;562 563 tifp = xfs_ifork_ptr(sc->tempip, whichfork);564 if (tifp->if_format == XFS_DINODE_FMT_LOCAL)565 state |= 2;566 567 switch (state) {568 case 0:569 /* Both files have mapped extents; use the regular estimate. */570 return xfs_exchrange_estimate(req);571 case 1:572 /*573 * The file being repaired is in local format, but the temp574 * file has mapped extents. To perform the exchange, the file575 * being repaired must have its shorform data converted to an576 * ondisk block so that the forks will be in extents format.577 * We need one resblk for the conversion; the number of578 * exchanges is (worst case) the temporary file's extent count579 * plus the block we converted.580 */581 req->ip1_bcount = sc->tempip->i_nblocks;582 req->ip2_bcount = 1;583 req->nr_exchanges = 1 + tifp->if_nextents;584 req->resblks = 1;585 break;586 case 2:587 /*588 * The temporary file is in local format, but the file being589 * repaired has mapped extents. To perform the exchange, the590 * temp file must have its shortform data converted to an591 * ondisk block, and the fork changed to extents format. We592 * need one resblk for the conversion; the number of exchanges593 * is (worst case) the extent count of the file being repaired594 * plus the block we converted.595 */596 req->ip1_bcount = 1;597 req->ip2_bcount = sc->ip->i_nblocks;598 req->nr_exchanges = 1 + ifp->if_nextents;599 req->resblks = 1;600 break;601 case 3:602 /*603 * Both forks are in local format. To perform the exchange,604 * both files must have their shortform data converted to605 * fsblocks, and both forks must be converted to extents606 * format. We need two resblks for the two conversions, and607 * the number of exchanges is 1 since there's only one block at608 * fileoff 0. Presumably, the caller could not exchange the609 * two inode fork areas directly.610 */611 req->ip1_bcount = 1;612 req->ip2_bcount = 1;613 req->nr_exchanges = 1;614 req->resblks = 2;615 break;616 }617 618 return xfs_exchmaps_estimate_overhead(req);619}620 621/*622 * Obtain a quota reservation to make sure we don't hit EDQUOT. We can skip623 * this if quota enforcement is disabled or if both inodes' dquots are the624 * same. The qretry structure must be initialized to zeroes before the first625 * call to this function.626 */627STATIC int628xrep_tempexch_reserve_quota(629 struct xfs_scrub *sc,630 const struct xrep_tempexch *tx)631{632 struct xfs_trans *tp = sc->tp;633 const struct xfs_exchmaps_req *req = &tx->req;634 int64_t ddelta, rdelta;635 int error;636 637 /*638 * Don't bother with a quota reservation if we're not enforcing them639 * or the two inodes have the same dquots.640 */641 if (!XFS_IS_QUOTA_ON(tp->t_mountp) || req->ip1 == req->ip2 ||642 (req->ip1->i_udquot == req->ip2->i_udquot &&643 req->ip1->i_gdquot == req->ip2->i_gdquot &&644 req->ip1->i_pdquot == req->ip2->i_pdquot))645 return 0;646 647 /*648 * Quota reservation for each file comes from two sources. First, we649 * need to account for any net gain in mapped blocks during the650 * exchange. Second, we need reservation for the gross gain in mapped651 * blocks so that we don't trip over any quota block reservation652 * assertions. We must reserve the gross gain because the quota code653 * subtracts from bcount the number of blocks that we unmap; it does654 * not add that quantity back to the quota block reservation.655 */656 ddelta = max_t(int64_t, 0, req->ip2_bcount - req->ip1_bcount);657 rdelta = max_t(int64_t, 0, req->ip2_rtbcount - req->ip1_rtbcount);658 error = xfs_trans_reserve_quota_nblks(tp, req->ip1,659 ddelta + req->ip1_bcount, rdelta + req->ip1_rtbcount,660 true);661 if (error)662 return error;663 664 ddelta = max_t(int64_t, 0, req->ip1_bcount - req->ip2_bcount);665 rdelta = max_t(int64_t, 0, req->ip1_rtbcount - req->ip2_rtbcount);666 return xfs_trans_reserve_quota_nblks(tp, req->ip2,667 ddelta + req->ip2_bcount, rdelta + req->ip2_rtbcount,668 true);669}670 671/*672 * Prepare an existing transaction for an atomic file contents exchange.673 *674 * This function fills out the mapping exchange request and resource estimation675 * structures in preparation for exchanging the contents of a metadata file676 * that has been rebuilt in the temp file. Next, it reserves space and quota677 * for the transaction.678 *679 * The caller must hold ILOCK_EXCL of the scrub target file and the temporary680 * file. The caller must join both inodes to the transaction with no unlock681 * flags, and is responsible for dropping both ILOCKs when appropriate. Only682 * use this when those ILOCKs cannot be dropped.683 */684int685xrep_tempexch_trans_reserve(686 struct xfs_scrub *sc,687 int whichfork,688 struct xrep_tempexch *tx)689{690 int error;691 692 ASSERT(sc->tp != NULL);693 xfs_assert_ilocked(sc->ip, XFS_ILOCK_EXCL);694 xfs_assert_ilocked(sc->tempip, XFS_ILOCK_EXCL);695 696 error = xrep_tempexch_prep_request(sc, whichfork, tx);697 if (error)698 return error;699 700 error = xfs_exchmaps_estimate(&tx->req);701 if (error)702 return error;703 704 error = xfs_trans_reserve_more(sc->tp, tx->req.resblks, 0);705 if (error)706 return error;707 708 return xrep_tempexch_reserve_quota(sc, tx);709}710 711/*712 * Create a new transaction for a file contents exchange.713 *714 * This function fills out the mapping excahange request and resource715 * estimation structures in preparation for exchanging the contents of a716 * metadata file that has been rebuilt in the temp file. Next, it reserves717 * space, takes ILOCK_EXCL of both inodes, joins them to the transaction and718 * reserves quota for the transaction.719 *720 * The caller is responsible for dropping both ILOCKs when appropriate.721 */722int723xrep_tempexch_trans_alloc(724 struct xfs_scrub *sc,725 int whichfork,726 struct xrep_tempexch *tx)727{728 unsigned int flags = 0;729 int error;730 731 ASSERT(sc->tp == NULL);732 ASSERT(xfs_has_exchange_range(sc->mp));733 734 error = xrep_tempexch_prep_request(sc, whichfork, tx);735 if (error)736 return error;737 738 error = xrep_tempexch_estimate(sc, tx);739 if (error)740 return error;741 742 if (xfs_has_lazysbcount(sc->mp))743 flags |= XFS_TRANS_RES_FDBLKS;744 745 error = xfs_trans_alloc(sc->mp, &M_RES(sc->mp)->tr_itruncate,746 tx->req.resblks, 0, flags, &sc->tp);747 if (error)748 return error;749 750 sc->temp_ilock_flags |= XFS_ILOCK_EXCL;751 sc->ilock_flags |= XFS_ILOCK_EXCL;752 xfs_exchrange_ilock(sc->tp, sc->ip, sc->tempip);753 754 return xrep_tempexch_reserve_quota(sc, tx);755}756 757/*758 * Exchange file mappings (and hence file contents) between the file being759 * repaired and the temporary file. Returns with both inodes locked and joined760 * to a clean scrub transaction.761 */762int763xrep_tempexch_contents(764 struct xfs_scrub *sc,765 struct xrep_tempexch *tx)766{767 int error;768 769 ASSERT(xfs_has_exchange_range(sc->mp));770 771 xfs_exchange_mappings(sc->tp, &tx->req);772 error = xfs_defer_finish(&sc->tp);773 if (error)774 return error;775 776 /*777 * If we exchanged the ondisk sizes of two metadata files, we must778 * exchanged the incore sizes as well.779 */780 if (tx->req.flags & XFS_EXCHMAPS_SET_SIZES) {781 loff_t temp;782 783 temp = i_size_read(VFS_I(sc->ip));784 i_size_write(VFS_I(sc->ip), i_size_read(VFS_I(sc->tempip)));785 i_size_write(VFS_I(sc->tempip), temp);786 }787 788 return 0;789}790 791/*792 * Write local format data from one of the temporary file's forks into the same793 * fork of file being repaired, and exchange the file sizes, if appropriate.794 * Caller must ensure that the file being repaired has enough fork space to795 * hold all the bytes.796 */797void798xrep_tempfile_copyout_local(799 struct xfs_scrub *sc,800 int whichfork)801{802 struct xfs_ifork *temp_ifp;803 struct xfs_ifork *ifp;804 unsigned int ilog_flags = XFS_ILOG_CORE;805 806 temp_ifp = xfs_ifork_ptr(sc->tempip, whichfork);807 ifp = xfs_ifork_ptr(sc->ip, whichfork);808 809 ASSERT(temp_ifp != NULL);810 ASSERT(ifp != NULL);811 ASSERT(temp_ifp->if_format == XFS_DINODE_FMT_LOCAL);812 ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);813 814 switch (whichfork) {815 case XFS_DATA_FORK:816 ASSERT(sc->tempip->i_disk_size <=817 xfs_inode_data_fork_size(sc->ip));818 break;819 case XFS_ATTR_FORK:820 ASSERT(sc->tempip->i_forkoff >= sc->ip->i_forkoff);821 break;822 default:823 ASSERT(0);824 return;825 }826 827 /* Recreate @sc->ip's incore fork (ifp) with data from temp_ifp. */828 xfs_idestroy_fork(ifp);829 xfs_init_local_fork(sc->ip, whichfork, temp_ifp->if_data,830 temp_ifp->if_bytes);831 832 if (whichfork == XFS_DATA_FORK) {833 i_size_write(VFS_I(sc->ip), i_size_read(VFS_I(sc->tempip)));834 sc->ip->i_disk_size = sc->tempip->i_disk_size;835 }836 837 ilog_flags |= xfs_ilog_fdata(whichfork);838 xfs_trans_log_inode(sc->tp, sc->ip, ilog_flags);839}840 841/* Decide if a given XFS inode is a temporary file for a repair. */842bool843xrep_is_tempfile(844 const struct xfs_inode *ip)845{846 const struct inode *inode = &ip->i_vnode;847 848 if (IS_PRIVATE(inode) && !(inode->i_opflags & IOP_XATTR))849 return true;850 851 return false;852}853