blob: b4ede461805d981902f1b8996b3b358502f13f21 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include "avl.h"
#include "globals.h"
#include "agheader.h"
#include "incore.h"
#include "protos.h"
#include "err_protos.h"
#include "dinode.h"
#include "versions.h"
#include "prefetch.h"
#include "progress.h"
/*
* validates inode block or chunk, returns # of good inodes
* the dinodes are verified using verify_uncertain_dinode() which
* means only the basic inode info is checked, no fork checks.
*/
static int
check_aginode_block(xfs_mount_t *mp,
xfs_agnumber_t agno,
xfs_agblock_t agbno)
{
xfs_dinode_t *dino_p;
int i;
int cnt = 0;
xfs_buf_t *bp;
/*
* it's ok to read these possible inode blocks in one at
* a time because they don't belong to known inodes (if
* they did, we'd know about them courtesy of the incore inode
* tree and we wouldn't be here and we stale the buffers out
* so no one else will overlap them.
*/
bp = libxfs_readbuf(mp->m_dev, XFS_AGB_TO_DADDR(mp, agno, agbno),
XFS_FSB_TO_BB(mp, 1), 0, NULL);
if (!bp) {
do_warn(_("cannot read agbno (%u/%u), disk block %" PRId64 "\n"),
agno, agbno, XFS_AGB_TO_DADDR(mp, agno, agbno));
return(0);
}
for (i = 0; i < mp->m_sb.sb_inopblock; i++) {
dino_p = xfs_make_iptr(mp, bp, i);
if (!verify_uncertain_dinode(mp, dino_p, agno,
XFS_OFFBNO_TO_AGINO(mp, agbno, i)))
cnt++;
}
if (cnt)
bp->b_ops = &xfs_inode_buf_ops;
libxfs_putbuf(bp);
return(cnt);
}
/*
* tries to establish if the inode really exists in a valid
* inode chunk. returns number of new inodes if things are good
* and 0 if bad. start is the start of the discovered inode chunk.
* routine assumes that ino is a legal inode number
* (verified by verify_inum()). If the inode chunk turns out
* to be good, this routine will put the inode chunk into
* the good inode chunk tree if required.
*
* the verify_(ag)inode* family of routines are utility
* routines called by check_uncertain_aginodes() and
* process_uncertain_aginodes().
*/
static int
verify_inode_chunk(xfs_mount_t *mp,
xfs_ino_t ino,
xfs_ino_t *start_ino)
{
xfs_agnumber_t agno;
xfs_agino_t agino;
xfs_agino_t start_agino;
xfs_agblock_t agbno;
xfs_agblock_t start_agbno = 0;
xfs_agblock_t end_agbno;
xfs_agblock_t max_agbno;
xfs_agblock_t cur_agbno;
xfs_agblock_t chunk_start_agbno;
xfs_agblock_t chunk_stop_agbno;
ino_tree_node_t *irec_before_p = NULL;
ino_tree_node_t *irec_after_p = NULL;
ino_tree_node_t *irec_p;
ino_tree_node_t *irec_next_p;
int irec_cnt;
int ino_cnt = 0;
int num_blks;
int i;
int j;
int state;
xfs_extlen_t blen;
agno = XFS_INO_TO_AGNO(mp, ino);
agino = XFS_INO_TO_AGINO(mp, ino);
agbno = XFS_INO_TO_AGBNO(mp, ino);
*start_ino = NULLFSINO;
ASSERT(mp->m_ialloc_blks > 0);
if (agno == mp->m_sb.sb_agcount - 1)
max_agbno = mp->m_sb.sb_dblocks -
(xfs_rfsblock_t) mp->m_sb.sb_agblocks * agno;
else
max_agbno = mp->m_sb.sb_agblocks;
/*
* is the inode beyond the end of the AG?
*/
if (agbno >= max_agbno)
return(0);
/*
* check for the easy case, inodes per block >= XFS_INODES_PER_CHUNK
* (multiple chunks per block)
*/
if (mp->m_ialloc_blks == 1) {
if (agbno > max_agbno)
return 0;
if (check_aginode_block(mp, agno, agino) == 0)
return 0;
pthread_mutex_lock(&ag_locks[agno].lock);
state = get_bmap(agno, agbno);
switch (state) {
case XR_E_INO:
do_warn(
_("uncertain inode block %d/%d already known\n"),
agno, agbno);
break;
case XR_E_UNKNOWN:
case XR_E_FREE1:
case XR_E_FREE:
set_bmap(agno, agbno, XR_E_INO);
break;
case XR_E_MULT:
case XR_E_INUSE:
case XR_E_INUSE_FS:
case XR_E_FS_MAP:
/*
* if block is already claimed, forget it.
*/
do_warn(
_("inode block %d/%d multiply claimed, (state %d)\n"),
agno, agbno, state);
set_bmap(agno, agbno, XR_E_MULT);
pthread_mutex_unlock(&ag_locks[agno].lock);
return(0);
default:
do_warn(
_("inode block %d/%d bad state, (state %d)\n"),
agno, agbno, state);
set_bmap(agno, agbno, XR_E_INO);
break;
}
pthread_mutex_unlock(&ag_locks[agno].lock);
start_agino = XFS_OFFBNO_TO_AGINO(mp, agbno, 0);
*start_ino = XFS_AGINO_TO_INO(mp, agno, start_agino);
/*
* put new inode record(s) into inode tree
*/
for (j = 0; j < chunks_pblock; j++) {
if ((irec_p = find_inode_rec(mp, agno, start_agino))
== NULL) {
irec_p = set_inode_free_alloc(mp, agno,
start_agino);
for (i = 1; i < XFS_INODES_PER_CHUNK; i++)
set_inode_free(irec_p, i);
}
if (start_agino <= agino && agino <
start_agino + XFS_INODES_PER_CHUNK)
set_inode_used(irec_p, agino - start_agino);
start_agino += XFS_INODES_PER_CHUNK;
ino_cnt += XFS_INODES_PER_CHUNK;
}
return(ino_cnt);
} else if (fs_aligned_inodes) {
/*
* next easy case -- aligned inode filesystem.
* just check out the chunk
*/
start_agbno = rounddown(XFS_INO_TO_AGBNO(mp, ino),
fs_ino_alignment);
end_agbno = start_agbno + mp->m_ialloc_blks;
/*
* if this fs has aligned inodes but the end of the
* chunk is beyond the end of the ag, this is a bad
* chunk
*/
if (end_agbno > max_agbno)
return(0);
/*
* check out all blocks in chunk
*/
ino_cnt = 0;
for (cur_agbno = start_agbno; cur_agbno < end_agbno;
cur_agbno++) {
ino_cnt += check_aginode_block(mp, agno, cur_agbno);
}
/*
* if we lose either 2 blocks worth of inodes or >25% of
* the chunk, just forget it.
*/
if (ino_cnt < XFS_INODES_PER_CHUNK - 2 * mp->m_sb.sb_inopblock
|| ino_cnt < XFS_INODES_PER_CHUNK - 16)
return(0);
/*
* ok, put the record into the tree, if no conflict.
*/
if (find_uncertain_inode_rec(agno,
XFS_OFFBNO_TO_AGINO(mp, start_agbno, 0)))
return(0);
start_agino = XFS_OFFBNO_TO_AGINO(mp, start_agbno, 0);
*start_ino = XFS_AGINO_TO_INO(mp, agno, start_agino);
irec_p = set_inode_free_alloc(mp, agno,
XFS_OFFBNO_TO_AGINO(mp, start_agbno, 0));
for (i = 1; i < XFS_INODES_PER_CHUNK; i++)
set_inode_free(irec_p, i);
ASSERT(start_agino <= agino &&
start_agino + XFS_INODES_PER_CHUNK > agino);
set_inode_used(irec_p, agino - start_agino);
return(XFS_INODES_PER_CHUNK);
}
/*
* hard case -- pre-6.3 filesystem.
* set default start/end agbnos and ensure agbnos are legal.
* we're setting a range [start_agbno, end_agbno) such that
* a discovered inode chunk completely within that range
* would include the inode passed into us.
*/
if (mp->m_ialloc_blks > 1) {
if (agino > mp->m_ialloc_inos)
start_agbno = agbno - mp->m_ialloc_blks + 1;
else
start_agbno = 1;
}
end_agbno = agbno + mp->m_ialloc_blks;
if (end_agbno > max_agbno)
end_agbno = max_agbno;
/*
* search tree for known inodes within +/- 1 inode chunk range
*/
irec_before_p = irec_after_p = NULL;
find_inode_rec_range(mp, agno, XFS_OFFBNO_TO_AGINO(mp, start_agbno, 0),
XFS_OFFBNO_TO_AGINO(mp, end_agbno, mp->m_sb.sb_inopblock - 1),
&irec_before_p, &irec_after_p);
/*
* if we have known inode chunks in our search range, establish
* their start and end-points to tighten our search range. range
* is [start, end) -- e.g. max/end agbno is one beyond the
* last block to be examined. the avl routines work this way.
*/
if (irec_before_p) {
/*
* only one inode record in the range, move one boundary in
*/
if (irec_before_p == irec_after_p) {
if (irec_before_p->ino_startnum < agino)
start_agbno = XFS_AGINO_TO_AGBNO(mp,
irec_before_p->ino_startnum +
XFS_INODES_PER_CHUNK);
else
end_agbno = XFS_AGINO_TO_AGBNO(mp,
irec_before_p->ino_startnum);
}
/*
* find the start of the gap in the search range (which
* should contain our unknown inode). if the only irec
* within +/- 1 chunks starts after the inode we're
* looking for, skip this stuff since the end_agbno
* of the range has already been trimmed in to not
* include that irec.
*/
if (irec_before_p->ino_startnum < agino) {
irec_p = irec_before_p;
irec_next_p = next_ino_rec(irec_p);
while(irec_next_p != NULL &&
irec_p->ino_startnum + XFS_INODES_PER_CHUNK ==
irec_next_p->ino_startnum) {
irec_p = irec_next_p;
irec_next_p = next_ino_rec(irec_next_p);
}
start_agbno = XFS_AGINO_TO_AGBNO(mp,
irec_p->ino_startnum) +
mp->m_ialloc_blks;
/*
* we know that the inode we're trying to verify isn't
* in an inode chunk so the next ino_rec marks the end
* of the gap -- is it within the search range?
*/
if (irec_next_p != NULL &&
agino + mp->m_ialloc_inos >=
irec_next_p->ino_startnum)
end_agbno = XFS_AGINO_TO_AGBNO(mp,
irec_next_p->ino_startnum);
}
ASSERT(start_agbno < end_agbno);
}
/*
* if the gap is too small to contain a chunk, we lose.
* this means that inode chunks known to be good surround
* the inode in question and that the space between them
* is too small for a legal inode chunk
*/
if (end_agbno - start_agbno < mp->m_ialloc_blks)
return(0);
/*
* now grunge around the disk, start at the inode block and
* go in each direction until you hit a non-inode block or
* run into a range boundary. A non-inode block is block
* with *no* good inodes in it. Unfortunately, we can't
* co-opt bad blocks into inode chunks (which might take
* care of disk blocks that turn into zeroes) because the
* filesystem could very well allocate two inode chunks
* with a one block file in between and we'd zap the file.
* We're better off just losing the rest of the
* inode chunk instead.
*/
for (cur_agbno = agbno; cur_agbno >= start_agbno; cur_agbno--) {
/*
* if the block has no inodes, it's a bad block so
* break out now without decrementing cur_agbno so
* chunk start blockno will be set to the last good block
*/
if (!(irec_cnt = check_aginode_block(mp, agno, cur_agbno)))
break;
ino_cnt += irec_cnt;
}
chunk_start_agbno = cur_agbno + 1;
for (cur_agbno = agbno + 1; cur_agbno < end_agbno; cur_agbno++) {
/*
* if the block has no inodes, it's a bad block so
* break out now without incrementing cur_agbno so
* chunk start blockno will be set to the block
* immediately after the last good block.
*/
if (!(irec_cnt = check_aginode_block(mp, agno, cur_agbno)))
break;
ino_cnt += irec_cnt;
}
chunk_stop_agbno = cur_agbno;
num_blks = chunk_stop_agbno - chunk_start_agbno;
if (num_blks < mp->m_ialloc_blks || ino_cnt == 0)
return 0;
/*
* XXX - later - if the entire range is selected and they're all
* good inodes, keep searching in either direction.
* until you the range of inodes end, then split into chunks
* for now, just take one chunk's worth starting at the lowest
* possible point and hopefully we'll pick the rest up later.
*
* XXX - if we were going to fix up an inode chunk for
* any good inodes in the chunk, this is where we would
* do it. For now, keep it simple and lose the rest of
* the chunk
*/
if (num_blks % mp->m_ialloc_blks != 0) {
num_blks = rounddown(num_blks, mp->m_ialloc_blks);
chunk_stop_agbno = chunk_start_agbno + num_blks;
}
/*
* ok, we've got a candidate inode chunk. now we have to
* verify that we aren't trying to use blocks that are already
* in use. If so, mark them as multiply claimed since odds
* are very low that we found this chunk by stumbling across
* user data -- we're probably here as a result of a directory
* entry or an iunlinked pointer
*/
pthread_mutex_lock(&ag_locks[agno].lock);
for (cur_agbno = chunk_start_agbno;
cur_agbno < chunk_stop_agbno;
cur_agbno += blen) {
state = get_bmap_ext(agno, cur_agbno, chunk_stop_agbno, &blen);
switch (state) {
case XR_E_MULT:
case XR_E_INUSE:
case XR_E_INUSE_FS:
case XR_E_FS_MAP:
do_warn(
_("inode block %d/%d multiply claimed, (state %d)\n"),
agno, cur_agbno, state);
set_bmap_ext(agno, cur_agbno, blen, XR_E_MULT);
pthread_mutex_unlock(&ag_locks[agno].lock);
return 0;
case XR_E_INO:
do_error(
_("uncertain inode block overlap, agbno = %d, ino = %" PRIu64 "\n"),
agbno, ino);
break;
default:
break;
}
}
pthread_mutex_unlock(&ag_locks[agno].lock);
/*
* ok, chunk is good. put the record into the tree if required,
* and fill in the bitmap. All inodes will be marked as "free"
* except for the one that led us to discover the chunk. That's
* ok because we'll override the free setting later if the
* contents of the inode indicate it's in use.
*/
start_agino = XFS_OFFBNO_TO_AGINO(mp, chunk_start_agbno, 0);
*start_ino = XFS_AGINO_TO_INO(mp, agno, start_agino);
ASSERT(find_inode_rec(mp, agno, start_agino) == NULL);
irec_p = set_inode_free_alloc(mp, agno, start_agino);
for (i = 1; i < XFS_INODES_PER_CHUNK; i++)
set_inode_free(irec_p, i);
ASSERT(start_agino <= agino &&
start_agino + XFS_INODES_PER_CHUNK > agino);
set_inode_used(irec_p, agino - start_agino);
pthread_mutex_lock(&ag_locks[agno].lock);
for (cur_agbno = chunk_start_agbno;
cur_agbno < chunk_stop_agbno;
cur_agbno += blen) {
state = get_bmap_ext(agno, cur_agbno, chunk_stop_agbno, &blen);
switch (state) {
case XR_E_INO:
do_error(
_("uncertain inode block %" PRIu64 " already known\n"),
XFS_AGB_TO_FSB(mp, agno, cur_agbno));
break;
case XR_E_UNKNOWN:
case XR_E_FREE1:
case XR_E_FREE:
set_bmap_ext(agno, cur_agbno, blen, XR_E_INO);
break;
case XR_E_MULT:
case XR_E_INUSE:
case XR_E_INUSE_FS:
case XR_E_FS_MAP:
do_error(
_("inode block %d/%d multiply claimed, (state %d)\n"),
agno, cur_agbno, state);
break;
default:
do_warn(
_("inode block %d/%d bad state, (state %d)\n"),
agno, cur_agbno, state);
set_bmap_ext(agno, cur_agbno, blen, XR_E_INO);
break;
}
}
pthread_mutex_unlock(&ag_locks[agno].lock);
return(ino_cnt);
}
/*
* same as above only for ag inode chunks
*/
static int
verify_aginode_chunk(xfs_mount_t *mp,
xfs_agnumber_t agno,
xfs_agino_t agino,
xfs_agino_t *agino_start)
{
xfs_ino_t ino;
int res;
res = verify_inode_chunk(mp, XFS_AGINO_TO_INO(mp, agno, agino), &ino);
if (res)
*agino_start = XFS_INO_TO_AGINO(mp, ino);
else
*agino_start = NULLAGINO;
return(res);
}
/*
* this does the same as the two above only it returns a pointer
* to the inode record in the good inode tree
*/
static ino_tree_node_t *
verify_aginode_chunk_irec(xfs_mount_t *mp,
xfs_agnumber_t agno,
xfs_agino_t agino)
{
xfs_agino_t start_agino;
ino_tree_node_t *irec = NULL;
if (verify_aginode_chunk(mp, agno, agino, &start_agino))
irec = find_inode_rec(mp, agno, start_agino);
return(irec);
}
/*
* Set the state of an inode block during inode chunk processing. The block is
* expected to be in the free or inode state. If free, it transitions to the
* inode state. Warn if the block is in neither expected state as this indicates
* multiply claimed blocks.
*/
static void
process_inode_agbno_state(
struct xfs_mount *mp,
xfs_agnumber_t agno,
xfs_agblock_t agbno)
{
int state;
pthread_mutex_lock(&ag_locks[agno].lock);
state = get_bmap(agno, agbno);
switch (state) {
case XR_E_INO: /* already marked */
break;
case XR_E_UNKNOWN:
case XR_E_FREE:
case XR_E_FREE1:
set_bmap(agno, agbno, XR_E_INO);
break;
case XR_E_BAD_STATE:
do_error(_("bad state in block map %d\n"), state);
break;
default:
set_bmap(agno, agbno, XR_E_MULT);
do_warn(
_("inode block %" PRIu64 " multiply claimed, state was %d\n"),
XFS_AGB_TO_FSB(mp, agno, agbno), state);
break;
}
pthread_mutex_unlock(&ag_locks[agno].lock);
}
/*
* processes an inode allocation chunk/block, returns 1 on I/O errors,
* 0 otherwise
*
* *bogus is set to 1 if the entire set of inodes is bad.
*/
static int
process_inode_chunk(
xfs_mount_t *mp,
xfs_agnumber_t agno,
int num_inos,
ino_tree_node_t *first_irec,
int ino_discovery,
int check_dups,
int extra_attr_check,
int *bogus)
{
xfs_ino_t parent;
ino_tree_node_t *ino_rec;
xfs_buf_t **bplist;
xfs_dinode_t *dino;
int icnt;
int status;
int is_used;
int ino_dirty;
int irec_offset;
int ibuf_offset;
xfs_agino_t agino;
xfs_agblock_t agbno;
xfs_ino_t ino;
int dirty = 0;
int isa_dir = 0;
int blks_per_cluster;
int cluster_count;
int bp_index;
int cluster_offset;
ASSERT(first_irec != NULL);
ASSERT(XFS_AGINO_TO_OFFSET(mp, first_irec->ino_startnum) == 0);
*bogus = 0;
ASSERT(mp->m_ialloc_blks > 0);
blks_per_cluster = mp->m_inode_cluster_size >> mp->m_sb.sb_blocklog;
if (blks_per_cluster == 0)
blks_per_cluster = 1;
cluster_count = XFS_INODES_PER_CHUNK / inodes_per_cluster;
if (cluster_count == 0)
cluster_count = 1;
/*
* get all blocks required to read in this chunk (may wind up
* having to process more chunks in a multi-chunk per block fs)
*/
agbno = XFS_AGINO_TO_AGBNO(mp, first_irec->ino_startnum);
/*
* set up first irec
*/
ino_rec = first_irec;
irec_offset = 0;
bplist = malloc(cluster_count * sizeof(xfs_buf_t *));
if (bplist == NULL)
do_error(_("failed to allocate %zd bytes of memory\n"),
cluster_count * sizeof(xfs_buf_t *));
for (bp_index = 0; bp_index < cluster_count; bp_index++) {
/*
* Skip the cluster buffer if the first inode is sparse. The
* remaining inodes in the cluster share the same state as
* sparse inodes occur at cluster granularity.
*/
if (is_inode_sparse(ino_rec, irec_offset)) {
pftrace("skip sparse inode, startnum 0x%x idx %d",
ino_rec->ino_startnum, irec_offset);
bplist[bp_index] = NULL;
goto next_readbuf;
}
pftrace("about to read off %llu in AG %d",
XFS_AGB_TO_DADDR(mp, agno, agbno), agno);
bplist[bp_index] = libxfs_readbuf(mp->m_dev,
XFS_AGB_TO_DADDR(mp, agno, agbno),
XFS_FSB_TO_BB(mp, blks_per_cluster), 0,
&xfs_inode_buf_ops);
if (!bplist[bp_index]) {
do_warn(_("cannot read inode %" PRIu64 ", disk block %" PRId64 ", cnt %d\n"),
XFS_AGINO_TO_INO(mp, agno, first_irec->ino_startnum),
XFS_AGB_TO_DADDR(mp, agno, agbno),
XFS_FSB_TO_BB(mp, blks_per_cluster));
while (bp_index > 0) {
bp_index--;
libxfs_putbuf(bplist[bp_index]);
}
free(bplist);
return(1);
}
pftrace("readbuf %p (%llu, %d) in AG %d", bplist[bp_index],
(long long)XFS_BUF_ADDR(bplist[bp_index]),
bplist[bp_index]->b_bcount, agno);
bplist[bp_index]->b_ops = &xfs_inode_buf_ops;
next_readbuf:
irec_offset += mp->m_sb.sb_inopblock * blks_per_cluster;
agbno += blks_per_cluster;
}
agbno = XFS_AGINO_TO_AGBNO(mp, first_irec->ino_startnum);
/*
* initialize counters
*/
irec_offset = 0;
ibuf_offset = 0;
cluster_offset = 0;
icnt = 0;
status = 0;
bp_index = 0;
/*
* verify inode chunk if necessary
*/
if (ino_discovery) {
for (;;) {
agino = irec_offset + ino_rec->ino_startnum;
/* no buffers for sparse clusters */
if (bplist[bp_index]) {
/* make inode pointer */
dino = xfs_make_iptr(mp, bplist[bp_index],
cluster_offset);
/*
* we always think that the root and realtime
* inodes are verified even though we may have
* to reset them later to keep from losing the
* chunk that they're in
*/
if (verify_dinode(mp, dino, agno, agino) == 0 ||
(agno == 0 &&
(mp->m_sb.sb_rootino == agino ||
mp->m_sb.sb_rsumino == agino ||
mp->m_sb.sb_rbmino == agino)))
status++;
}
irec_offset++;
icnt++;
cluster_offset++;
if (icnt == mp->m_ialloc_inos &&
irec_offset == XFS_INODES_PER_CHUNK) {
/*
* done! - finished up irec and block
* simultaneously
*/
break;
} else if (irec_offset == XFS_INODES_PER_CHUNK) {
/*
* get new irec (multiple chunks per block fs)
*/
ino_rec = next_ino_rec(ino_rec);
ASSERT(ino_rec->ino_startnum == agino + 1);
irec_offset = 0;
}
if (cluster_offset == inodes_per_cluster) {
bp_index++;
cluster_offset = 0;
}
}
/*
* if chunk/block is bad, blow it off. the inode records
* will be deleted by the caller if appropriate.
*/
if (!status) {
*bogus = 1;
for (bp_index = 0; bp_index < cluster_count; bp_index++)
if (bplist[bp_index])
libxfs_putbuf(bplist[bp_index]);
free(bplist);
return(0);
}
/*
* reset irec and counters
*/
ino_rec = first_irec;
irec_offset = 0;
cluster_offset = 0;
bp_index = 0;
icnt = 0;
status = 0;
}
/*
* mark block as an inode block in the incore bitmap
*/
if (!is_inode_sparse(ino_rec, irec_offset))
process_inode_agbno_state(mp, agno, agbno);
for (;;) {
agino = irec_offset + ino_rec->ino_startnum;
ino = XFS_AGINO_TO_INO(mp, agno, agino);
if (is_inode_sparse(ino_rec, irec_offset))
goto process_next;
/* make inode pointer */
dino = xfs_make_iptr(mp, bplist[bp_index], cluster_offset);
is_used = 3;
ino_dirty = 0;
parent = 0;
status = process_dinode(mp, dino, agno, agino,
is_inode_free(ino_rec, irec_offset),
&ino_dirty, &is_used,ino_discovery, check_dups,
extra_attr_check, &isa_dir, &parent);
ASSERT(is_used != 3);
if (ino_dirty) {
dirty = 1;
libxfs_dinode_calc_crc(mp, dino);
}
/*
* XXX - if we want to try and keep
* track of whether we need to bang on
* the inode maps (instead of just
* blindly reconstructing them like
* we do now, this is where to start.
*/
if (is_used) {
if (is_inode_free(ino_rec, irec_offset)) {
if (verbose || no_modify) {
do_warn(
_("imap claims in-use inode %" PRIu64 " is free, "),
ino);
}
if (verbose || !no_modify)
do_warn(_("correcting imap\n"));
else
do_warn(_("would correct imap\n"));
}
set_inode_used(ino_rec, irec_offset);
/*
* store the on-disk file type for comparing in
* phase 6.
*/
set_inode_ftype(ino_rec, irec_offset,
libxfs_mode_to_ftype(be16_to_cpu(dino->di_mode)));
/*
* store on-disk nlink count for comparing in phase 7
*/
set_inode_disk_nlinks(ino_rec, irec_offset,
dino->di_version > 1
? be32_to_cpu(dino->di_nlink)
: be16_to_cpu(dino->di_onlink));
} else {
set_inode_free(ino_rec, irec_offset);
}
/*
* if we lose the root inode, or it turns into
* a non-directory, that allows us to double-check
* later whether or not we need to reinitialize it.
*/
if (isa_dir) {
set_inode_isadir(ino_rec, irec_offset);
/*
* we always set the parent but
* we may as well wait until
* phase 4 (no inode discovery)
* because the parent info will
* be solid then.
*/
if (!ino_discovery) {
ASSERT(parent != 0);
set_inode_parent(ino_rec, irec_offset, parent);
ASSERT(parent ==
get_inode_parent(ino_rec, irec_offset));
}
} else {
clear_inode_isadir(ino_rec, irec_offset);
}
if (status) {
if (mp->m_sb.sb_rootino == ino) {
need_root_inode = 1;
if (!no_modify) {
do_warn(
_("cleared root inode %" PRIu64 "\n"),
ino);
} else {
do_warn(
_("would clear root inode %" PRIu64 "\n"),
ino);
}
} else if (mp->m_sb.sb_rbmino == ino) {
need_rbmino = 1;
if (!no_modify) {
do_warn(
_("cleared realtime bitmap inode %" PRIu64 "\n"),
ino);
} else {
do_warn(
_("would clear realtime bitmap inode %" PRIu64 "\n"),
ino);
}
} else if (mp->m_sb.sb_rsumino == ino) {
need_rsumino = 1;
if (!no_modify) {
do_warn(
_("cleared realtime summary inode %" PRIu64 "\n"),
ino);
} else {
do_warn(
_("would clear realtime summary inode %" PRIu64 "\n"),
ino);
}
} else if (!no_modify) {
do_warn(_("cleared inode %" PRIu64 "\n"),
ino);
} else {
do_warn(_("would have cleared inode %" PRIu64 "\n"),
ino);
}
clear_inode_was_rl(ino_rec, irec_offset);
}
process_next:
irec_offset++;
ibuf_offset++;
icnt++;
cluster_offset++;
if (icnt == mp->m_ialloc_inos &&
irec_offset == XFS_INODES_PER_CHUNK) {
/*
* done! - finished up irec and block simultaneously
*/
for (bp_index = 0; bp_index < cluster_count; bp_index++) {
if (!bplist[bp_index])
continue;
pftrace("put/writebuf %p (%llu) in AG %d",
bplist[bp_index], (long long)
XFS_BUF_ADDR(bplist[bp_index]), agno);
if (dirty && !no_modify)
libxfs_writebuf(bplist[bp_index], 0);
else
libxfs_putbuf(bplist[bp_index]);
}
free(bplist);
break;
} else if (ibuf_offset == mp->m_sb.sb_inopblock) {
/*
* mark block as an inode block in the incore bitmap
* and reset inode buffer offset counter
*/
ibuf_offset = 0;
agbno++;
if (!is_inode_sparse(ino_rec, irec_offset))
process_inode_agbno_state(mp, agno, agbno);
} else if (irec_offset == XFS_INODES_PER_CHUNK) {
/*
* get new irec (multiple chunks per block fs)
*/
ino_rec = next_ino_rec(ino_rec);
ASSERT(ino_rec->ino_startnum == agino + 1);
irec_offset = 0;
}
if (cluster_offset == inodes_per_cluster) {
bp_index++;
cluster_offset = 0;
}
}
return(0);
}
/*
* check all inodes mentioned in the ag's incore inode maps.
* the map may be incomplete. If so, we'll catch the missing
* inodes (hopefully) when we traverse the directory tree.
* check_dirs is set to 1 if directory inodes should be
* processed for internal consistency, parent setting and
* discovery of unknown inodes. this only happens
* in phase 3. check_dups is set to 1 if we're looking for
* inodes that reference duplicate blocks so we can trash
* the inode right then and there. this is set only in
* phase 4 after we've run through and set the bitmap once.
*/
void
process_aginodes(
xfs_mount_t *mp,
prefetch_args_t *pf_args,
xfs_agnumber_t agno,
int ino_discovery,
int check_dups,
int extra_attr_check)
{
int num_inos, bogus;
ino_tree_node_t *ino_rec, *first_ino_rec, *prev_ino_rec;
#ifdef XR_PF_TRACE
int count;
#endif
first_ino_rec = ino_rec = findfirst_inode_rec(agno);
while (ino_rec != NULL) {
/*
* paranoia - step through inode records until we step
* through a full allocation of inodes. this could
* be an issue in big-block filesystems where a block
* can hold more than one inode chunk. make sure to
* grab the record corresponding to the beginning of
* the next block before we call the processing routines.
*/
num_inos = XFS_INODES_PER_CHUNK;
while (num_inos < mp->m_ialloc_inos && ino_rec != NULL) {
/*
* inodes chunks will always be aligned and sized
* correctly
*/
if ((ino_rec = next_ino_rec(ino_rec)) != NULL)
num_inos += XFS_INODES_PER_CHUNK;
}
ASSERT(num_inos == mp->m_ialloc_inos);
if (pf_args) {
sem_post(&pf_args->ra_count);
#ifdef XR_PF_TRACE
sem_getvalue(&pf_args->ra_count, &count);
pftrace("processing inode chunk %p in AG %d (sem count = %d)",
first_ino_rec, agno, count);
#endif
}
if (process_inode_chunk(mp, agno, num_inos, first_ino_rec,
ino_discovery, check_dups, extra_attr_check,
&bogus)) {
/* XXX - i/o error, we've got a problem */
abort();
}
if (!bogus)
first_ino_rec = ino_rec = next_ino_rec(ino_rec);
else {
/*
* inodes pointed to by this record are
* completely bogus, blow the records for
* this chunk out.
* the inode block(s) will get reclaimed
* in phase 4 when the block map is
* reconstructed after inodes claiming
* duplicate blocks are deleted.
*/
num_inos = 0;
ino_rec = first_ino_rec;
while (num_inos < mp->m_ialloc_inos &&
ino_rec != NULL) {
prev_ino_rec = ino_rec;
if ((ino_rec = next_ino_rec(ino_rec)) != NULL)
num_inos += XFS_INODES_PER_CHUNK;
get_inode_rec(mp, agno, prev_ino_rec);
free_inode_rec(agno, prev_ino_rec);
}
first_ino_rec = ino_rec;
}
PROG_RPT_INC(prog_rpt_done[agno], num_inos);
}
}
/*
* verify the uncertain inode list for an ag.
* Good inodes get moved into the good inode tree.
* returns 0 if there are no uncertain inode records to
* be processed, 1 otherwise. This routine destroys the
* the entire uncertain inode tree for the ag as a side-effect.
*/
void
check_uncertain_aginodes(xfs_mount_t *mp, xfs_agnumber_t agno)
{
ino_tree_node_t *irec;
ino_tree_node_t *nrec;
xfs_agino_t start;
xfs_agino_t i;
xfs_agino_t agino;
int got_some;
nrec = NULL;
got_some = 0;
clear_uncertain_ino_cache(agno);
if ((irec = findfirst_uncertain_inode_rec(agno)) == NULL)
return;
/*
* the trick here is to find a contiguous range
* of inodes, make sure that it doesn't overlap
* with a known to exist chunk, and then make
* sure it is a number of entire chunks.
* we check on-disk once we have an idea of what's
* going on just to double-check.
*
* process the uncertain inode record list and look
* on disk to see if the referenced inodes are good
*/
do_warn(_("found inodes not in the inode allocation tree\n"));
do {
/*
* check every confirmed (which in this case means
* inode that we really suspect to be an inode) inode
*/
for (i = 0; i < XFS_INODES_PER_CHUNK; i++) {
if (!is_inode_confirmed(irec, i))
continue;
agino = i + irec->ino_startnum;
if (verify_aginum(mp, agno, agino))
continue;
if (nrec != NULL && nrec->ino_startnum <= agino &&
agino < nrec->ino_startnum +
XFS_INODES_PER_CHUNK)
continue;
if ((nrec = find_inode_rec(mp, agno, agino)) == NULL)
if (!verify_aginum(mp, agno, agino))
if (verify_aginode_chunk(mp, agno,
agino, &start))
got_some = 1;
}
get_uncertain_inode_rec(mp, agno, irec);
free_inode_rec(agno, irec);
irec = findfirst_uncertain_inode_rec(agno);
} while (irec != NULL);
if (got_some)
do_warn(_("found inodes not in the inode allocation tree\n"));
return;
}
/*
* verify and process the uncertain inodes for an ag.
* this is different from check_ in that we can't just
* move the good inodes into the good inode tree and let
* process_aginodes() deal with them because this gets called
* after process_aginodes() has been run on the ag inode tree.
* So we have to process the inodes as well as verify since
* we don't want to rerun process_aginodes() on a tree that has
* mostly been processed.
*
* Note that if this routine does process some inodes, it can
* add uncertain inodes to any ag which would require that
* the routine be called again to process those newly-added
* uncertain inodes.
*
* returns 0 if no inodes were processed and 1 if inodes
* were processed (and it is possible that new uncertain
* inodes were discovered).
*
* as a side-effect, this routine tears down the uncertain
* inode tree for the ag.
*/
int
process_uncertain_aginodes(xfs_mount_t *mp, xfs_agnumber_t agno)
{
ino_tree_node_t *irec;
ino_tree_node_t *nrec;
xfs_agino_t agino;
int i;
int bogus;
int cnt;
int got_some;
#ifdef XR_INODE_TRACE
fprintf(stderr, "in process_uncertain_aginodes, agno = %d\n", agno);
#endif
got_some = 0;
clear_uncertain_ino_cache(agno);
if ((irec = findfirst_uncertain_inode_rec(agno)) == NULL)
return(0);
nrec = NULL;
do {
/*
* check every confirmed inode
*/
for (cnt = i = 0; i < XFS_INODES_PER_CHUNK; i++) {
if (!is_inode_confirmed(irec, i))
continue;
cnt++;
agino = i + irec->ino_startnum;
#ifdef XR_INODE_TRACE
fprintf(stderr, "ag inode = %d (0x%x)\n", agino, agino);
#endif
/*
* skip over inodes already processed (in the
* good tree), bad inode numbers, and inode numbers
* pointing to bogus inodes
*/
if (verify_aginum(mp, agno, agino))
continue;
if (nrec != NULL && nrec->ino_startnum <= agino &&
agino < nrec->ino_startnum +
XFS_INODES_PER_CHUNK)
continue;
if ((nrec = find_inode_rec(mp, agno, agino)) != NULL)
continue;
/*
* verify the chunk. if good, it will be
* added to the good inode tree.
*/
if ((nrec = verify_aginode_chunk_irec(mp,
agno, agino)) == NULL)
continue;
got_some = 1;
/*
* process the inode record we just added
* to the good inode tree. The inode
* processing may add more records to the
* uncertain inode lists.
*/
if (process_inode_chunk(mp, agno, mp->m_ialloc_inos,
nrec, 1, 0, 0, &bogus)) {
/* XXX - i/o error, we've got a problem */
abort();
}
}
ASSERT(cnt != 0);
/*
* now return the uncertain inode record to the free pool
* and pull another one off the list for processing
*/
get_uncertain_inode_rec(mp, agno, irec);
free_inode_rec(agno, irec);
irec = findfirst_uncertain_inode_rec(agno);
} while (irec != NULL);
if (got_some)
do_warn(_("found inodes not in the inode allocation tree\n"));
return(1);
}