DBM_COWDATA,
DBM_RTSB,
DBM_BTRTRMAP,
+ DBM_BTRTREFC,
+ DBM_RLRTDATA,
DBM_NDBM
} dbm_t;
"cowdata",
"rtsb",
"btrtrmap",
+ "btrtrefc",
+ "rlrtdata",
NULL
};
static int check_range(xfs_agnumber_t agno, xfs_agblock_t agbno,
xfs_extlen_t len);
static void check_rdbmap(xfs_rfsblock_t bno, xfs_extlen_t len,
- dbm_t type);
+ dbm_t type, bool is_reflink);
static int check_rinomap(xfs_rfsblock_t bno, xfs_extlen_t len,
xfs_ino_t c_ino);
static void check_rootdir(void);
static void process_rtrmap(struct inodata *id,
struct xfs_dinode *dip,
xfs_rfsblock_t *toti);
+static void process_rtrefc(struct inodata *id,
+ struct xfs_dinode *dip,
+ xfs_rfsblock_t *toti);
static void quota_add(xfs_dqid_t *p, xfs_dqid_t *g, xfs_dqid_t *u,
int dq, xfs_qcnt_t bc, xfs_qcnt_t ic,
xfs_qcnt_t rc);
xfs_rfsblock_t *toti, xfs_extnum_t *nex,
blkmap_t **blkmapp, int isroot,
typnm_t btype);
+static void scanfunc_rtrefc(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);
(1ULL << DBM_BTFINO) |
(1ULL << DBM_BTRMAP) |
(1ULL << DBM_BTREFC) |
+ (1ULL << DBM_BTRTREFC) |
(1ULL << DBM_SB);
while ((c = getopt(argc, argv, "0123n:o:s:t:x:y:z")) != EOF) {
switch (c) {
check_rdbmap(
xfs_rfsblock_t bno,
xfs_extlen_t len,
- dbm_t type)
+ dbm_t type,
+ bool ignore_reflink)
{
xfs_extlen_t i;
char *p;
error++;
break;
}
+ if (ignore_reflink && (*p == DBM_UNKNOWN || *p == DBM_RTDATA ||
+ *p == DBM_RLRTDATA))
+ continue;
if ((dbm_t)*p != type) {
if (!sflag || CHECK_BLIST(bno + i))
dbprintf(_("rtblock %llu expected type %s got "
bno, bno + len - 1, c_ino);
return 0;
}
+ if (xfs_has_rtreflink(mp))
+ return 0;
for (i = 0, rval = 1, idp = &inomap[mp->m_sb.sb_agcount][bno];
i < len;
i++, idp++) {
}
}
+/*
+ * We don't check the accuracy of reference counts -- all we do is ensure
+ * that a data block never crosses with non-data blocks. repair can check
+ * those kinds of things.
+ *
+ * So with that in mind, if we're setting a block to be data or rldata,
+ * don't complain so long as the block is currently unknown, data, or rldata.
+ * Don't let blocks downgrade from rldata -> data.
+ */
+static bool
+is_rtreflink(
+ dbm_t type2)
+{
+ if (!xfs_has_rtreflink(mp))
+ return false;
+ if (type2 == DBM_RTDATA || type2 == DBM_RLRTDATA)
+ return true;
+ return false;
+}
+
static void
check_set_rdbmap(
xfs_rfsblock_t bno,
if (!check_rrange(bno, len))
return;
- check_rdbmap(bno, len, type1);
+ check_rdbmap(bno, len, type1, is_rtreflink(type2));
mayprint = verbose | blist_size;
for (i = 0, p = &dbmap[mp->m_sb.sb_agcount][bno]; i < len; i++, p++) {
if (!rdbmap_boundscheck(bno + i)) {
0 /* type 15 unused */
};
static char *fmtnames[] = {
- "dev", "local", "extents", "btree", "uuid", "rmap"
+ "dev", "local", "extents", "btree", "uuid", "rmap", "refcount"
};
ino = XFS_AGINO_TO_INO(mp, be32_to_cpu(agf->agf_seqno), agino);
error++;
return;
}
+ } else if (is_rtrefcount_inode(ino)) {
+ if (!S_ISREG(mode) || dip->di_format != XFS_DINODE_FMT_REFCOUNT) {
+ if (v)
+ dbprintf(
+ _("bad format %d for rtrefc inode %lld type %#o\n"),
+ dip->di_format, (long long)ino,
+ mode & S_IFMT);
+ error++;
+ return;
+ }
} else if ((((mode & S_IFMT) >> 12) > 15) ||
(!(okfmts[(mode & S_IFMT) >> 12] & (1 << dip->di_format)))) {
if (v)
} else if (is_rtrmap_inode(id->ino)) {
type = DBM_BTRTRMAP;
blkmap = blkmap_alloc(be32_to_cpu(dip->di_nextents));
+ } else if (is_rtrefcount_inode(id->ino)) {
+ type = DBM_BTRTREFC;
+ blkmap = blkmap_alloc(be32_to_cpu(dip->di_nextents));
}
else
type = DBM_DATA;
id->rgno = rtgroup_for_rtrmap_ino(mp, id->ino);
process_rtrmap(id, dip, &totiblocks);
break;
+ case XFS_DINODE_FMT_REFCOUNT:
+ id->rgno = rtgroup_for_rtrefcount_ino(mp, id->ino);
+ process_rtrefc(id, dip, &totiblocks);
+ break;
}
if (dip->di_forkoff) {
sbversion |= XFS_SB_VERSION_ATTRBIT;
case DBM_RTSUM:
case DBM_SYMLINK:
case DBM_BTRTRMAP:
+ case DBM_BTRTREFC:
case DBM_UNKNOWN:
bc = totdblocks + totiblocks +
atotdblocks + atotiblocks;
i, be32_to_cpu(rp[i].rm_startblock),
be32_to_cpu(rp[i].rm_startblock));
} else {
- lastblock = be32_to_cpu(rp[i].rm_startblock) +
- be32_to_cpu(rp[i].rm_blockcount);
+ lastblock = be32_to_cpu(rp[i].rm_startblock);
}
}
return;
}
}
+static void
+process_rtrefc(
+ struct inodata *id,
+ struct xfs_dinode *dip,
+ xfs_rfsblock_t *toti)
+{
+ xfs_extnum_t nex = 0;
+ xfs_rfsblock_t totd = 0;
+ struct xfs_rtrefcount_root *dib;
+ int whichfork = XFS_DATA_FORK;
+ int i;
+ int maxrecs;
+ xfs_rtrefcount_ptr_t *pp;
+
+ if (id->rgno == NULLRGNUMBER) {
+ dbprintf(
+ _("rt group for refcount ino %lld not found\n"),
+ id->ino);
+ error++;
+ return;
+ }
+
+ dib = (struct xfs_rtrefcount_root *)XFS_DFORK_PTR(dip, whichfork);
+ if (be16_to_cpu(dib->bb_level) >= mp->m_rtrefc_maxlevels) {
+ if (!sflag || id->ilist)
+ dbprintf(_("level for ino %lld rtrefc root too "
+ "large (%u)\n"),
+ id->ino,
+ be16_to_cpu(dib->bb_level));
+ error++;
+ return;
+ }
+ maxrecs = libxfs_rtrefcountbt_droot_maxrecs(
+ XFS_DFORK_SIZE(dip, mp, whichfork),
+ dib->bb_level == 0);
+ if (be16_to_cpu(dib->bb_numrecs) > maxrecs) {
+ if (!sflag || id->ilist)
+ dbprintf(_("numrecs for ino %lld rtrefc 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_refcount_rec *rp;
+ xfs_fsblock_t lastblock;
+
+ rp = xfs_rtrefcount_droot_rec_addr(dib, 1);
+ lastblock = 0;
+ for (i = 0; i < be16_to_cpu(dib->bb_numrecs); i++) {
+ if (be32_to_cpu(rp[i].rc_startblock) < lastblock) {
+ dbprintf(_(
+ "out-of-order rtrefc btree record %d (%u %u) root\n"),
+ i, be32_to_cpu(rp[i].rc_startblock),
+ be32_to_cpu(rp[i].rc_startblock));
+ } else {
+ lastblock = be32_to_cpu(rp[i].rc_startblock) +
+ be32_to_cpu(rp[i].rc_blockcount);
+ }
+ }
+ return;
+ } else {
+ pp = xfs_rtrefcount_droot_ptr_addr(dib, 1, maxrecs);
+ 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_rtrefc, DBM_BTRTREFC,
+ id, &totd, toti,
+ &nex, NULL, 1, TYP_RTREFCBT);
+ }
+}
+
static xfs_ino_t
process_sf_dir_v2(
struct xfs_dinode *dip,
be32_to_cpu(rp[i].rm_blockcount),
agno, bno, lastblock);
} else {
- lastblock = be32_to_cpu(rp[i].rm_startblock) +
- be32_to_cpu(rp[i].rm_blockcount);
+ lastblock = be32_to_cpu(rp[i].rm_startblock);
}
}
return;
TYP_REFCBT);
}
+static void
+scanfunc_rtrefc(
+ 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_rtrefcount_ptr_t *pp;
+ struct xfs_refcount_rec *rp;
+ xfs_rtblock_t lastblock;
+
+ agno = XFS_FSB_TO_AGNO(mp, bno);
+ agbno = XFS_FSB_TO_AGBNO(mp, bno);
+ if (be32_to_cpu(block->bb_magic) != XFS_RTREFC_CRC_MAGIC) {
+ dbprintf(_("bad magic # %#x in rtrefcbt block %u/%u\n"),
+ be32_to_cpu(block->bb_magic), agno, agbno);
+ serious_error++;
+ return;
+ }
+ if (be16_to_cpu(block->bb_level) != level) {
+ if (!sflag)
+ dbprintf(_("expected level %d got %d in rtrefcntbt block "
+ "%u/%u\n"),
+ level, be16_to_cpu(block->bb_level), agno, agbno);
+ error++;
+ }
+ set_dbmap(agno, agbno, 1, type, agno, agbno);
+ set_inomap(agno, agbno, 1, id);
+ (*toti)++;
+ if (level == 0) {
+ if (be16_to_cpu(block->bb_numrecs) > mp->m_rtrefc_mxr[0] ||
+ (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_rtrefc_mnr[0])) {
+ dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
+ "rtrefcntbt block %u/%u\n"),
+ be16_to_cpu(block->bb_numrecs), mp->m_rtrefc_mnr[0],
+ mp->m_rtrefc_mxr[0], agno, agbno);
+ serious_error++;
+ return;
+ }
+ rp = xfs_rtrefcount_rec_addr(block, 1);
+ lastblock = 0;
+ for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
+ xfs_rtblock_t rtbno;
+
+ if (be32_to_cpu(rp[i].rc_refcount) == 1) {
+ xfs_fsblock_t bno;
+ char *msg;
+
+ bno = be32_to_cpu(rp[i].rc_startblock);
+ if (bno & XFS_REFC_COWFLAG) {
+ bno &= ~XFS_REFC_COWFLAG;
+ msg = _(
+ "leftover rt CoW extent (%lu) len %u\n");
+ } else {
+ msg = _(
+ "leftover rt CoW extent at unexpected address (%lu) len %lu\n");
+ }
+ dbprintf(msg,
+ agbno,
+ be32_to_cpu(rp[i].rc_blockcount));
+ rtbno = xfs_rgbno_to_rtb(mp, id->rgno, bno);
+ set_rdbmap(rtbno,
+ be32_to_cpu(rp[i].rc_blockcount),
+ DBM_COWDATA);
+ } else {
+ rtbno = xfs_rgbno_to_rtb(mp, id->rgno,
+ be32_to_cpu(rp[i].rc_startblock));
+ set_rdbmap(rtbno,
+ be32_to_cpu(rp[i].rc_blockcount),
+ DBM_RLRTDATA);
+ }
+ if (be32_to_cpu(rp[i].rc_startblock) < lastblock) {
+ dbprintf(_(
+ "out-of-order rt refcnt btree record %d (%llu %llu) block %llu\n"),
+ i, be32_to_cpu(rp[i].rc_startblock),
+ be32_to_cpu(rp[i].rc_startblock),
+ bno);
+ } else {
+ lastblock = be32_to_cpu(rp[i].rc_startblock) +
+ be32_to_cpu(rp[i].rc_blockcount);
+ }
+ }
+ return;
+ }
+ if (be16_to_cpu(block->bb_numrecs) > mp->m_rtrefc_mxr[1] ||
+ (isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_rtrefc_mnr[1])) {
+ dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in rtrefcntbt "
+ "block %u/%u\n"),
+ be16_to_cpu(block->bb_numrecs), mp->m_rtrefc_mnr[1],
+ mp->m_rtrefc_mxr[1], agno, agbno);
+ serious_error++;
+ return;
+ }
+ pp = xfs_rtrefcount_ptr_addr(block, 1, mp->m_rtrefc_mxr[1]);
+ for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
+ scan_lbtree(be64_to_cpu(pp[i]), level, scanfunc_rtrefc,
+ type, id, totd, toti, nex, blkmapp, 0, btype);
+}
+
static void
set_dbmap(
xfs_agnumber_t agno,