blob: b752b4c871ea838915dc620ad4db729baf0971fd [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include "threads.h"
#include "prefetch.h"
#include "avl.h"
#include "globals.h"
#include "agheader.h"
#include "incore.h"
#include "protos.h"
#include "err_protos.h"
#include "dinode.h"
#include "bmap.h"
#include "versions.h"
#include "dir2.h"
#include "progress.h"
#include "slab.h"
#include "rmap.h"
bool collect_rmaps;
static inline void
quotino_check_one(
struct xfs_mount *mp,
xfs_dqtype_t type)
{
struct ino_tree_node *irec;
xfs_ino_t ino;
if (!has_quota_inode(type))
return;
ino = get_quota_inode(type);
if (!libxfs_verify_ino(mp, ino))
goto bad;
irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, ino),
XFS_INO_TO_AGINO(mp, ino));
if (!irec)
goto bad;
if (is_inode_free(irec, ino - irec->ino_startnum))
goto bad;
return;
bad:
lose_quota_inode(type);
}
/*
* null out quota inode fields in sb if they point to non-existent inodes.
* this isn't as redundant as it looks since it's possible that the sb field
* might be set but the imap and inode(s) agree that the inode is
* free in which case they'd never be cleared so the fields wouldn't
* be cleared by process_dinode().
*/
static void
quotino_check(
struct xfs_mount *mp)
{
quotino_check_one(mp, XFS_DQTYPE_USER);
quotino_check_one(mp, XFS_DQTYPE_GROUP);
quotino_check_one(mp, XFS_DQTYPE_PROJ);
}
static void
quota_sb_check(xfs_mount_t *mp)
{
if (xfs_has_metadir(mp)) {
/*
* Metadir filesystems try to preserve the quota accounting
* and enforcement flags so that users don't have to remember
* to supply quota mount options. Phase 1 discovered the
* QUOTABIT flag (fs_quotas) and phase 2 discovered the quota
* inodes from the metadir for us.
*
* If QUOTABIT wasn't set but we found quota inodes, signal
* phase 5 to add the feature bit for us. We do not ever
* downgrade the filesystem.
*/
if (!fs_quotas &&
(has_quota_inode(XFS_DQTYPE_USER) ||
has_quota_inode(XFS_DQTYPE_GROUP) ||
has_quota_inode(XFS_DQTYPE_PROJ)))
fs_quotas = 1;
return;
}
/*
* if the sb says we have quotas and we lost both,
* signal a superblock downgrade. that will cause
* the quota flags to get zeroed. (if we only lost
* one quota inode, do nothing and complain later.)
*
* if the sb says we have quotas but we didn't start out
* with any quota inodes, signal a superblock downgrade.
*
* The sb downgrades are so that older systems can mount
* the filesystem.
*
* if the sb says we don't have quotas but it looks like
* we do have quota inodes, then signal a superblock upgrade.
*
* if the sb says we don't have quotas and we have no
* quota inodes, then leave will enough alone.
*/
if (fs_quotas &&
!has_quota_inode(XFS_DQTYPE_USER) &&
!has_quota_inode(XFS_DQTYPE_GROUP) &&
!has_quota_inode(XFS_DQTYPE_PROJ)) {
lost_quotas = 1;
fs_quotas = 0;
} else if (libxfs_verify_ino(mp, get_quota_inode(XFS_DQTYPE_USER)) &&
libxfs_verify_ino(mp, get_quota_inode(XFS_DQTYPE_GROUP)) &&
libxfs_verify_ino(mp, get_quota_inode(XFS_DQTYPE_PROJ))) {
fs_quotas = 1;
}
}
static void
process_ag_func(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
wait_for_inode_prefetch(arg);
do_log(_(" - agno = %d\n"), agno);
process_aginodes(wq->wq_ctx, arg, agno, 0, 1, 0);
blkmap_free_final();
cleanup_inode_prefetch(arg);
/*
* now recycle the per-AG duplicate extent records
*/
release_dup_extent_tree(agno);
}
static void
process_ags(
xfs_mount_t *mp)
{
do_inode_prefetch(mp, ag_stride, process_ag_func, true, false);
}
static void
check_rmap_btrees(
struct workqueue*wq,
xfs_agnumber_t agno,
void *arg)
{
rmap_add_fixed_ag_rec(wq->wq_ctx, agno);
rmaps_verify_btree(wq->wq_ctx, agno);
}
static void
check_rtrmap_btrees(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
rmap_add_fixed_rtgroup_rec(wq->wq_ctx, agno);
rtrmaps_verify_btree(wq->wq_ctx, agno);
}
static void
compute_ag_refcounts(
struct workqueue*wq,
xfs_agnumber_t agno,
void *arg)
{
int error;
error = compute_refcounts(wq->wq_ctx, false, agno);
if (error)
do_error(
_("%s while computing reference count records.\n"),
strerror(error));
}
static void
compute_rt_refcounts(
struct workqueue*wq,
xfs_agnumber_t rgno,
void *arg)
{
int error;
error = compute_refcounts(wq->wq_ctx, true, rgno);
if (error)
do_error(
_("%s while computing realtime reference count records.\n"),
strerror(error));
}
static void
process_inode_reflink_flags(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
int error;
error = fix_inode_reflink_flags(wq->wq_ctx, agno);
if (error)
do_error(
_("%s while fixing inode reflink flags.\n"),
strerror(-error));
}
static void
check_refcount_btrees(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
check_refcounts(wq->wq_ctx, agno);
}
static void
check_rt_refcount_btrees(
struct workqueue *wq,
xfs_agnumber_t agno,
void *arg)
{
check_rtrefcounts(wq->wq_ctx, agno);
}
static void
process_rmap_data(
struct xfs_mount *mp)
{
struct workqueue wq;
xfs_agnumber_t i;
if (!rmap_needs_work(mp))
return;
create_work_queue(&wq, mp, platform_nproc());
for (i = 0; i < mp->m_sb.sb_agcount; i++)
queue_work(&wq, check_rmap_btrees, i, NULL);
if (xfs_has_rtrmapbt(mp)) {
for (i = 0; i < mp->m_sb.sb_rgcount; i++)
queue_work(&wq, check_rtrmap_btrees, i, NULL);
}
destroy_work_queue(&wq);
if (!xfs_has_reflink(mp))
return;
create_work_queue(&wq, mp, platform_nproc());
for (i = 0; i < mp->m_sb.sb_agcount; i++)
queue_work(&wq, compute_ag_refcounts, i, NULL);
if (xfs_has_rtreflink(mp)) {
for (i = 0; i < mp->m_sb.sb_rgcount; i++)
queue_work(&wq, compute_rt_refcounts, i, NULL);
}
destroy_work_queue(&wq);
create_work_queue(&wq, mp, platform_nproc());
for (i = 0; i < mp->m_sb.sb_agcount; i++) {
queue_work(&wq, process_inode_reflink_flags, i, NULL);
queue_work(&wq, check_refcount_btrees, i, NULL);
}
if (xfs_has_rtreflink(mp)) {
for (i = 0; i < mp->m_sb.sb_rgcount; i++)
queue_work(&wq, check_rt_refcount_btrees, i, NULL);
}
destroy_work_queue(&wq);
}
static void
process_dup_rt_extents(
struct xfs_mount *mp)
{
xfs_rtxnum_t rt_start = 0;
xfs_rtxlen_t rt_len = 0;
xfs_rtxnum_t rtx;
for (rtx = 0; rtx < mp->m_sb.sb_rextents; rtx++) {
int state;
state = get_rtbmap(rtx);
switch (state) {
case XR_E_BAD_STATE:
default:
do_warn(
_("unknown rt extent state %d, extent %" PRIu64 "\n"),
state, rtx);
fallthrough;
case XR_E_METADATA:
case XR_E_UNKNOWN:
case XR_E_FREE1:
case XR_E_FREE:
case XR_E_INUSE:
case XR_E_INUSE_FS:
case XR_E_INO:
case XR_E_FS_MAP:
if (rt_start == 0)
continue;
/*
* Add extent and reset extent state.
*/
add_rt_dup_extent(rt_start, rt_len);
rt_start = 0;
rt_len = 0;
break;
case XR_E_MULT:
switch (rt_start) {
case 0:
rt_start = rtx;
rt_len = 1;
break;
case XFS_MAX_BMBT_EXTLEN:
/*
* Large extent case.
*/
add_rt_dup_extent(rt_start, rt_len);
rt_start = rtx;
rt_len = 1;
break;
default:
rt_len++;
break;
}
break;
}
}
/*
* Catch the tail case, extent hitting the end of the RTG.
*/
if (rt_start != 0)
add_rt_dup_extent(rt_start, rt_len);
}
/*
* Set up duplicate extent list for an AG or RTG.
*/
static void
process_dup_extents(
struct xfs_mount *mp,
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_agblock_t ag_end,
bool isrt)
{
do {
int bstate;
xfs_extlen_t blen;
bstate = get_bmap_ext(agno, agbno, ag_end, &blen, isrt);
switch (bstate) {
case XR_E_FREE1:
if (no_modify)
do_warn(
_("free space (%u,%u-%u) only seen by one free space btree\n"),
agno, agbno, agbno + blen - 1);
break;
case XR_E_METADATA:
case XR_E_UNKNOWN:
case XR_E_FREE:
case XR_E_INUSE:
case XR_E_INUSE_FS:
case XR_E_INO:
case XR_E_FS_MAP:
break;
case XR_E_MULT:
/*
* Nothing is searching for duplicate RT extents, so
* don't bother tracking them.
*/
if (!isrt)
add_dup_extent(agno, agbno, blen);
break;
case XR_E_BAD_STATE:
default:
do_warn(
_("unknown block state, ag %d, blocks %u-%u\n"),
agno, agbno, agbno + blen - 1);
break;
}
agbno += blen;
} while (agbno < ag_end);
}
void
phase4(xfs_mount_t *mp)
{
ino_tree_node_t *irec;
xfs_agnumber_t i;
int ag_hdr_len = 4 * mp->m_sb.sb_sectsize;
int ag_hdr_block;
if (rmap_needs_work(mp))
collect_rmaps = true;
ag_hdr_block = howmany(ag_hdr_len, mp->m_sb.sb_blocksize);
do_log(_("Phase 4 - check for duplicate blocks...\n"));
do_log(_(" - setting up duplicate extent list...\n"));
set_progress_msg(PROG_FMT_DUP_EXTENT, (uint64_t) glob_agcount);
irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino));
/*
* we always have a root inode, even if it's free...
* if the root is free, forget it, lost+found is already gone
*/
if (is_inode_free(irec, 0) || !inode_isadir(irec, 0)) {
need_root_inode = 1;
if (no_modify)
do_warn(_("root inode would be lost\n"));
else
do_warn(_("root inode lost\n"));
}
/*
* If metadata directory trees are enabled, the metadata root directory
* always comes immediately after the regular root directory, even if
* it's free.
*/
if (xfs_has_metadir(mp) &&
(is_inode_free(irec, 1) || !inode_isadir(irec, 1))) {
need_metadir_inode = true;
if (no_modify)
do_warn(
_("metadata directory root inode would be lost\n"));
else
do_warn(
_("metadata directory root inode lost\n"));
}
for (i = 0; i < mp->m_sb.sb_agcount; i++) {
xfs_agblock_t ag_end;
ag_end = (i < mp->m_sb.sb_agcount - 1) ? mp->m_sb.sb_agblocks :
mp->m_sb.sb_dblocks -
(xfs_rfsblock_t) mp->m_sb.sb_agblocks * i;
process_dup_extents(mp, i, ag_hdr_block, ag_end, false);
PROG_RPT_INC(prog_rpt_done[i], 1);
}
print_final_rpt();
if (xfs_has_rtgroups(mp)) {
for (i = 0; i < mp->m_sb.sb_rgcount; i++) {
uint64_t rblocks;
rblocks = xfs_rtbxlen_to_blen(mp,
libxfs_rtgroup_extents(mp, i));
process_dup_extents(mp, i, 0, rblocks, true);
}
} else {
process_dup_rt_extents(mp);
}
/*
* initialize bitmaps for all AGs
*/
reset_bmaps(mp);
do_log(_(" - check for inodes claiming duplicate blocks...\n"));
set_progress_msg(PROG_FMT_DUP_BLOCKS, (uint64_t) mp->m_sb.sb_icount);
/*
* ok, now process the inodes -- signal 2-pass check per inode.
* first pass checks if the inode conflicts with a known
* duplicate extent. if so, the inode is cleared and second
* pass is skipped. second pass sets the block bitmap
* for all blocks claimed by the inode. directory
* and attribute processing is turned OFF since we did that
* already in phase 3.
*/
process_ags(mp);
/*
* Process all the reverse-mapping data that we collected. This
* involves checking the rmap data against the btree, computing
* reference counts based on the rmap data, and checking the counts
* against the refcount btree.
*/
process_rmap_data(mp);
print_final_rpt();
/*
* free up memory used to track trealtime duplicate extents
*/
free_rt_dup_extent_tree(mp);
/*
* ensure consistency of quota inode pointers in superblock,
* make sure they point to real inodes
*/
quotino_check(mp);
quota_sb_check(mp);
/* Check the rt metadata before we rebuild */
if (mp->m_sb.sb_rblocks) {
do_log(
_(" - generate realtime summary info and bitmap...\n"));
check_rtmetadata(mp);
}
}