201 lines
4.8 KiB
C
201 lines
4.8 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* Copyright (C) 2020-2023 Oracle. All Rights Reserved.
|
||
|
* Author: Darrick J. Wong <djwong@kernel.org>
|
||
|
*/
|
||
|
#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_btree.h"
|
||
|
#include "xfs_log_format.h"
|
||
|
#include "xfs_trans.h"
|
||
|
#include "xfs_inode.h"
|
||
|
#include "xfs_bit.h"
|
||
|
#include "xfs_bmap.h"
|
||
|
#include "xfs_bmap_btree.h"
|
||
|
#include "scrub/scrub.h"
|
||
|
#include "scrub/common.h"
|
||
|
#include "scrub/trace.h"
|
||
|
#include "scrub/repair.h"
|
||
|
#include "scrub/xfile.h"
|
||
|
#include "scrub/rtbitmap.h"
|
||
|
|
||
|
/* Set up to repair the realtime bitmap file metadata. */
|
||
|
int
|
||
|
xrep_setup_rtbitmap(
|
||
|
struct xfs_scrub *sc,
|
||
|
struct xchk_rtbitmap *rtb)
|
||
|
{
|
||
|
struct xfs_mount *mp = sc->mp;
|
||
|
unsigned long long blocks = 0;
|
||
|
|
||
|
/*
|
||
|
* Reserve enough blocks to write out a completely new bmbt for a
|
||
|
* maximally fragmented bitmap file. We do not hold the rtbitmap
|
||
|
* ILOCK yet, so this is entirely speculative.
|
||
|
*/
|
||
|
blocks = xfs_bmbt_calc_size(mp, mp->m_sb.sb_rbmblocks);
|
||
|
if (blocks > UINT_MAX)
|
||
|
return -EOPNOTSUPP;
|
||
|
|
||
|
rtb->resblks += blocks;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Make sure that the given range of the data fork of the realtime file is
|
||
|
* mapped to written blocks. The caller must ensure that the inode is joined
|
||
|
* to the transaction.
|
||
|
*/
|
||
|
STATIC int
|
||
|
xrep_rtbitmap_data_mappings(
|
||
|
struct xfs_scrub *sc,
|
||
|
xfs_filblks_t len)
|
||
|
{
|
||
|
struct xfs_bmbt_irec map;
|
||
|
xfs_fileoff_t off = 0;
|
||
|
int error;
|
||
|
|
||
|
ASSERT(sc->ip != NULL);
|
||
|
|
||
|
while (off < len) {
|
||
|
int nmaps = 1;
|
||
|
|
||
|
/*
|
||
|
* If we have a real extent mapping this block then we're
|
||
|
* in ok shape.
|
||
|
*/
|
||
|
error = xfs_bmapi_read(sc->ip, off, len - off, &map, &nmaps,
|
||
|
XFS_DATA_FORK);
|
||
|
if (error)
|
||
|
return error;
|
||
|
if (nmaps == 0) {
|
||
|
ASSERT(nmaps != 0);
|
||
|
return -EFSCORRUPTED;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Written extents are ok. Holes are not filled because we
|
||
|
* do not know the freespace information.
|
||
|
*/
|
||
|
if (xfs_bmap_is_written_extent(&map) ||
|
||
|
map.br_startblock == HOLESTARTBLOCK) {
|
||
|
off = map.br_startoff + map.br_blockcount;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If we find a delalloc reservation then something is very
|
||
|
* very wrong. Bail out.
|
||
|
*/
|
||
|
if (map.br_startblock == DELAYSTARTBLOCK)
|
||
|
return -EFSCORRUPTED;
|
||
|
|
||
|
/* Make sure we're really converting an unwritten extent. */
|
||
|
if (map.br_state != XFS_EXT_UNWRITTEN) {
|
||
|
ASSERT(map.br_state == XFS_EXT_UNWRITTEN);
|
||
|
return -EFSCORRUPTED;
|
||
|
}
|
||
|
|
||
|
/* Make sure this block has a real zeroed extent mapped. */
|
||
|
nmaps = 1;
|
||
|
error = xfs_bmapi_write(sc->tp, sc->ip, map.br_startoff,
|
||
|
map.br_blockcount,
|
||
|
XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO,
|
||
|
0, &map, &nmaps);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
/* Commit new extent and all deferred work. */
|
||
|
error = xrep_defer_finish(sc);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
off = map.br_startoff + map.br_blockcount;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Fix broken rt volume geometry. */
|
||
|
STATIC int
|
||
|
xrep_rtbitmap_geometry(
|
||
|
struct xfs_scrub *sc,
|
||
|
struct xchk_rtbitmap *rtb)
|
||
|
{
|
||
|
struct xfs_mount *mp = sc->mp;
|
||
|
struct xfs_trans *tp = sc->tp;
|
||
|
|
||
|
/* Superblock fields */
|
||
|
if (mp->m_sb.sb_rextents != rtb->rextents)
|
||
|
xfs_trans_mod_sb(sc->tp, XFS_TRANS_SB_REXTENTS,
|
||
|
rtb->rextents - mp->m_sb.sb_rextents);
|
||
|
|
||
|
if (mp->m_sb.sb_rbmblocks != rtb->rbmblocks)
|
||
|
xfs_trans_mod_sb(tp, XFS_TRANS_SB_RBMBLOCKS,
|
||
|
rtb->rbmblocks - mp->m_sb.sb_rbmblocks);
|
||
|
|
||
|
if (mp->m_sb.sb_rextslog != rtb->rextslog)
|
||
|
xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTSLOG,
|
||
|
rtb->rextslog - mp->m_sb.sb_rextslog);
|
||
|
|
||
|
/* Fix broken isize */
|
||
|
sc->ip->i_disk_size = roundup_64(sc->ip->i_disk_size,
|
||
|
mp->m_sb.sb_blocksize);
|
||
|
|
||
|
if (sc->ip->i_disk_size < XFS_FSB_TO_B(mp, rtb->rbmblocks))
|
||
|
sc->ip->i_disk_size = XFS_FSB_TO_B(mp, rtb->rbmblocks);
|
||
|
|
||
|
xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
|
||
|
return xrep_roll_trans(sc);
|
||
|
}
|
||
|
|
||
|
/* Repair the realtime bitmap file metadata. */
|
||
|
int
|
||
|
xrep_rtbitmap(
|
||
|
struct xfs_scrub *sc)
|
||
|
{
|
||
|
struct xchk_rtbitmap *rtb = sc->buf;
|
||
|
struct xfs_mount *mp = sc->mp;
|
||
|
unsigned long long blocks = 0;
|
||
|
int error;
|
||
|
|
||
|
/* Impossibly large rtbitmap means we can't touch the filesystem. */
|
||
|
if (rtb->rbmblocks > U32_MAX)
|
||
|
return 0;
|
||
|
|
||
|
/*
|
||
|
* If the size of the rt bitmap file is larger than what we reserved,
|
||
|
* figure out if we need to adjust the block reservation in the
|
||
|
* transaction.
|
||
|
*/
|
||
|
blocks = xfs_bmbt_calc_size(mp, rtb->rbmblocks);
|
||
|
if (blocks > UINT_MAX)
|
||
|
return -EOPNOTSUPP;
|
||
|
if (blocks > rtb->resblks) {
|
||
|
error = xfs_trans_reserve_more(sc->tp, blocks, 0);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
rtb->resblks += blocks;
|
||
|
}
|
||
|
|
||
|
/* Fix inode core and forks. */
|
||
|
error = xrep_metadata_inode_forks(sc);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
xfs_trans_ijoin(sc->tp, sc->ip, 0);
|
||
|
|
||
|
/* Ensure no unwritten extents. */
|
||
|
error = xrep_rtbitmap_data_mappings(sc, rtb->rbmblocks);
|
||
|
if (error)
|
||
|
return error;
|
||
|
|
||
|
/* Fix inconsistent bitmap geometry */
|
||
|
return xrep_rtbitmap_geometry(sc, rtb);
|
||
|
}
|