Perform some fairly superficial checks of the rtrmap btree. We'll
do more sophisticated checks in xfs_repair, but provide enough of
a spot-check here that we can do simple things.
Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
db/check.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 182 insertions(+), 9 deletions(-)
diff --git a/db/check.c b/db/check.c
index 7392852..7ffd5bb 100644
--- a/db/check.c
+++ b/db/check.c
@@ -45,7 +45,7 @@ typedef enum {
DBM_LOG, DBM_MISSING, DBM_QUOTA, DBM_RTBITMAP,
DBM_RTDATA, DBM_RTFREE, DBM_RTSUM, DBM_SB,
DBM_SYMLINK, DBM_BTFINO, DBM_BTRMAP, DBM_BTREFC,
- DBM_RLDATA, DBM_COWDATA,
+ DBM_RLDATA, DBM_COWDATA, DBM_BTRTRMAP,
DBM_NDBM
} dbm_t;
@@ -176,6 +176,8 @@ static const char *typename[] = {
"btrmap",
"btrefcnt",
"rldata",
+ "cowdata",
+ "btrtrmap",
NULL
};
static int verbose;
@@ -316,6 +318,9 @@ static void process_quota(qtype_t qtype, inodata_t
*id,
blkmap_t *blkmap);
static void process_rtbitmap(blkmap_t *blkmap);
static void process_rtsummary(blkmap_t *blkmap);
+static void process_rtrmap(struct inodata *id,
+ struct xfs_dinode *dip,
+ xfs_rfsblock_t *toti);
static xfs_ino_t process_sf_dir_v2(xfs_dinode_t *dip, int *dot,
int *dotdot, inodata_t *id);
static void quota_add(xfs_dqid_t *p, xfs_dqid_t *g, xfs_dqid_t *u,
@@ -343,6 +348,12 @@ static void scanfunc_bmap(struct
xfs_btree_block *block,
xfs_rfsblock_t *toti, xfs_extnum_t *nex,
blkmap_t **blkmapp, int isroot,
typnm_t btype);
+static void scanfunc_rtrmap(struct xfs_btree_block *block,
+ int level, dbm_t type, xfs_fsblock_t bno,
+ inodata_t *id, xfs_rfsblock_t *totd,
+ xfs_rfsblock_t *toti, xfs_extnum_t *nex,
+ blkmap_t **blkmapp, int isroot,
+ typnm_t btype);
static void scanfunc_bno(struct xfs_btree_block *block, int level,
xfs_agf_t *agf, xfs_agblock_t bno,
int isroot);
@@ -1060,6 +1071,7 @@ blocktrash_f(
(1 << DBM_QUOTA) |
(1 << DBM_RTBITMAP) |
(1 << DBM_RTSUM) |
+ (1 << DBM_BTRTRMAP) |
(1 << DBM_SYMLINK) |
(1 << DBM_BTFINO) |
(1 << DBM_BTRMAP) |
@@ -2706,7 +2718,7 @@ process_inode(
0 /* type 15 unused */
};
static char *fmtnames[] = {
- "dev", "local", "extents", "btree", "uuid"
+ "dev", "local", "extents", "btree", "uuid", "rtrmap"
};
libxfs_inode_from_disk(&xino, dip);
@@ -2761,12 +2773,23 @@ process_inode(
be32_to_cpu(dip->di_next_unlinked), ino);
error++;
}
- /*
- * di_mode is a 16-bit uint so no need to check the < 0 case
- */
+
+ /* Check that mode and data fork format match. */
mode = be16_to_cpu(dip->di_mode);
- if ((((mode & S_IFMT) >> 12) > 15) ||
- (!(okfmts[(mode & S_IFMT) >> 12] & (1 << xino.i_d.di_format)))) {
+ if (ino == mp->m_sb.sb_rrmapino) {
+ if ((mode & S_IFMT) != S_IFREG ||
+ xino.i_d.di_format != XFS_DINODE_FMT_RMAP) {
+ if (v)
+ dbprintf(_("bad format %d for rtrmap inode %lld
type %#o\n"),
+ xino.i_d.di_format, id->ino, mode &
S_IFMT);
+ error++;
+ return;
+ }
+ } else if ((((mode & S_IFMT) >> 12) > 15) ||
+ (!(okfmts[(mode & S_IFMT) >> 12] & (1 <<
xino.i_d.di_format)))) {
+ /*
+ * di_mode is a 16-bit uint so no need to check the < 0 case
+ */
if (v)
dbprintf(_("bad format %d for inode %lld type %#o\n"),
xino.i_d.di_format, id->ino, mode & S_IFMT);
@@ -2823,8 +2846,10 @@ process_inode(
type = DBM_RTSUM;
blkmap = blkmap_alloc(xino.i_d.di_nextents);
addlink_inode(id);
- }
- else if (id->ino == mp->m_sb.sb_uquotino ||
+ } else if (id->ino == mp->m_sb.sb_rrmapino) {
+ type = DBM_BTRTRMAP;
+ addlink_inode(id);
+ } else if (id->ino == mp->m_sb.sb_uquotino ||
id->ino == mp->m_sb.sb_gquotino ||
id->ino == mp->m_sb.sb_pquotino) {
type = DBM_QUOTA;
@@ -2861,6 +2886,9 @@ process_inode(
process_btinode(id, dip, type, &totdblocks, &totiblocks,
&nextents, &blkmap, XFS_DATA_FORK);
break;
+ case XFS_DINODE_FMT_RMAP:
+ process_rtrmap(id, dip, &totiblocks);
+ break;
}
if (XFS_DFORK_Q(dip)) {
sbversion |= XFS_SB_VERSION_ATTRBIT;
@@ -3604,6 +3632,71 @@ process_rtsummary(
}
}
+static void
+process_rtrmap(
+ struct inodata *id,
+ struct xfs_dinode *dip,
+ xfs_rfsblock_t *toti)
+{
+ xfs_extnum_t nex = 0;
+ xfs_rfsblock_t totd = 0;
+ struct xfs_rtrmap_root *dib;
+ int whichfork = XFS_DATA_FORK;
+ int i;
+ xfs_rtrmap_ptr_t *pp;
+
+ dib = (struct xfs_rtrmap_root *)XFS_DFORK_PTR(dip, whichfork);
+ if (be16_to_cpu(dib->bb_level) >= mp->m_rtrmap_maxlevels) {
+ if (!sflag || id->ilist)
+ dbprintf(_("level for ino %lld rtrmap root too "
+ "large (%u)\n"),
+ id->ino,
+ be16_to_cpu(dib->bb_level));
+ error++;
+ return;
+ }
+ if (be16_to_cpu(dib->bb_numrecs) > libxfs_rtrmapbt_maxrecs(mp,
+ XFS_DFORK_SIZE(dip, mp, whichfork),
+ be16_to_cpu(dib->bb_level) == 0)) {
+ if (!sflag || id->ilist)
+ dbprintf(_("numrecs for ino %lld rtrmap root too "
+ "large (%u)\n"),
+ id->ino,
+ be16_to_cpu(dib->bb_numrecs));
+ error++;
+ return;
+ }
+ if (be16_to_cpu(dib->bb_level) == 0) {
+ struct xfs_rtrmap_rec *rp;
+ xfs_fsblock_t lastblock;
+
+ rp = XFS_RTRMAP_ROOT_REC_ADDR(dib, 1);
+ lastblock = 0;
+ for (i = 0; i < be16_to_cpu(dib->bb_numrecs); i++) {
+ if (be64_to_cpu(rp[i].rm_startblock) < lastblock) {
+ dbprintf(_(
+ "out-of-order rtrmap btree record %d (%u %u) root\n"),
+ i, be64_to_cpu(rp[i].rm_startblock),
+ be32_to_cpu(rp[i].rm_startblock));
+ } else {
+ lastblock = be64_to_cpu(rp[i].rm_startblock) +
+ be64_to_cpu(rp[i].rm_blockcount);
+ }
+ }
+ return;
+ } else {
+ pp = XFS_RTRMAP_ROOT_PTR_ADDR(dib, 1,
+ libxfs_rtrmapbt_maxrecs(mp,
+ XFS_DFORK_SIZE(dip, mp, whichfork), 0));
+ for (i = 0; i < be16_to_cpu(dib->bb_numrecs); i++)
+ scan_lbtree(get_unaligned_be64(&pp[i]),
+ be16_to_cpu(dib->bb_level),
+ scanfunc_rtrmap, DBM_BTRTRMAP,
+ id, &totd, toti,
+ &nex, NULL, 1, TYP_RTRMAPBT);
+ }
+}
+
static xfs_ino_t
process_sf_dir_v2(
xfs_dinode_t *dip,
@@ -4691,6 +4784,86 @@ scanfunc_rmap(
}
static void
+scanfunc_rtrmap(
+ struct xfs_btree_block *block,
+ int level,
+ dbm_t type,
+ xfs_fsblock_t bno,
+ inodata_t *id,
+ xfs_rfsblock_t *totd,
+ xfs_rfsblock_t *toti,
+ xfs_extnum_t *nex,
+ blkmap_t **blkmapp,
+ int isroot,
+ typnm_t btype)
+{
+ xfs_agblock_t agbno;
+ xfs_agnumber_t agno;
+ int i;
+ xfs_rtrmap_ptr_t *pp;
+ struct xfs_rtrmap_rec *rp;
+ xfs_fsblock_t lastblock;
+
+ agno = XFS_FSB_TO_AGNO(mp, bno);
+ agbno = XFS_FSB_TO_AGBNO(mp, bno);
+ if (be32_to_cpu(block->bb_magic) != XFS_RTRMAP_CRC_MAGIC) {
+ dbprintf(_("bad magic # %#x in rtrmapbt block %u/%u\n"),
+ be32_to_cpu(block->bb_magic), agno, bno);
+ serious_error++;
+ return;
+ }
+ if (be16_to_cpu(block->bb_level) != level) {
+ if (!sflag)
+ dbprintf(_("expected level %d got %d in rtrmapbt block "
+ "%u/%u\n"),
+ level, be16_to_cpu(block->bb_level), agno, bno);
+ error++;
+ }
+ set_dbmap(agno, bno, 1, type, agno, bno);
+ set_inomap(agno, agbno, 1, id);
+ (*toti)++;
+ if (level == 0) {
+ if (be16_to_cpu(block->bb_numrecs) > mp->m_rtrmap_mxr[0] ||
+ (isroot == 0 && be16_to_cpu(block->bb_numrecs) <
mp->m_rtrmap_mnr[0])) {
+ dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
+ "rtrmapbt block %u/%u\n"),
+ be16_to_cpu(block->bb_numrecs),
mp->m_rtrmap_mnr[0],
+ mp->m_rtrmap_mxr[0], agno, bno);
+ serious_error++;
+ return;
+ }
+ rp = XFS_RTRMAP_REC_ADDR(block, 1);
+ lastblock = 0;
+ for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
+ if (be64_to_cpu(rp[i].rm_startblock) < lastblock) {
+ dbprintf(_(
+ "out-of-order rtrmap btree record %d (%u %u) block %u/%u l
%llu\n"),
+ i, be64_to_cpu(rp[i].rm_startblock),
+ be64_to_cpu(rp[i].rm_blockcount),
+ agno, bno, lastblock);
+ } else {
+ lastblock = be64_to_cpu(rp[i].rm_startblock) +
+ be64_to_cpu(rp[i].rm_blockcount);
+ }
+ }
+ return;
+ }
+ if (be16_to_cpu(block->bb_numrecs) > mp->m_rtrmap_mxr[1] ||
+ (isroot == 0 && be16_to_cpu(block->bb_numrecs) <
mp->m_rtrmap_mnr[1])) {
+ dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in rtrmapbt "
+ "block %u/%u\n"),
+ be16_to_cpu(block->bb_numrecs), mp->m_rtrmap_mnr[1],
+ mp->m_rtrmap_mxr[1], agno, bno);
+ serious_error++;
+ return;
+ }
+ pp = XFS_RTRMAP_PTR_ADDR(block, 1, mp->m_rtrmap_mxr[1]);
+ for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
+ scan_lbtree(be64_to_cpu(pp[i]), level, scanfunc_rtrmap, type,
id,
+ totd, toti, nex, blkmapp, 0, btype);
+}
+
+static void
scanfunc_refcnt(
struct xfs_btree_block *block,
int level,
|