blob: 3b713bdc5a305cbfab716b53b7932c2929f87fc2 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include <math.h>
#include <sys/time.h>
#include "bmap.h"
#include "check.h"
#include "command.h"
#include "io.h"
#include "type.h"
#include "fprint.h"
#include "faddr.h"
#include "field.h"
#include "sb.h"
#include "output.h"
#include "init.h"
#include "malloc.h"
#include "dir2.h"
typedef enum {
IS_USER_QUOTA, IS_PROJECT_QUOTA, IS_GROUP_QUOTA,
} qtype_t;
typedef enum {
DBM_UNKNOWN, DBM_AGF, DBM_AGFL, DBM_AGI,
DBM_ATTR, DBM_BTBMAPA, DBM_BTBMAPD, DBM_BTBNO,
DBM_BTCNT, DBM_BTINO, DBM_DATA, DBM_DIR,
DBM_FREE1, DBM_FREE2, DBM_FREELIST, DBM_INODE,
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_NDBM
} dbm_t;
typedef struct inodata {
struct inodata *next;
nlink_t link_set;
nlink_t link_add;
bool isdir;
bool isreflink;
char security;
char ilist;
xfs_ino_t ino;
struct inodata *parent;
char *name;
} inodata_t;
#define MIN_INODATA_HASH_SIZE 256
#define MAX_INODATA_HASH_SIZE 65536
#define INODATA_AVG_HASH_LENGTH 8
typedef struct qinfo {
xfs_qcnt_t bc;
xfs_qcnt_t ic;
xfs_qcnt_t rc;
} qinfo_t;
#define QDATA_HASH_SIZE 256
typedef struct qdata {
struct qdata *next;
xfs_dqid_t id;
qinfo_t count;
qinfo_t dq;
} qdata_t;
typedef struct blkent {
xfs_fileoff_t startoff;
int nblks;
xfs_fsblock_t blks[1];
} blkent_t;
#define BLKENT_SIZE(n) \
(offsetof(blkent_t, blks) + (sizeof(xfs_fsblock_t) * (n)))
typedef struct blkmap {
int naents;
int nents;
blkent_t *ents[1];
} blkmap_t;
#define BLKMAP_SIZE(n) \
(offsetof(blkmap_t, ents) + (sizeof(blkent_t *) * (n)))
typedef struct freetab {
int naents;
int nents;
xfs_dir2_data_off_t ents[1];
} freetab_t;
#define FREETAB_SIZE(n) \
(offsetof(freetab_t, ents) + (sizeof(xfs_dir2_data_off_t) * (n)))
typedef struct dirhash {
struct dirhash *next;
__u32 hashval;
__u32 address;
int seen;
} dirhash_t;
#define DIR_HASH_SIZE 1024
#define DIR_HASH_FUNC(h,a) (((h) ^ (a)) % DIR_HASH_SIZE)
static xfs_extlen_t agffreeblks;
static xfs_extlen_t agflongest;
static uint64_t agf_aggr_freeblks; /* aggregate count over all */
static uint32_t agfbtreeblks;
static int lazycount;
static xfs_agino_t agicount;
static xfs_agino_t agifreecount;
static xfs_fsblock_t *blist;
static int blist_size;
static char **dbmap; /* really dbm_t:8 */
static dirhash_t **dirhash;
static int error;
static uint64_t fdblocks;
static uint64_t frextents;
static uint64_t icount;
static uint64_t ifree;
static inodata_t ***inodata;
static int inodata_hash_size;
static inodata_t ***inomap;
static int nflag;
static int pflag;
static int tflag;
static qdata_t **qpdata;
static int qpdo;
static qdata_t **qudata;
static int qudo;
static qdata_t **qgdata;
static int qgdo;
static unsigned sbversion;
static int sbver_err;
static int serious_error;
static int sflag;
static xfs_suminfo_t *sumcompute;
static xfs_suminfo_t *sumfile;
static const char *typename[] = {
"unknown",
"agf",
"agfl",
"agi",
"attr",
"btbmapa",
"btbmapd",
"btbno",
"btcnt",
"btino",
"data",
"dir",
"free1",
"free2",
"freelist",
"inode",
"log",
"missing",
"quota",
"rtbitmap",
"rtdata",
"rtfree",
"rtsum",
"sb",
"symlink",
"btfino",
"btrmap",
"btrefcnt",
"rldata",
"cowdata",
NULL
};
/*
* Make sure typename has the same number of elements as there are DBM types.
* This function isn't called anywhere; we just use it to trip up the compiler.
*/
static inline void check_typename(void)
{
BUILD_BUG_ON(ARRAY_SIZE(typename) != DBM_NDBM + 1);
}
static int verbose;
#define CHECK_BLIST(b) (blist_size && check_blist(b))
#define CHECK_BLISTA(a,b) \
(blist_size && check_blist(XFS_AGB_TO_FSB(mp, a, b)))
typedef void (*scan_lbtree_f_t)(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);
typedef void (*scan_sbtree_f_t)(struct xfs_btree_block *block,
int level,
xfs_agf_t *agf,
xfs_agblock_t bno,
int isroot);
static void add_blist(xfs_fsblock_t bno);
static void add_ilist(xfs_ino_t ino);
static void addlink_inode(inodata_t *id);
static void addname_inode(inodata_t *id, char *name, int namelen);
static void addparent_inode(inodata_t *id, xfs_ino_t parent);
static void blkent_append(blkent_t **entp, xfs_fsblock_t b,
xfs_extlen_t c);
static blkent_t *blkent_new(xfs_fileoff_t o, xfs_fsblock_t b,
xfs_extlen_t c);
static void blkent_prepend(blkent_t **entp, xfs_fsblock_t b,
xfs_extlen_t c);
static blkmap_t *blkmap_alloc(xfs_extnum_t);
static void blkmap_free(blkmap_t *blkmap);
static xfs_fsblock_t blkmap_get(blkmap_t *blkmap, xfs_fileoff_t o);
static int blkmap_getn(blkmap_t *blkmap, xfs_fileoff_t o, int nb,
bmap_ext_t **bmpp);
static void blkmap_grow(blkmap_t **blkmapp, blkent_t **entp,
blkent_t *newent);
static xfs_fileoff_t blkmap_next_off(blkmap_t *blkmap, xfs_fileoff_t o,
int *t);
static void blkmap_set_blk(blkmap_t **blkmapp, xfs_fileoff_t o,
xfs_fsblock_t b);
static void blkmap_set_ext(blkmap_t **blkmapp, xfs_fileoff_t o,
xfs_fsblock_t b, xfs_extlen_t c);
static void blkmap_shrink(blkmap_t *blkmap, blkent_t **entp);
static int blockfree_f(int argc, char **argv);
static int blockget_f(int argc, char **argv);
static int blocktrash_f(int argc, char **argv);
static int blockuse_f(int argc, char **argv);
static int check_blist(xfs_fsblock_t bno);
static void check_dbmap(xfs_agnumber_t agno, xfs_agblock_t agbno,
xfs_extlen_t len, dbm_t type,
int ignore_reflink);
static int check_inomap(xfs_agnumber_t agno, xfs_agblock_t agbno,
xfs_extlen_t len, xfs_ino_t c_ino);
static void check_linkcounts(xfs_agnumber_t agno);
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);
static int check_rinomap(xfs_rfsblock_t bno, xfs_extlen_t len,
xfs_ino_t c_ino);
static void check_rootdir(void);
static int check_rrange(xfs_rfsblock_t bno, xfs_extlen_t len);
static void check_set_dbmap(xfs_agnumber_t agno,
xfs_agblock_t agbno, xfs_extlen_t len,
dbm_t type1, dbm_t type2,
xfs_agnumber_t c_agno,
xfs_agblock_t c_agbno);
static void check_set_rdbmap(xfs_rfsblock_t bno, xfs_extlen_t len,
dbm_t type1, dbm_t type2);
static void check_summary(void);
static void checknot_dbmap(xfs_agnumber_t agno, xfs_agblock_t agbno,
xfs_extlen_t len, int typemask);
static void checknot_rdbmap(xfs_rfsblock_t bno, xfs_extlen_t len,
int typemask);
static void dir_hash_add(xfs_dahash_t hash,
xfs_dir2_dataptr_t addr);
static void dir_hash_check(inodata_t *id, int v);
static void dir_hash_done(void);
static void dir_hash_init(void);
static int dir_hash_see(xfs_dahash_t hash,
xfs_dir2_dataptr_t addr);
static inodata_t *find_inode(xfs_ino_t ino, int add);
static void free_inodata(xfs_agnumber_t agno);
static int init(int argc, char **argv);
static char *inode_name(xfs_ino_t ino, inodata_t **ipp);
static int ncheck_f(int argc, char **argv);
static char *prepend_path(char *oldpath, char *parent);
static xfs_ino_t process_block_dir_v2(blkmap_t *blkmap, int *dot,
int *dotdot, inodata_t *id);
static void process_bmbt_reclist(xfs_bmbt_rec_t *rp, int numrecs,
dbm_t type, inodata_t *id,
xfs_rfsblock_t *tot,
blkmap_t **blkmapp);
static void process_btinode(inodata_t *id, xfs_dinode_t *dip,
dbm_t type, xfs_rfsblock_t *totd,
xfs_rfsblock_t *toti, xfs_extnum_t *nex,
blkmap_t **blkmapp, int whichfork);
static xfs_ino_t process_data_dir_v2(int *dot, int *dotdot,
inodata_t *id, int v,
xfs_dablk_t dabno,
freetab_t **freetabp);
static xfs_dir2_data_free_t *process_data_dir_v2_freefind(
struct xfs_dir2_data_hdr *data,
struct xfs_dir2_data_unused *dup);
static void process_dir(xfs_dinode_t *dip, blkmap_t *blkmap,
inodata_t *id);
static int process_dir_v2(xfs_dinode_t *dip, blkmap_t *blkmap,
int *dot, int *dotdot, inodata_t *id,
xfs_ino_t *parent);
static void process_exinode(inodata_t *id, xfs_dinode_t *dip,
dbm_t type, xfs_rfsblock_t *totd,
xfs_rfsblock_t *toti, xfs_extnum_t *nex,
blkmap_t **blkmapp, int whichfork);
static void process_inode(xfs_agf_t *agf, xfs_agino_t agino,
xfs_dinode_t *dip, int isfree);
static void process_lclinode(inodata_t *id, xfs_dinode_t *dip,
dbm_t type, xfs_rfsblock_t *totd,
xfs_rfsblock_t *toti, xfs_extnum_t *nex,
blkmap_t **blkmapp, int whichfork);
static xfs_ino_t process_leaf_node_dir_v2(blkmap_t *blkmap, int *dot,
int *dotdot, inodata_t *id,
xfs_fsize_t dirsize);
static void process_leaf_node_dir_v2_free(inodata_t *id, int v,
xfs_dablk_t dbno,
freetab_t *freetab);
static void process_leaf_node_dir_v2_int(inodata_t *id, int v,
xfs_dablk_t dbno,
freetab_t *freetab);
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 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,
int dq, xfs_qcnt_t bc, xfs_qcnt_t ic,
xfs_qcnt_t rc);
static void quota_add1(qdata_t **qt, xfs_dqid_t id, int dq,
xfs_qcnt_t bc, xfs_qcnt_t ic,
xfs_qcnt_t rc);
static void quota_check(char *s, qdata_t **qt);
static void quota_init(void);
static void scan_ag(xfs_agnumber_t agno);
static void scan_freelist(xfs_agf_t *agf);
static void scan_lbtree(xfs_fsblock_t root, int nlevels,
scan_lbtree_f_t func, dbm_t type,
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 scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
int nlevels, int isroot,
scan_sbtree_f_t func, typnm_t btype);
static void scanfunc_bmap(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);
static void scanfunc_cnt(struct xfs_btree_block *block, int level,
xfs_agf_t *agf, xfs_agblock_t bno,
int isroot);
static void scanfunc_ino(struct xfs_btree_block *block, int level,
xfs_agf_t *agf, xfs_agblock_t bno,
int isroot);
static void scanfunc_fino(struct xfs_btree_block *block, int level,
struct xfs_agf *agf, xfs_agblock_t bno,
int isroot);
static void scanfunc_rmap(struct xfs_btree_block *block, int level,
struct xfs_agf *agf, xfs_agblock_t bno,
int isroot);
static void scanfunc_refcnt(struct xfs_btree_block *block, int level,
struct xfs_agf *agf, xfs_agblock_t bno,
int isroot);
static void set_dbmap(xfs_agnumber_t agno, xfs_agblock_t agbno,
xfs_extlen_t len, dbm_t type,
xfs_agnumber_t c_agno, xfs_agblock_t c_agbno);
static void set_inomap(xfs_agnumber_t agno, xfs_agblock_t agbno,
xfs_extlen_t len, inodata_t *id);
static void set_rdbmap(xfs_rfsblock_t bno, xfs_extlen_t len,
dbm_t type);
static void set_rinomap(xfs_rfsblock_t bno, xfs_extlen_t len,
inodata_t *id);
static void setlink_inode(inodata_t *id, nlink_t nlink, int isdir,
int security);
static const cmdinfo_t blockfree_cmd =
{ "blockfree", NULL, blockfree_f, 0, 0, 0,
NULL, N_("free block usage information"), NULL };
static const cmdinfo_t blockget_cmd =
{ "blockget", "check", blockget_f, 0, -1, 0,
N_("[-s|-v] [-n] [-t] [-b bno]... [-i ino] ..."),
N_("get block usage and check consistency"), NULL };
static const cmdinfo_t blocktrash_cmd =
{ "blocktrash", NULL, blocktrash_f, 0, -1, 0,
N_("[-n count] [-x minlen] [-y maxlen] [-s seed] [-0123] [-t type] ..."),
N_("trash randomly selected block(s)"), NULL };
static const cmdinfo_t blockuse_cmd =
{ "blockuse", NULL, blockuse_f, 0, 3, 0,
N_("[-n] [-c blockcount]"),
N_("print usage for current block(s)"), NULL };
static const cmdinfo_t ncheck_cmd =
{ "ncheck", NULL, ncheck_f, 0, -1, 0,
N_("[-s] [-i ino] ..."),
N_("print inode-name pairs"), NULL };
static void
add_blist(
xfs_fsblock_t bno)
{
blist_size++;
blist = xrealloc(blist, blist_size * sizeof(bno));
blist[blist_size - 1] = bno;
}
static void
add_ilist(
xfs_ino_t ino)
{
inodata_t *id;
id = find_inode(ino, 1);
if (id == NULL) {
dbprintf(_("-i %lld bad inode number\n"), ino);
return;
}
id->ilist = 1;
}
static void
addlink_inode(
inodata_t *id)
{
id->link_add++;
if (verbose || id->ilist)
dbprintf(_("inode %lld add link, now %u\n"), id->ino,
id->link_add);
}
static void
addname_inode(
inodata_t *id,
char *name,
int namelen)
{
if (!nflag || id->name)
return;
id->name = xmalloc(namelen + 1);
memcpy(id->name, name, namelen);
id->name[namelen] = '\0';
}
static void
addparent_inode(
inodata_t *id,
xfs_ino_t parent)
{
inodata_t *pid;
pid = find_inode(parent, 1);
id->parent = pid;
if (verbose || id->ilist || (pid && pid->ilist))
dbprintf(_("inode %lld parent %lld\n"), id->ino, parent);
}
static void
blkent_append(
blkent_t **entp,
xfs_fsblock_t b,
xfs_extlen_t c)
{
blkent_t *ent;
int i;
ent = *entp;
*entp = ent = xrealloc(ent, BLKENT_SIZE(c + ent->nblks));
for (i = 0; i < c; i++)
ent->blks[ent->nblks + i] = b + i;
ent->nblks += c;
}
static blkent_t *
blkent_new(
xfs_fileoff_t o,
xfs_fsblock_t b,
xfs_extlen_t c)
{
blkent_t *ent;
int i;
ent = xmalloc(BLKENT_SIZE(c));
ent->nblks = c;
ent->startoff = o;
for (i = 0; i < c; i++)
ent->blks[i] = b + i;
return ent;
}
static void
blkent_prepend(
blkent_t **entp,
xfs_fsblock_t b,
xfs_extlen_t c)
{
int i;
blkent_t *newent;
blkent_t *oldent;
oldent = *entp;
newent = xmalloc(BLKENT_SIZE(oldent->nblks + c));
newent->nblks = oldent->nblks + c;
newent->startoff = oldent->startoff - c;
for (i = 0; i < c; i++)
newent->blks[i] = b + c;
for (; i < oldent->nblks + c; i++)
newent->blks[i] = oldent->blks[i - c];
xfree(oldent);
*entp = newent;
}
static blkmap_t *
blkmap_alloc(
xfs_extnum_t nex)
{
blkmap_t *blkmap;
if (nex < 1)
nex = 1;
blkmap = xmalloc(BLKMAP_SIZE(nex));
blkmap->naents = nex;
blkmap->nents = 0;
return blkmap;
}
static void
blkmap_free(
blkmap_t *blkmap)
{
blkent_t **entp;
xfs_extnum_t i;
for (i = 0, entp = blkmap->ents; i < blkmap->nents; i++, entp++)
xfree(*entp);
xfree(blkmap);
}
static xfs_fsblock_t
blkmap_get(
blkmap_t *blkmap,
xfs_fileoff_t o)
{
blkent_t *ent;
blkent_t **entp;
int i;
for (i = 0, entp = blkmap->ents; i < blkmap->nents; i++, entp++) {
ent = *entp;
if (o >= ent->startoff && o < ent->startoff + ent->nblks)
return ent->blks[o - ent->startoff];
}
return NULLFSBLOCK;
}
static int
blkmap_getn(
blkmap_t *blkmap,
xfs_fileoff_t o,
int nb,
bmap_ext_t **bmpp)
{
bmap_ext_t *bmp;
blkent_t *ent;
xfs_fileoff_t ento;
blkent_t **entp;
int i;
int nex;
for (i = nex = 0, bmp = NULL, entp = blkmap->ents;
i < blkmap->nents;
i++, entp++) {
ent = *entp;
if (ent->startoff >= o + nb)
break;
if (ent->startoff + ent->nblks <= o)
continue;
for (ento = ent->startoff;
ento < ent->startoff + ent->nblks && ento < o + nb;
ento++) {
if (ento < o)
continue;
if (bmp &&
bmp[nex - 1].startoff + bmp[nex - 1].blockcount ==
ento &&
bmp[nex - 1].startblock + bmp[nex - 1].blockcount ==
ent->blks[ento - ent->startoff])
bmp[nex - 1].blockcount++;
else {
bmp = realloc(bmp, ++nex * sizeof(*bmp));
bmp[nex - 1].startoff = ento;
bmp[nex - 1].startblock =
ent->blks[ento - ent->startoff];
bmp[nex - 1].blockcount = 1;
bmp[nex - 1].flag = 0;
}
}
}
*bmpp = bmp;
return nex;
}
static void
blkmap_grow(
blkmap_t **blkmapp,
blkent_t **entp,
blkent_t *newent)
{
blkmap_t *blkmap;
int i;
int idx;
blkmap = *blkmapp;
idx = (int)(entp - blkmap->ents);
if (blkmap->naents == blkmap->nents) {
blkmap = xrealloc(blkmap, BLKMAP_SIZE(blkmap->nents + 1));
*blkmapp = blkmap;
blkmap->naents++;
}
for (i = blkmap->nents; i > idx; i--)
blkmap->ents[i] = blkmap->ents[i - 1];
blkmap->ents[idx] = newent;
blkmap->nents++;
}
static xfs_fileoff_t
blkmap_last_off(
blkmap_t *blkmap)
{
blkent_t *ent;
if (!blkmap->nents)
return NULLFILEOFF;
ent = blkmap->ents[blkmap->nents - 1];
return ent->startoff + ent->nblks;
}
static xfs_fileoff_t
blkmap_next_off(
blkmap_t *blkmap,
xfs_fileoff_t o,
int *t)
{
blkent_t *ent;
blkent_t **entp;
if (!blkmap->nents)
return NULLFILEOFF;
if (o == NULLFILEOFF) {
*t = 0;
ent = blkmap->ents[0];
return ent->startoff;
}
entp = &blkmap->ents[*t];
ent = *entp;
if (o < ent->startoff + ent->nblks - 1)
return o + 1;
entp++;
if (entp >= &blkmap->ents[blkmap->nents])
return NULLFILEOFF;
(*t)++;
ent = *entp;
return ent->startoff;
}
static void
blkmap_set_blk(
blkmap_t **blkmapp,
xfs_fileoff_t o,
xfs_fsblock_t b)
{
blkmap_t *blkmap;
blkent_t *ent;
blkent_t **entp;
blkent_t *nextent;
blkmap = *blkmapp;
for (entp = blkmap->ents; entp < &blkmap->ents[blkmap->nents]; entp++) {
ent = *entp;
if (o < ent->startoff - 1) {
ent = blkent_new(o, b, 1);
blkmap_grow(blkmapp, entp, ent);
return;
}
if (o == ent->startoff - 1) {
blkent_prepend(entp, b, 1);
return;
}
if (o >= ent->startoff && o < ent->startoff + ent->nblks) {
ent->blks[o - ent->startoff] = b;
return;
}
if (o > ent->startoff + ent->nblks)
continue;
blkent_append(entp, b, 1);
if (entp == &blkmap->ents[blkmap->nents - 1])
return;
ent = *entp;
nextent = entp[1];
if (ent->startoff + ent->nblks < nextent->startoff)
return;
blkent_append(entp, nextent->blks[0], nextent->nblks);
blkmap_shrink(blkmap, &entp[1]);
return;
}
ent = blkent_new(o, b, 1);
blkmap_grow(blkmapp, entp, ent);
}
static void
blkmap_set_ext(
blkmap_t **blkmapp,
xfs_fileoff_t o,
xfs_fsblock_t b,
xfs_extlen_t c)
{
blkmap_t *blkmap;
blkent_t *ent;
blkent_t **entp;
xfs_extnum_t i;
blkmap = *blkmapp;
if (!blkmap->nents) {
blkmap->ents[0] = blkent_new(o, b, c);
blkmap->nents = 1;
return;
}
entp = &blkmap->ents[blkmap->nents - 1];
ent = *entp;
if (ent->startoff + ent->nblks == o) {
blkent_append(entp, b, c);
return;
}
if (ent->startoff + ent->nblks < o) {
ent = blkent_new(o, b, c);
blkmap_grow(blkmapp, &blkmap->ents[blkmap->nents], ent);
return;
}
for (i = 0; i < c; i++)
blkmap_set_blk(blkmapp, o + i, b + i);
}
static void
blkmap_shrink(
blkmap_t *blkmap,
blkent_t **entp)
{
int i;
int idx;
xfree(*entp);
idx = (int)(entp - blkmap->ents);
for (i = idx + 1; i < blkmap->nents; i++)
blkmap->ents[i] = blkmap->ents[i - 1];
blkmap->nents--;
}
/* ARGSUSED */
static int
blockfree_f(
int argc,
char **argv)
{
xfs_agnumber_t c;
int rt;
if (!dbmap) {
dbprintf(_("block usage information not allocated\n"));
return 0;
}
rt = mp->m_sb.sb_rextents != 0;
for (c = 0; c < mp->m_sb.sb_agcount; c++) {
xfree(dbmap[c]);
xfree(inomap[c]);
free_inodata(c);
}
if (rt) {
xfree(dbmap[c]);
xfree(inomap[c]);
xfree(sumcompute);
xfree(sumfile);
sumcompute = sumfile = NULL;
}
xfree(dbmap);
xfree(inomap);
xfree(inodata);
dbmap = NULL;
inomap = NULL;
inodata = NULL;
return 0;
}
/*
* Check consistency of xfs filesystem contents.
*/
static int
blockget_f(
int argc,
char **argv)
{
xfs_agnumber_t agno;
int oldprefix;
int sbyell;
if (dbmap) {
dbprintf(_("already have block usage information\n"));
return 0;
}
if (!init(argc, argv)) {
if (serious_error)
exitcode = 3;
else
exitcode = 1;
return 0;
}
oldprefix = dbprefix;
dbprefix |= pflag;
for (agno = 0, sbyell = 0; agno < mp->m_sb.sb_agcount; agno++) {
scan_ag(agno);
if (sbver_err > 4 && !sbyell && sbver_err >= agno) {
sbyell = 1;
dbprintf(_("WARNING: this may be a newer XFS "
"filesystem.\n"));
}
}
if (blist_size) {
xfree(blist);
blist = NULL;
blist_size = 0;
}
if (serious_error) {
exitcode = 2;
dbprefix = oldprefix;
return 0;
}
check_rootdir();
/*
* Check that there are no blocks either
* a) unaccounted for or
* b) bno-free but not cnt-free
*/
if (!tflag) { /* are we in test mode, faking out freespace? */
for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
checknot_dbmap(agno, 0, mp->m_sb.sb_agblocks,
(1 << DBM_UNKNOWN) | (1 << DBM_FREE1));
}
for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
check_linkcounts(agno);
if (mp->m_sb.sb_rblocks) {
checknot_rdbmap(0,
(xfs_extlen_t)(mp->m_sb.sb_rextents *
mp->m_sb.sb_rextsize),
1 << DBM_UNKNOWN);
check_summary();
}
if (mp->m_sb.sb_icount != icount) {
if (!sflag)
dbprintf(_("sb_icount %lld, counted %lld\n"),
mp->m_sb.sb_icount, icount);
error++;
}
if (mp->m_sb.sb_ifree != ifree) {
if (!sflag)
dbprintf(_("sb_ifree %lld, counted %lld\n"),
mp->m_sb.sb_ifree, ifree);
error++;
}
if (mp->m_sb.sb_fdblocks != fdblocks) {
if (!sflag)
dbprintf(_("sb_fdblocks %lld, counted %lld\n"),
mp->m_sb.sb_fdblocks, fdblocks);
error++;
}
if (lazycount && mp->m_sb.sb_fdblocks != agf_aggr_freeblks) {
if (!sflag)
dbprintf(_("sb_fdblocks %lld, aggregate AGF count %lld\n"),
mp->m_sb.sb_fdblocks, agf_aggr_freeblks);
error++;
}
if (mp->m_sb.sb_frextents != frextents) {
if (!sflag)
dbprintf(_("sb_frextents %lld, counted %lld\n"),
mp->m_sb.sb_frextents, frextents);
error++;
}
if (mp->m_sb.sb_bad_features2 != 0 &&
mp->m_sb.sb_bad_features2 != mp->m_sb.sb_features2) {
if (!sflag)
dbprintf(_("sb_features2 (0x%x) not same as "
"sb_bad_features2 (0x%x)\n"),
mp->m_sb.sb_features2,
mp->m_sb.sb_bad_features2);
error++;
}
if ((sbversion & XFS_SB_VERSION_ATTRBIT) &&
!xfs_sb_version_hasattr(&mp->m_sb)) {
if (!sflag)
dbprintf(_("sb versionnum missing attr bit %x\n"),
XFS_SB_VERSION_ATTRBIT);
error++;
}
if ((sbversion & XFS_SB_VERSION_QUOTABIT) &&
!xfs_sb_version_hasquota(&mp->m_sb)) {
if (!sflag)
dbprintf(_("sb versionnum missing quota bit %x\n"),
XFS_SB_VERSION_QUOTABIT);
error++;
}
if (!(sbversion & XFS_SB_VERSION_ALIGNBIT) &&
xfs_sb_version_hasalign(&mp->m_sb)) {
if (!sflag)
dbprintf(_("sb versionnum extra align bit %x\n"),
XFS_SB_VERSION_ALIGNBIT);
error++;
}
if (qudo)
quota_check("user", qudata);
if (qpdo)
quota_check("project", qpdata);
if (qgdo)
quota_check("group", qgdata);
if (sbver_err > mp->m_sb.sb_agcount / 2)
dbprintf(_("WARNING: this may be a newer XFS filesystem.\n"));
if (error)
exitcode = 3;
dbprefix = oldprefix;
return 0;
}
typedef struct ltab {
int min;
int max;
} ltab_t;
static void
blocktrash_b(
int bit_offset,
dbm_t type,
ltab_t *ltabp,
int mode)
{
int bit;
int bitno;
char *buf;
int byte;
int len;
int mask;
int newbit;
const struct xfs_buf_ops *stashed_ops;
static char *modestr[] = {
N_("zeroed"), N_("set"), N_("flipped"), N_("randomized")
};
xfs_agnumber_t agno;
xfs_agblock_t agbno;
agno = XFS_FSB_TO_AGNO(mp, XFS_DADDR_TO_FSB(mp, iocur_top->bb));
agbno = XFS_FSB_TO_AGBNO(mp, XFS_DADDR_TO_FSB(mp, iocur_top->bb));
if (iocur_top->len == 0) {
dbprintf(_("zero-length block %u/%u buffer to trash??\n"),
agno, agbno);
return;
}
len = (int)((random() % (ltabp->max - ltabp->min + 1)) + ltabp->min);
/*
* bit_offset >= 0: start fuzzing at this exact bit_offset.
* bit_offset < 0: pick an offset at least as high at -(bit_offset + 1).
*/
if (bit_offset < 0) {
bit_offset = -(bit_offset + 1);
bit_offset += (int)(random() % (int)((iocur_top->len - bit_offset) * NBBY));
}
if (bit_offset + len >= iocur_top->len * NBBY)
len = (iocur_top->len * NBBY) - bit_offset;
newbit = 0;
stashed_ops = iocur_top->bp->b_ops;
iocur_top->bp->b_ops = NULL;
if ((buf = iocur_top->data) == NULL) {
dbprintf(_("can't read block %u/%u for trashing\n"), agno, agbno);
return;
}
for (bitno = 0; bitno < len; bitno++) {
bit = (bit_offset + bitno) % (mp->m_sb.sb_blocksize * NBBY);
byte = bit / NBBY;
bit %= NBBY;
mask = 1 << bit;
switch (mode) {
case 0:
newbit = 0;
break;
case 1:
newbit = 1;
break;
case 2:
newbit = (buf[byte] & mask) == 0;
break;
case 3:
newbit = (int)random() & 1;
break;
}
if (newbit)
buf[byte] |= mask;
else
buf[byte] &= ~mask;
}
write_cur();
iocur_top->bp->b_ops = stashed_ops;
printf(_("blocktrash: %u/%u %s block %d bit%s starting %d:%d %s\n"),
agno, agbno, typename[type], len, len == 1 ? "" : "s",
bit_offset / NBBY, bit_offset % NBBY, modestr[mode]);
}
static int
blocktrash_f(
int argc,
char **argv)
{
xfs_agblock_t agbno;
xfs_agnumber_t agno;
xfs_rfsblock_t bi;
xfs_rfsblock_t blocks;
int c;
int count;
int done;
int goodmask;
int i;
ltab_t *lentab;
int lentablen;
int max;
int min;
int mode;
struct timeval now;
char *p;
xfs_rfsblock_t randb;
uint seed;
int sopt;
int tmask;
bool this_block = false;
int bit_offset = -1;
optind = 0;
count = 1;
min = 1;
max = 128 * NBBY;
mode = 2;
gettimeofday(&now, NULL);
seed = (unsigned int)(now.tv_sec ^ now.tv_usec);
sopt = 0;
tmask = 0;
goodmask = (1 << DBM_AGF) |
(1 << DBM_AGFL) |
(1 << DBM_AGI) |
(1 << DBM_ATTR) |
(1 << DBM_BTBMAPA) |
(1 << DBM_BTBMAPD) |
(1 << DBM_BTBNO) |
(1 << DBM_BTCNT) |
(1 << DBM_BTINO) |
(1 << DBM_DIR) |
(1 << DBM_INODE) |
(1 << DBM_LOG) |
(1 << DBM_QUOTA) |
(1 << DBM_RTBITMAP) |
(1 << DBM_RTSUM) |
(1 << DBM_SYMLINK) |
(1 << DBM_BTFINO) |
(1 << DBM_BTRMAP) |
(1 << DBM_BTREFC) |
(1 << DBM_SB);
while ((c = getopt(argc, argv, "0123n:o:s:t:x:y:z")) != EOF) {
switch (c) {
case '0':
mode = 0;
break;
case '1':
mode = 1;
break;
case '2':
mode = 2;
break;
case '3':
mode = 3;
break;
case 'n':
count = (int)strtol(optarg, &p, 0);
if (*p != '\0' || count <= 0) {
dbprintf(_("bad blocktrash count %s\n"), optarg);
return 0;
}
break;
case 'o': {
int relative = 0;
if (optarg[0] == '+') {
optarg++;
relative = 1;
}
bit_offset = (int)strtol(optarg, &p, 0);
if (*p != '\0' || bit_offset < 0) {
dbprintf(_("bad blocktrash offset %s\n"), optarg);
return 0;
}
if (relative)
bit_offset = -bit_offset - 1;
break;
}
case 's':
seed = (uint)strtoul(optarg, &p, 0);
sopt = 1;
break;
case 't':
for (i = 0; typename[i]; i++) {
if (strcmp(typename[i], optarg) == 0)
break;
}
if (!typename[i] || (((1 << i) & goodmask) == 0)) {
dbprintf(_("bad blocktrash type %s\n"), optarg);
return 0;
}
tmask |= 1 << i;
break;
case 'x':
min = (int)strtol(optarg, &p, 0);
if (*p != '\0' || min <= 0 ||
min > mp->m_sb.sb_blocksize * NBBY) {
dbprintf(_("bad blocktrash min %s\n"), optarg);
return 0;
}
break;
case 'y':
max = (int)strtol(optarg, &p, 0);
if (*p != '\0' || max <= 0 ||
max > mp->m_sb.sb_blocksize * NBBY) {
dbprintf(_("bad blocktrash max %s\n"), optarg);
return 0;
}
break;
case 'z':
this_block = true;
break;
default:
dbprintf(_("bad option for blocktrash command\n"));
return 0;
}
}
if (!this_block && !dbmap) {
dbprintf(_("must run blockget first\n"));
return 0;
}
if (this_block && iocur_sp == 0) {
dbprintf(_("nothing on stack\n"));
return 0;
}
if (min > max) {
dbprintf(_("bad min/max for blocktrash command\n"));
return 0;
}
if (tmask == 0)
tmask = goodmask & ~((1 << DBM_LOG) | (1 << DBM_SB));
lentab = xmalloc(sizeof(ltab_t));
lentab->min = lentab->max = min;
lentablen = 1;
for (i = min + 1; i <= max; i++) {
if ((i & (i - 1)) == 0) {
lentab = xrealloc(lentab,
sizeof(ltab_t) * (lentablen + 1));
lentab[lentablen].min = lentab[lentablen].max = i;
lentablen++;
} else
lentab[lentablen - 1].max = i;
}
if (!sopt)
dbprintf(_("blocktrash: seed %u\n"), seed);
srandom(seed);
if (this_block) {
blocktrash_b(bit_offset, DBM_UNKNOWN,
&lentab[random() % lentablen], mode);
goto out;
}
for (blocks = 0, agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
for (agbno = 0, p = dbmap[agno];
agbno < mp->m_sb.sb_agblocks;
agbno++, p++) {
if ((1 << *p) & tmask)
blocks++;
}
}
if (blocks == 0) {
dbprintf(_("blocktrash: no matching blocks\n"));
goto out;
}
for (i = 0; i < count; i++) {
randb = (xfs_rfsblock_t)((((int64_t)random() << 32) |
random()) % blocks);
for (bi = 0, agno = 0, done = 0;
!done && agno < mp->m_sb.sb_agcount;
agno++) {
for (agbno = 0, p = dbmap[agno];
agbno < mp->m_sb.sb_agblocks;
agbno++, p++) {
if (!((1 << *p) & tmask))
continue;
if (bi++ < randb)
continue;
push_cur();
set_cur(NULL,
XFS_AGB_TO_DADDR(mp, agno, agbno),
blkbb, DB_RING_IGN, NULL);
blocktrash_b(bit_offset, (dbm_t)*p,
&lentab[random() % lentablen], mode);
pop_cur();
done = 1;
break;
}
}
}
out:
xfree(lentab);
return 0;
}
static int
blockuse_f(
int argc,
char **argv)
{
xfs_agblock_t agbno;
xfs_agnumber_t agno;
int c;
int count;
xfs_agblock_t end;
xfs_fsblock_t fsb;
inodata_t *i;
char *p;
int shownames;
if (!dbmap) {
dbprintf(_("must run blockget first\n"));
return 0;
}
optind = 0;
shownames = 0;
fsb = XFS_DADDR_TO_FSB(mp, iocur_top->off >> BBSHIFT);
agno = XFS_FSB_TO_AGNO(mp, fsb);
end = agbno = XFS_FSB_TO_AGBNO(mp, fsb);
while ((c = getopt(argc, argv, "c:n")) != EOF) {
switch (c) {
case 'c':
count = (int)strtol(optarg, &p, 0);
end = agbno + count - 1;
if (*p != '\0' || count <= 0 ||
end >= mp->m_sb.sb_agblocks) {
dbprintf(_("bad blockuse count %s\n"), optarg);
return 0;
}
break;
case 'n':
if (!nflag) {
dbprintf(_("must run blockget -n first\n"));
return 0;
}
shownames = 1;
break;
default:
dbprintf(_("bad option for blockuse command\n"));
return 0;
}
}
while (agbno <= end) {
p = &dbmap[agno][agbno];
i = inomap[agno][agbno];
dbprintf(_("block %llu (%u/%u) type %s"),
(xfs_fsblock_t)XFS_AGB_TO_FSB(mp, agno, agbno),
agno, agbno, typename[(dbm_t)*p]);
if (i) {
dbprintf(_(" inode %lld"), i->ino);
if (shownames && (p = inode_name(i->ino, NULL))) {
dbprintf(" %s", p);
xfree(p);
}
}
dbprintf("\n");
agbno++;
}
return 0;
}
static int
check_blist(
xfs_fsblock_t bno)
{
int i;
for (i = 0; i < blist_size; i++) {
if (blist[i] == bno)
return 1;
}
return 0;
}
static void
check_dbmap(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_extlen_t len,
dbm_t type,
int ignore_reflink)
{
xfs_extlen_t i;
char *p;
dbm_t d;
for (i = 0, p = &dbmap[agno][agbno]; i < len; i++, p++) {
d = (dbm_t)*p;
if (ignore_reflink && (d == DBM_UNKNOWN || d == DBM_DATA ||
d == DBM_RLDATA))
continue;
if ((dbm_t)*p != type) {
if (!sflag || CHECK_BLISTA(agno, agbno + i)) {
dbprintf(_("block %u/%u expected type %s got "
"%s\n"),
agno, agbno + i, typename[type],
typename[(dbm_t)*p]);
}
error++;
}
}
}
void
check_init(void)
{
add_command(&blockfree_cmd);
add_command(&blockget_cmd);
if (expert_mode)
add_command(&blocktrash_cmd);
add_command(&blockuse_cmd);
add_command(&ncheck_cmd);
}
static int
check_inomap(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_extlen_t len,
xfs_ino_t c_ino)
{
xfs_extlen_t i;
inodata_t **idp;
int rval;
if (!check_range(agno, agbno, len)) {
dbprintf(_("blocks %u/%u..%u claimed by inode %lld\n"),
agno, agbno, agbno + len - 1, c_ino);
return 0;
}
for (i = 0, rval = 1, idp = &inomap[agno][agbno]; i < len; i++, idp++) {
if (*idp && !(*idp)->isreflink) {
if (!sflag || (*idp)->ilist ||
CHECK_BLISTA(agno, agbno + i))
dbprintf(_("block %u/%u claimed by inode %lld, "
"previous inum %lld\n"),
agno, agbno + i, c_ino, (*idp)->ino);
error++;
rval = 0;
}
}
return rval;
}
static void
check_linkcounts(
xfs_agnumber_t agno)
{
inodata_t *ep;
inodata_t **ht;
int idx;
char *path;
ht = inodata[agno];
for (idx = 0; idx < inodata_hash_size; ht++, idx++) {
ep = *ht;
while (ep) {
if (ep->link_set != ep->link_add || ep->link_set == 0) {
path = inode_name(ep->ino, NULL);
if (!path && ep->link_add)
path = xstrdup("?");
if (!sflag || ep->ilist) {
if (ep->link_add)
dbprintf(_("link count mismatch "
"for inode %lld (name "
"%s), nlink %d, "
"counted %d\n"),
ep->ino, path,
ep->link_set,
ep->link_add);
else if (ep->link_set)
dbprintf(_("disconnected inode "
"%lld, nlink %d\n"),
ep->ino, ep->link_set);
else
dbprintf(_("allocated inode %lld "
"has 0 link count\n"),
ep->ino);
}
if (path)
xfree(path);
error++;
} else if (verbose || ep->ilist) {
path = inode_name(ep->ino, NULL);
if (path) {
dbprintf(_("inode %lld name %s\n"),
ep->ino, path);
xfree(path);
}
}
ep = ep->next;
}
}
}
static int
check_range(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_extlen_t len)
{
xfs_extlen_t i;
xfs_agblock_t low = 0;
xfs_agblock_t high = 0;
int valid_range = 0;
int cur, prev = 0;
if (agno >= mp->m_sb.sb_agcount ||
agbno + len - 1 >= mp->m_sb.sb_agblocks) {
for (i = 0; i < len; i++) {
cur = !sflag || CHECK_BLISTA(agno, agbno + i) ? 1 : 0;
if (cur == 1 && prev == 0) {
low = high = agbno + i;
valid_range = 1;
} else if (cur == 0 && prev == 0) {
/* Do nothing */
} else if (cur == 0 && prev == 1) {
if (low == high) {
dbprintf(_("block %u/%u out of range\n"),
agno, low);
} else {
dbprintf(_("blocks %u/%u..%u "
"out of range\n"),
agno, low, high);
}
valid_range = 0;
} else if (cur == 1 && prev == 1) {
high = agbno + i;
}
prev = cur;
}
if (valid_range) {
if (low == high) {
dbprintf(_("block %u/%u out of range\n"),
agno, low);
} else {
dbprintf(_("blocks %u/%u..%u "
"out of range\n"),
agno, low, high);
}
}
error++;
return 0;
}
return 1;
}
static void
check_rdbmap(
xfs_rfsblock_t bno,
xfs_extlen_t len,
dbm_t type)
{
xfs_extlen_t i;
char *p;
for (i = 0, p = &dbmap[mp->m_sb.sb_agcount][bno]; i < len; i++, p++) {
if ((dbm_t)*p != type) {
if (!sflag || CHECK_BLIST(bno + i))
dbprintf(_("rtblock %llu expected type %s got "
"%s\n"),
bno + i, typename[type],
typename[(dbm_t)*p]);
error++;
}
}
}
static int
check_rinomap(
xfs_rfsblock_t bno,
xfs_extlen_t len,
xfs_ino_t c_ino)
{
xfs_extlen_t i;
inodata_t **idp;
int rval;
if (!check_rrange(bno, len)) {
dbprintf(_("rtblocks %llu..%llu claimed by inode %lld\n"),
bno, bno + len - 1, c_ino);
return 0;
}
for (i = 0, rval = 1, idp = &inomap[mp->m_sb.sb_agcount][bno];
i < len;
i++, idp++) {
if (*idp) {
if (!sflag || (*idp)->ilist || CHECK_BLIST(bno + i))
dbprintf(_("rtblock %llu claimed by inode %lld, "
"previous inum %lld\n"),
bno + i, c_ino, (*idp)->ino);
error++;
rval = 0;
}
}
return rval;
}
static void
check_rootdir(void)
{
inodata_t *id;
id = find_inode(mp->m_sb.sb_rootino, 0);
if (id == NULL) {
if (!sflag)
dbprintf(_("root inode %lld is missing\n"),
mp->m_sb.sb_rootino);
error++;
} else if (!id->isdir) {
if (!sflag || id->ilist)
dbprintf(_("root inode %lld is not a directory\n"),
mp->m_sb.sb_rootino);
error++;
}
}
static int
check_rrange(
xfs_rfsblock_t bno,
xfs_extlen_t len)
{
xfs_extlen_t i;
if (bno + len - 1 >= mp->m_sb.sb_rblocks) {
for (i = 0; i < len; i++) {
if (!sflag || CHECK_BLIST(bno + i))
dbprintf(_("rtblock %llu out of range\n"),
bno + i);
}
error++;
return 0;
}
return 1;
}
/*
* 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_reflink(
dbm_t type2)
{
if (!xfs_sb_version_hasreflink(&mp->m_sb))
return false;
if (type2 == DBM_DATA || type2 == DBM_RLDATA)
return true;
return false;
}
static void
check_set_dbmap(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_extlen_t len,
dbm_t type1,
dbm_t type2,
xfs_agnumber_t c_agno,
xfs_agblock_t c_agbno)
{
xfs_extlen_t i;
int mayprint;
char *p;
if (!check_range(agno, agbno, len)) {
dbprintf(_("blocks %u/%u..%u claimed by block %u/%u\n"), agno,
agbno, agbno + len - 1, c_agno, c_agbno);
return;
}
check_dbmap(agno, agbno, len, type1, is_reflink(type2));
mayprint = verbose | blist_size;
for (i = 0, p = &dbmap[agno][agbno]; i < len; i++, p++) {
if (*p == DBM_RLDATA && type2 == DBM_DATA)
; /* do nothing */
else if (*p == DBM_DATA && type2 == DBM_DATA)
*p = (char)DBM_RLDATA;
else
*p = (char)type2;
if (mayprint && (verbose || CHECK_BLISTA(agno, agbno + i)))
dbprintf(_("setting block %u/%u to %s\n"), agno, agbno + i,
typename[type2]);
}
}
static void
check_set_rdbmap(
xfs_rfsblock_t bno,
xfs_extlen_t len,
dbm_t type1,
dbm_t type2)
{
xfs_extlen_t i;
int mayprint;
char *p;
if (!check_rrange(bno, len))
return;
check_rdbmap(bno, len, type1);
mayprint = verbose | blist_size;
for (i = 0, p = &dbmap[mp->m_sb.sb_agcount][bno]; i < len; i++, p++) {
*p = (char)type2;
if (mayprint && (verbose || CHECK_BLIST(bno + i)))
dbprintf(_("setting rtblock %llu to %s\n"),
bno + i, typename[type2]);
}
}
static void
check_summary(void)
{
xfs_rfsblock_t bno;
xfs_suminfo_t *csp;
xfs_suminfo_t *fsp;
int log;
csp = sumcompute;
fsp = sumfile;
for (log = 0; log < mp->m_rsumlevels; log++) {
for (bno = 0;
bno < mp->m_sb.sb_rbmblocks;
bno++, csp++, fsp++) {
if (*csp != *fsp) {
if (!sflag)
dbprintf(_("rt summary mismatch, size %d "
"block %llu, file: %d, "
"computed: %d\n"),
log, bno, *fsp, *csp);
error++;
}
}
}
}
static void
checknot_dbmap(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_extlen_t len,
int typemask)
{
xfs_extlen_t i;
char *p;
if (!check_range(agno, agbno, len))
return;
for (i = 0, p = &dbmap[agno][agbno]; i < len; i++, p++) {
if ((1 << *p) & typemask) {
if (!sflag || CHECK_BLISTA(agno, agbno + i))
dbprintf(_("block %u/%u type %s not expected\n"),
agno, agbno + i, typename[(dbm_t)*p]);
error++;
}
}
}
static void
checknot_rdbmap(
xfs_rfsblock_t bno,
xfs_extlen_t len,
int typemask)
{
xfs_extlen_t i;
char *p;
if (!check_rrange(bno, len))
return;
for (i = 0, p = &dbmap[mp->m_sb.sb_agcount][bno]; i < len; i++, p++) {
if ((1 << *p) & typemask) {
if (!sflag || CHECK_BLIST(bno + i))
dbprintf(_("rtblock %llu type %s not expected\n"),
bno + i, typename[(dbm_t)*p]);
error++;
}
}
}
static void
dir_hash_add(
xfs_dahash_t hash,
xfs_dir2_dataptr_t addr)
{
int i;
dirhash_t *p;
i = DIR_HASH_FUNC(hash, addr);
p = malloc(sizeof(*p));
p->next = dirhash[i];
dirhash[i] = p;
p->hashval = hash;
p->address = addr;
p->seen = 0;
}
static void
dir_hash_check(
inodata_t *id,
int v)
{
int i;
dirhash_t *p;
for (i = 0; i < DIR_HASH_SIZE; i++) {
for (p = dirhash[i]; p; p = p->next) {
if (p->seen)
continue;
if (!sflag || id->ilist || v)
dbprintf(_("dir ino %lld missing leaf entry for "
"%x/%x\n"),
id->ino, p->hashval, p->address);
error++;
}
}
}
static void
dir_hash_done(void)
{
int i;
dirhash_t *n;
dirhash_t *p;
for (i = 0; i < DIR_HASH_SIZE; i++) {
for (p = dirhash[i]; p; p = n) {
n = p->next;
free(p);
}
dirhash[i] = NULL;
}
}
static void
dir_hash_init(void)
{
if (!dirhash)
dirhash = calloc(DIR_HASH_SIZE, sizeof(*dirhash));
}
static int
dir_hash_see(
xfs_dahash_t hash,
xfs_dir2_dataptr_t addr)
{
int i;
dirhash_t *p;
i = DIR_HASH_FUNC(hash, addr);
for (p = dirhash[i]; p; p = p->next) {
if (p->hashval == hash && p->address == addr) {
if (p->seen)
return 1;
p->seen = 1;
return 0;
}
}
return -1;
}
static inodata_t *
find_inode(
xfs_ino_t ino,
int add)
{
xfs_agino_t agino;
xfs_agnumber_t agno;
inodata_t *ent;
inodata_t **htab;
xfs_agino_t ih;
agno = XFS_INO_TO_AGNO(mp, ino);
agino = XFS_INO_TO_AGINO(mp, ino);
if (agno >= mp->m_sb.sb_agcount ||
XFS_AGINO_TO_INO(mp, agno, agino) != ino)
return NULL;
htab = inodata[agno];
ih = agino % inodata_hash_size;
ent = htab[ih];
while (ent) {
if (ent->ino == ino)
return ent;
ent = ent->next;
}
if (!add)
return NULL;
ent = xcalloc(1, sizeof(*ent));
ent->ino = ino;
ent->next = htab[ih];
htab[ih] = ent;
return ent;
}
static void
free_inodata(
xfs_agnumber_t agno)
{
inodata_t *hp;
inodata_t **ht;
int i;
inodata_t *next;
ht = inodata[agno];
for (i = 0; i < inodata_hash_size; i++) {
hp = ht[i];
while (hp) {
next = hp->next;
if (hp->name)
xfree(hp->name);
xfree(hp);
hp = next;
}
}
xfree(ht);
}
static int
init(
int argc,
char **argv)
{
xfs_fsblock_t bno;
int c;
xfs_ino_t ino;
int rt;
serious_error = 0;
if (mp->m_sb.sb_magicnum != XFS_SB_MAGIC) {
dbprintf(_("bad superblock magic number %x, giving up\n"),
mp->m_sb.sb_magicnum);
serious_error = 1;
return 0;
}
if (!sb_logcheck())
return 0;
rt = mp->m_sb.sb_rextents != 0;
dbmap = xmalloc((mp->m_sb.sb_agcount + rt) * sizeof(*dbmap));
inomap = xmalloc((mp->m_sb.sb_agcount + rt) * sizeof(*inomap));
inodata = xmalloc(mp->m_sb.sb_agcount * sizeof(*inodata));
inodata_hash_size =
(int)max(min(mp->m_sb.sb_icount /
(INODATA_AVG_HASH_LENGTH * mp->m_sb.sb_agcount),
MAX_INODATA_HASH_SIZE),
MIN_INODATA_HASH_SIZE);
for (c = 0; c < mp->m_sb.sb_agcount; c++) {
dbmap[c] = xcalloc(mp->m_sb.sb_agblocks, sizeof(**dbmap));
inomap[c] = xcalloc(mp->m_sb.sb_agblocks, sizeof(**inomap));
inodata[c] = xcalloc(inodata_hash_size, sizeof(**inodata));
}
if (rt) {
dbmap[c] = xcalloc(mp->m_sb.sb_rblocks, sizeof(**dbmap));
inomap[c] = xcalloc(mp->m_sb.sb_rblocks, sizeof(**inomap));
sumfile = xcalloc(mp->m_rsumsize, 1);
sumcompute = xcalloc(mp->m_rsumsize, 1);
}
nflag = sflag = tflag = verbose = optind = 0;
while ((c = getopt(argc, argv, "b:i:npstv")) != EOF) {
switch (c) {
case 'b':
bno = strtoll(optarg, NULL, 10);
add_blist(bno);
break;
case 'i':
ino = strtoll(optarg, NULL, 10);
add_ilist(ino);
break;
case 'n':
nflag = 1;
break;
case 'p':
pflag = 1;
break;
case 's':
sflag = 1;
break;
case 't':
tflag = 1;
break;
case 'v':
verbose = 1;
break;
default:
dbprintf(_("bad option for blockget command\n"));
return 0;
}
}
error = sbver_err = serious_error = 0;
fdblocks = frextents = icount = ifree = 0;
sbversion = XFS_SB_VERSION_4;
/*
* Note that inoalignmt == 0 is valid when fsb size is large enough for
* at least one full inode record per block. Check this case explicitly.
*/
if (mp->m_sb.sb_inoalignmt ||
(xfs_sb_version_hasalign(&mp->m_sb) &&
mp->m_sb.sb_inopblock >= XFS_INODES_PER_CHUNK))
sbversion |= XFS_SB_VERSION_ALIGNBIT;
if ((mp->m_sb.sb_uquotino && mp->m_sb.sb_uquotino != NULLFSINO) ||
(mp->m_sb.sb_gquotino && mp->m_sb.sb_gquotino != NULLFSINO) ||
(mp->m_sb.sb_pquotino && mp->m_sb.sb_pquotino != NULLFSINO))
sbversion |= XFS_SB_VERSION_QUOTABIT;
quota_init();
return 1;
}
static char *
inode_name(
xfs_ino_t ino,
inodata_t **ipp)
{
inodata_t *id;
char *npath;
char *path;
id = find_inode(ino, 0);
if (ipp)
*ipp = id;
if (id == NULL)
return NULL;
if (id->name == NULL)
return NULL;
path = xstrdup(id->name);
while (id->parent) {
id = id->parent;
if (id->name == NULL)
break;
npath = prepend_path(path, id->name);
xfree(path);
path = npath;
}
return path;
}
static int
ncheck_f(
int argc,
char **argv)
{
xfs_agnumber_t agno;
int c;
inodata_t *hp;
inodata_t **ht;
int i;
inodata_t *id;
xfs_ino_t *ilist;
int ilist_size;
xfs_ino_t *ilp;
xfs_ino_t ino;
char *p;
int security;
if (!inodata || !nflag) {
dbprintf(_("must run blockget -n first\n"));
return 0;
}
security = optind = ilist_size = 0;
ilist = NULL;
while ((c = getopt(argc, argv, "i:s")) != EOF) {
switch (c) {
case 'i':
ino = strtoll(optarg, NULL, 10);
ilist = xrealloc(ilist, (ilist_size + 1) *
sizeof(*ilist));
ilist[ilist_size++] = ino;
break;
case 's':
security = 1;
break;
default:
dbprintf(_("bad option -%c for ncheck command\n"), c);
xfree(ilist);
return 0;
}
}
if (ilist) {
for (ilp = ilist; ilp < &ilist[ilist_size]; ilp++) {
ino = *ilp;
if ((p = inode_name(ino, &hp))) {
dbprintf("%11llu %s", ino, p);
if (hp->isdir)
dbprintf("/.");
dbprintf("\n");
xfree(p);
}
}
xfree(ilist);
return 0;
}
for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
ht = inodata[agno];
for (i = 0; i < inodata_hash_size; i++) {
hp = ht[i];
for (hp = ht[i]; hp; hp = hp->next) {
ino = XFS_AGINO_TO_INO(mp, agno, hp->ino);
p = inode_name(ino, &id);
if (!p || !id)
continue;
if (!security || id->security) {
dbprintf("%11llu %s", ino, p);
if (hp->isdir)
dbprintf("/.");
dbprintf("\n");
}
xfree(p);
}
}
}
return 0;
}
static char *
prepend_path(
char *oldpath,
char *parent)
{
int len;
char *path;
len = (int)(strlen(oldpath) + strlen(parent) + 2);
path = xmalloc(len);
snprintf(path, len, "%s/%s", parent, oldpath);
return path;
}
static xfs_ino_t
process_block_dir_v2(
blkmap_t *blkmap,
int *dot,
int *dotdot,
inodata_t *id)
{
xfs_fsblock_t b;
bbmap_t bbmap;
bmap_ext_t *bmp;
int nex;
xfs_ino_t parent;
int v;
int i;
nex = blkmap_getn(blkmap, 0, mp->m_dir_geo->fsbcount, &bmp);
v = id->ilist || verbose;
if (nex == 0) {
if (!sflag || v)
dbprintf(_("block 0 for directory inode %lld is "
"missing\n"),
id->ino);
error++;
return 0;
}
push_cur();
if (nex > 1)
make_bbmap(&bbmap, nex, bmp);
set_cur(&typtab[TYP_DIR2], XFS_FSB_TO_DADDR(mp, bmp->startblock),
mp->m_dir_geo->fsbcount * blkbb, DB_RING_IGN, nex > 1 ? &bbmap : NULL);
for (i = 0; !v && i < nex; i++) {
for (b = bmp[i].startblock;
!v && b < bmp[i].startblock + bmp[i].blockcount;
b++)
v = CHECK_BLIST(b);
}
free(bmp);
if (iocur_top->data == NULL) {
if (!sflag || id->ilist || v)
dbprintf(_("can't read block 0 for directory inode "
"%lld\n"),
id->ino);
error++;
pop_cur();
return 0;
}
dir_hash_init();
parent = process_data_dir_v2(dot, dotdot, id, v, mp->m_dir_geo->datablk,
NULL);
dir_hash_check(id, v);
dir_hash_done();
pop_cur();
return parent;
}
static void
process_bmbt_reclist(
xfs_bmbt_rec_t *rp,
int numrecs,
dbm_t type,
inodata_t *id,
xfs_rfsblock_t *tot,
blkmap_t **blkmapp)
{
xfs_agblock_t agbno;
xfs_agnumber_t agno;
xfs_fsblock_t b;
xfs_filblks_t c;
xfs_filblks_t cp;
int f;
int i;
xfs_agblock_t iagbno;
xfs_agnumber_t iagno;
xfs_fileoff_t o;
xfs_fileoff_t op;
xfs_fsblock_t s;
int v;
cp = op = 0;
v = verbose || id->ilist;
iagno = XFS_INO_TO_AGNO(mp, id->ino);
iagbno = XFS_INO_TO_AGBNO(mp, id->ino);
for (i = 0; i < numrecs; i++, rp++) {
convert_extent(rp, &o, &s, &c, &f);
if (v)
dbprintf(_("inode %lld extent [%lld,%lld,%lld,%d]\n"),
id->ino, o, s, c, f);
if (!sflag && i > 0 && op + cp > o)
dbprintf(_("bmap rec out of order, inode %lld entry %d\n"),
id->ino, i);
op = o;
cp = c;
if (type == DBM_RTDATA) {
if (!sflag && s >= mp->m_sb.sb_rblocks) {
dbprintf(_("inode %lld bad rt block number %lld, "
"offset %lld\n"),
id->ino, s, o);
continue;
}
} else if (!sflag) {
agno = XFS_FSB_TO_AGNO(mp, s);
agbno = XFS_FSB_TO_AGBNO(mp, s);
if (agno >= mp->m_sb.sb_agcount ||
agbno >= mp->m_sb.sb_agblocks) {
dbprintf(_("inode %lld bad block number %lld "
"[%d,%d], offset %lld\n"),
id->ino, s, agno, agbno, o);
continue;
}
if (agbno + c - 1 >= mp->m_sb.sb_agblocks) {
dbprintf(_("inode %lld bad block number %lld "
"[%d,%d], offset %lld\n"),
id->ino, s + c - 1, agno,
agbno + (xfs_agblock_t)c - 1, o);
continue;
}
}
if (blkmapp && *blkmapp)
blkmap_set_ext(blkmapp, (xfs_fileoff_t)o,
(xfs_fsblock_t)s, (xfs_extlen_t)c);
if (type == DBM_RTDATA) {
set_rdbmap((xfs_fsblock_t)s, (xfs_extlen_t)c,
DBM_RTDATA);
set_rinomap((xfs_fsblock_t)s, (xfs_extlen_t)c, id);
for (b = (xfs_fsblock_t)s;
blist_size && b < s + c;
b++, o++) {
if (CHECK_BLIST(b))
dbprintf(_("inode %lld block %lld at "
"offset %lld\n"),
id->ino, (xfs_fsblock_t)b, o);
}
} else {
agno = XFS_FSB_TO_AGNO(mp, (xfs_fsblock_t)s);
agbno = XFS_FSB_TO_AGBNO(mp, (xfs_fsblock_t)s);
set_dbmap(agno, agbno, (xfs_extlen_t)c, type, iagno,
iagbno);
set_inomap(agno, agbno, (xfs_extlen_t)c, id);
for (b = (xfs_fsblock_t)s;
blist_size && b < s + c;
b++, o++, agbno++) {
if (CHECK_BLIST(b))
dbprintf(_("inode %lld block %lld at "
"offset %lld\n"),
id->ino, (xfs_fsblock_t)b, o);
}
}
*tot += c;
}
}
static void
process_btinode(
inodata_t *id,
xfs_dinode_t *dip,
dbm_t type,
xfs_rfsblock_t *totd,
xfs_rfsblock_t *toti,
xfs_extnum_t *nex,
blkmap_t **blkmapp,
int whichfork)
{
xfs_bmdr_block_t *dib;
int i;
xfs_bmbt_ptr_t *pp;
dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
if (be16_to_cpu(dib->bb_level) >= XFS_BM_MAXLEVELS(mp, whichfork)) {
if (!sflag || id->ilist)
dbprintf(_("level for ino %lld %s fork bmap root too "
"large (%u)\n"),
id->ino,
whichfork == XFS_DATA_FORK ? _("data") : _("attr"),
be16_to_cpu(dib->bb_level));
error++;
return;
}
if (be16_to_cpu(dib->bb_numrecs) >
libxfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork),
be16_to_cpu(dib->bb_level) == 0)) {
if (!sflag || id->ilist)
dbprintf(_("numrecs for ino %lld %s fork bmap root too "
"large (%u)\n"),
id->ino,
whichfork == XFS_DATA_FORK ? _("data") : _("attr"),
be16_to_cpu(dib->bb_numrecs));
error++;
return;
}
if (be16_to_cpu(dib->bb_level) == 0) {
xfs_bmbt_rec_t *rp = XFS_BMDR_REC_ADDR(dib, 1);
process_bmbt_reclist(rp, be16_to_cpu(dib->bb_numrecs), type,
id, totd, blkmapp);
*nex += be16_to_cpu(dib->bb_numrecs);
return;
} else {
pp = XFS_BMDR_PTR_ADDR(dib, 1, libxfs_bmdr_maxrecs(
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_bmap, type, id, totd, toti,
nex, blkmapp, 1,
whichfork == XFS_DATA_FORK ?
TYP_BMAPBTD : TYP_BMAPBTA);
}
if (*nex <= XFS_DFORK_SIZE(dip, mp, whichfork) / sizeof(xfs_bmbt_rec_t)) {
if (!sflag || id->ilist)
dbprintf(_("extent count for ino %lld %s fork too low "
"(%d) for file format\n"),
id->ino,
whichfork == XFS_DATA_FORK ? _("data") : _("attr"),
*nex);
error++;
}
}
static xfs_ino_t
process_data_dir_v2(
int *dot,
int *dotdot,
inodata_t *id,
int v,
xfs_dablk_t dabno,
freetab_t **freetabp)
{
xfs_dir2_dataptr_t addr;
xfs_dir2_data_free_t *bf;
int bf_err;
struct xfs_dir2_data_hdr *block;
xfs_dir2_block_tail_t *btp = NULL;
inodata_t *cid;
int count;
struct xfs_dir2_data_hdr *data;
xfs_dir2_db_t db;
xfs_dir2_data_entry_t *dep;
xfs_dir2_data_free_t *dfp;
xfs_dir2_data_unused_t *dup;
char *endptr;
int freeseen;
freetab_t *freetab;
int i;
int lastfree;
int lastfree_err;
xfs_dir2_leaf_entry_t *lep = NULL;
xfs_ino_t lino;
xfs_ino_t parent = 0;
char *ptr;
int stale = 0;
int tag_err;
__be16 *tagp;
struct xfs_name xname;
data = iocur_top->data;
block = iocur_top->data;
if (be32_to_cpu(block->magic) != XFS_DIR2_BLOCK_MAGIC &&
be32_to_cpu(data->magic) != XFS_DIR2_DATA_MAGIC &&
be32_to_cpu(block->magic) != XFS_DIR3_BLOCK_MAGIC &&
be32_to_cpu(data->magic) != XFS_DIR3_DATA_MAGIC) {
if (!sflag || v)
dbprintf(_("bad directory data magic # %#x for dir ino "
"%lld block %d\n"),
be32_to_cpu(data->magic), id->ino, dabno);
error++;
return NULLFSINO;
}
db = xfs_dir2_da_to_db(mp->m_dir_geo, dabno);
bf = libxfs_dir2_data_bestfree_p(mp, data);
ptr = (char *)data + mp->m_dir_geo->data_entry_offset;
if (be32_to_cpu(block->magic) == XFS_DIR2_BLOCK_MAGIC ||
be32_to_cpu(block->magic) == XFS_DIR3_BLOCK_MAGIC) {
btp = xfs_dir2_block_tail_p(mp->m_dir_geo, block);
lep = xfs_dir2_block_leaf_p(btp);
endptr = (char *)lep;
if (endptr <= ptr || endptr > (char *)btp) {
endptr = (char *)data + mp->m_dir_geo->blksize;
lep = NULL;
if (!sflag || v)
dbprintf(_("bad block directory tail for dir ino "
"%lld\n"),
id->ino);
error++;
}
} else
endptr = (char *)data + mp->m_dir_geo->blksize;
bf_err = lastfree_err = tag_err = 0;
count = lastfree = freeseen = 0;
if (be16_to_cpu(bf[0].length) == 0) {
bf_err += be16_to_cpu(bf[0].offset) != 0;
freeseen |= 1 << 0;
}
if (be16_to_cpu(bf[1].length) == 0) {
bf_err += be16_to_cpu(bf[1].offset) != 0;
freeseen |= 1 << 1;
}
if (be16_to_cpu(bf[2].length) == 0) {
bf_err += be16_to_cpu(bf[2].offset) != 0;
freeseen |= 1 << 2;
}
bf_err += be16_to_cpu(bf[0].length) < be16_to_cpu(bf[1].length);
bf_err += be16_to_cpu(bf[1].length) < be16_to_cpu(bf[2].length);
if (freetabp) {
freetab = *freetabp;
if (freetab->naents <= db) {
*freetabp = freetab =
realloc(freetab, FREETAB_SIZE(db + 1));
for (i = freetab->naents; i < db; i++)
freetab->ents[i] = NULLDATAOFF;
freetab->naents = db + 1;
}
if (freetab->nents < db + 1)
freetab->nents = db + 1;
freetab->ents[db] = be16_to_cpu(bf[0].length);
}
while (ptr < endptr) {
dup = (xfs_dir2_data_unused_t *)ptr;
if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
lastfree_err += lastfree != 0;
tagp = xfs_dir2_data_unused_tag_p(dup);
if ((be16_to_cpu(dup->length) & (XFS_DIR2_DATA_ALIGN - 1)) ||
be16_to_cpu(dup->length) == 0 ||
(char *)tagp >= endptr) {
if (!sflag || v)
dbprintf(_("dir %lld block %d bad free "
"entry at %d\n"),
id->ino, dabno,
(int)((char *)dup -
(char *)data));
error++;
break;
}
tag_err += be16_to_cpu(*tagp) != (char *)dup - (char *)data;
dfp = process_data_dir_v2_freefind(data, dup);
if (dfp) {
i = (int)(dfp - bf);
bf_err += (freeseen & (1 << i)) != 0;
freeseen |= 1 << i;
} else
bf_err += be16_to_cpu(dup->length) >
be16_to_cpu(bf[2].length);
ptr += be16_to_cpu(dup->length);
lastfree = 1;
continue;
}
dep = (xfs_dir2_data_entry_t *)dup;
if (dep->namelen == 0) {
if (!sflag || v)
dbprintf(_("dir %lld block %d zero length entry "
"at %d\n"),
id->ino, dabno,
(int)((char *)dep - (char *)data));
error++;
}
tagp = libxfs_dir2_data_entry_tag_p(mp, dep);
if ((char *)tagp >= endptr) {
if (!sflag || v)
dbprintf(_("dir %lld block %d bad entry at %d\n"),
id->ino, dabno,
(int)((char *)dep - (char *)data));
error++;
break;
}
tag_err += be16_to_cpu(*tagp) != (char *)dep - (char *)data;
addr = xfs_dir2_db_off_to_dataptr(mp->m_dir_geo, db,
(char *)dep - (char *)data);
xname.name = dep->name;
xname.len = dep->namelen;
dir_hash_add(libxfs_dir2_hashname(mp, &xname), addr);
ptr += libxfs_dir2_data_entsize(mp, dep->namelen);
count++;
lastfree = 0;
lino = be64_to_cpu(dep->inumber);
cid = find_inode(lino, 1);
if (v)
dbprintf(_("dir %lld block %d entry %*.*s %lld\n"),
id->ino, dabno, dep->namelen, dep->namelen,
dep->name, lino);
if (cid)
addlink_inode(cid);
else {
if (!sflag || v)
dbprintf(_("dir %lld block %d entry %*.*s bad "
"inode number %lld\n"),
id->ino, dabno, dep->namelen,
dep->namelen, dep->name, lino);
error++;
}
if (dep->namelen == 2 && dep->name[0] == '.' &&
dep->name[1] == '.') {
if (parent) {
if (!sflag || v)
dbprintf(_("multiple .. entries in dir "
"%lld (%lld, %lld)\n"),
id->ino, parent, lino);
error++;
} else
parent = cid ? lino : NULLFSINO;
(*dotdot)++;
} else if (dep->namelen != 1 || dep->name[0] != '.') {
if (cid != NULL) {
if (!cid->parent)
cid->parent = id;
addname_inode(cid, (char *)dep->name,
dep->namelen);
}
} else {
if (lino != id->ino) {
if (!sflag || v)
dbprintf(_("dir %lld entry . inode "
"number mismatch (%lld)\n"),
id->ino, lino);
error++;
}
(*dot)++;
}
}
if (be32_to_cpu(data->magic) == XFS_DIR2_BLOCK_MAGIC ||
be32_to_cpu(data->magic) == XFS_DIR3_BLOCK_MAGIC) {
endptr = (char *)data + mp->m_dir_geo->blksize;
for (i = stale = 0; lep && i < be32_to_cpu(btp->count); i++) {
if ((char *)&lep[i] >= endptr) {
if (!sflag || v)
dbprintf(_("dir %lld block %d bad count "
"%u\n"), id->ino, dabno,
be32_to_cpu(btp->count));
error++;
break;
}
if (be32_to_cpu(lep[i].address) == XFS_DIR2_NULL_DATAPTR)
stale++;
else if (dir_hash_see(be32_to_cpu(lep[i].hashval),
be32_to_cpu(lep[i].address))) {
if (!sflag || v)
dbprintf(_("dir %lld block %d extra leaf "
"entry %x %x\n"),
id->ino, dabno,
be32_to_cpu(lep[i].hashval),
be32_to_cpu(lep[i].address));
error++;
}
}
}
bf_err += freeseen != 7;
if (bf_err) {
if (!sflag || v)
dbprintf(_("dir %lld block %d bad bestfree data\n"),
id->ino, dabno);
error++;
}
if ((be32_to_cpu(data->magic) == XFS_DIR2_BLOCK_MAGIC ||
be32_to_cpu(data->magic) == XFS_DIR3_BLOCK_MAGIC) &&
count != be32_to_cpu(btp->count) - be32_to_cpu(btp->stale)) {
if (!sflag || v)
dbprintf(_("dir %lld block %d bad block tail count %d "
"(stale %d)\n"),
id->ino, dabno, be32_to_cpu(btp->count),
be32_to_cpu(btp->stale));
error++;
}
if ((be32_to_cpu(data->magic) == XFS_DIR2_BLOCK_MAGIC ||
be32_to_cpu(data->magic) == XFS_DIR2_BLOCK_MAGIC) &&
stale != be32_to_cpu(btp->stale)) {
if (!sflag || v)
dbprintf(_("dir %lld block %d bad stale tail count %d\n"),
id->ino, dabno, be32_to_cpu(btp->stale));
error++;
}
if (lastfree_err) {
if (!sflag || v)
dbprintf(_("dir %lld block %d consecutive free entries\n"),
id->ino, dabno);
error++;
}
if (tag_err) {
if (!sflag || v)
dbprintf(_("dir %lld block %d entry/unused tag "
"mismatch\n"),
id->ino, dabno);
error++;
}
return parent;
}
static xfs_dir2_data_free_t *
process_data_dir_v2_freefind(
struct xfs_dir2_data_hdr *data,
xfs_dir2_data_unused_t *dup)
{
struct xfs_dir2_data_free *bf;
struct xfs_dir2_data_free *dfp;
xfs_dir2_data_aoff_t off;
off = (xfs_dir2_data_aoff_t)((char *)dup - (char *)data);
bf = libxfs_dir2_data_bestfree_p(mp, data);
if (be16_to_cpu(dup->length) <
be16_to_cpu(bf[XFS_DIR2_DATA_FD_COUNT - 1].length))
return NULL;
for (dfp = bf; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
if (be16_to_cpu(dfp->offset) == 0)
return NULL;
if (be16_to_cpu(dfp->offset) == off)
return dfp;
}
return NULL;
}
static void
process_dir(
xfs_dinode_t *dip,
blkmap_t *blkmap,
inodata_t *id)
{
xfs_fsblock_t bno;
int dot;
int dotdot;
xfs_ino_t parent;
dot = dotdot = 0;
if (process_dir_v2(dip, blkmap, &dot, &dotdot, id, &parent))
return;
bno = XFS_INO_TO_FSB(mp, id->ino);
if (dot == 0) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("no . entry for directory %lld\n"), id->ino);
error++;
}
if (dotdot == 0) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("no .. entry for directory %lld\n"), id->ino);
error++;
} else if (parent == id->ino && id->ino != mp->m_sb.sb_rootino) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_(". and .. same for non-root directory %lld\n"),
id->ino);
error++;
} else if (id->ino == mp->m_sb.sb_rootino && id->ino != parent) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("root directory %lld has .. %lld\n"), id->ino,
parent);
error++;
} else if (parent != NULLFSINO && id->ino != parent)
addparent_inode(id, parent);
}
static int
process_dir_v2(
xfs_dinode_t *dip,
blkmap_t *blkmap,
int *dot,
int *dotdot,
inodata_t *id,
xfs_ino_t *parent)
{
xfs_fileoff_t last = 0;
xfs_fsize_t size = be64_to_cpu(dip->di_size);
if (blkmap)
last = blkmap_last_off(blkmap);
if (size <= XFS_DFORK_DSIZE(dip, mp) &&
dip->di_format == XFS_DINODE_FMT_LOCAL)
*parent = process_sf_dir_v2(dip, dot, dotdot, id);
else if (last == mp->m_dir_geo->fsbcount &&
(dip->di_format == XFS_DINODE_FMT_EXTENTS ||
dip->di_format == XFS_DINODE_FMT_BTREE))
*parent = process_block_dir_v2(blkmap, dot, dotdot, id);
else if (last >= mp->m_dir_geo->leafblk + mp->m_dir_geo->fsbcount &&
(dip->di_format == XFS_DINODE_FMT_EXTENTS ||
dip->di_format == XFS_DINODE_FMT_BTREE))
*parent = process_leaf_node_dir_v2(blkmap, dot, dotdot, id, size);
else {
dbprintf(_("bad size (%lld) or format (%d) for directory inode "
"%lld\n"),
size, dip->di_format, id->ino);
error++;
return 1;
}
return 0;
}
/* ARGSUSED */
static void
process_exinode(
inodata_t *id,
xfs_dinode_t *dip,
dbm_t type,
xfs_rfsblock_t *totd,
xfs_rfsblock_t *toti,
xfs_extnum_t *nex,
blkmap_t **blkmapp,
int whichfork)
{
xfs_bmbt_rec_t *rp;
rp = (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork);
*nex = XFS_DFORK_NEXTENTS(dip, whichfork);
if (*nex < 0 || *nex > XFS_DFORK_SIZE(dip, mp, whichfork) /
sizeof(xfs_bmbt_rec_t)) {
if (!sflag || id->ilist)
dbprintf(_("bad number of extents %d for inode %lld\n"),
*nex, id->ino);
error++;
return;
}
process_bmbt_reclist(rp, *nex, type, id, totd, blkmapp);
}
static void
process_inode(
xfs_agf_t *agf,
xfs_agino_t agino,
xfs_dinode_t *dip,
int isfree)
{
blkmap_t *blkmap;
xfs_fsblock_t bno = 0;
struct xfs_inode xino;
inodata_t *id = NULL;
xfs_ino_t ino;
xfs_extnum_t nextents = 0;
int security;
xfs_rfsblock_t totblocks;
xfs_rfsblock_t totdblocks = 0;
xfs_rfsblock_t totiblocks = 0;
dbm_t type;
xfs_extnum_t anextents = 0;
xfs_rfsblock_t atotdblocks = 0;
xfs_rfsblock_t atotiblocks = 0;
xfs_qcnt_t bc = 0;
xfs_qcnt_t ic = 0;
xfs_qcnt_t rc = 0;
int v = 0;
mode_t mode;
static char okfmts[] = {
0, /* type 0 unused */
1 << XFS_DINODE_FMT_DEV, /* FIFO */
1 << XFS_DINODE_FMT_DEV, /* CHR */
0, /* type 3 unused */
(1 << XFS_DINODE_FMT_LOCAL) |
(1 << XFS_DINODE_FMT_EXTENTS) |
(1 << XFS_DINODE_FMT_BTREE), /* DIR */
0, /* type 5 unused */
1 << XFS_DINODE_FMT_DEV, /* BLK */
0, /* type 7 unused */
(1 << XFS_DINODE_FMT_EXTENTS) |
(1 << XFS_DINODE_FMT_BTREE), /* REG */
0, /* type 9 unused */
(1 << XFS_DINODE_FMT_LOCAL) |
(1 << XFS_DINODE_FMT_EXTENTS), /* LNK */
0, /* type 11 unused */
1 << XFS_DINODE_FMT_DEV, /* SOCK */
0, /* type 13 unused */
1 << XFS_DINODE_FMT_UUID, /* MNT */
0 /* type 15 unused */
};
static char *fmtnames[] = {
"dev", "local", "extents", "btree", "uuid"
};
libxfs_inode_from_disk(&xino, dip);
ino = XFS_AGINO_TO_INO(mp, be32_to_cpu(agf->agf_seqno), agino);
if (!isfree) {
id = find_inode(ino, 1);
bno = XFS_INO_TO_FSB(mp, ino);
blkmap = NULL;
}
v = (!sflag || (id && id->ilist) || CHECK_BLIST(bno));
if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC)) {
if (isfree || v)
dbprintf(_("bad magic number %#x for inode %lld\n"),
be16_to_cpu(dip->di_magic), ino);
error++;
return;
}
if (!libxfs_dinode_good_version(mp, xino.i_d.di_version)) {
if (isfree || v)
dbprintf(_("bad version number %#x for inode %lld\n"),
xino.i_d.di_version, ino);
error++;
return;
}
if (isfree) {
if (xino.i_d.di_nblocks != 0) {
if (v)
dbprintf(_("bad nblocks %lld for free inode "
"%lld\n"),
xino.i_d.di_nblocks, ino);
error++;
}
if (dip->di_nlink != 0) {
if (v)
dbprintf(_("bad nlink %d for free inode %lld\n"),
be32_to_cpu(dip->di_nlink), ino);
error++;
}
if (dip->di_mode != 0) {
if (v)
dbprintf(_("bad mode %#o for free inode %lld\n"),
be16_to_cpu(dip->di_mode), ino);
error++;
}
return;
}
if (be32_to_cpu(dip->di_next_unlinked) != NULLAGINO) {
if (v)
dbprintf(_("bad next unlinked %#x for inode %lld\n"),
be32_to_cpu(dip->di_next_unlinked), ino);
error++;
}
/*
* di_mode is a 16-bit uint so no need to check the < 0 case
*/
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 (v)
dbprintf(_("bad format %d for inode %lld type %#o\n"),
xino.i_d.di_format, id->ino, mode & S_IFMT);
error++;
return;
}
if ((unsigned int)XFS_DFORK_ASIZE(dip, mp) >=
XFS_LITINO(mp, xino.i_d.di_version)) {
if (v)
dbprintf(_("bad fork offset %d for inode %lld\n"),
xino.i_d.di_forkoff, id->ino);
error++;
return;
}
if ((unsigned int)xino.i_d.di_aformat > XFS_DINODE_FMT_BTREE) {
if (v)
dbprintf(_("bad attribute format %d for inode %lld\n"),
xino.i_d.di_aformat, id->ino);
error++;
return;
}
if (verbose || (id && id->ilist) || CHECK_BLIST(bno))
dbprintf(_("inode %lld mode %#o fmt %s "
"afmt %s "
"nex %d anex %d nblk %lld sz %lld%s%s%s%s%s%s%s\n"),
id->ino, mode, fmtnames[(int)xino.i_d.di_format],
fmtnames[(int)xino.i_d.di_aformat],
xino.i_d.di_nextents,
xino.i_d.di_anextents,
xino.i_d.di_nblocks, xino.i_d.di_size,
xino.i_d.di_flags & XFS_DIFLAG_REALTIME ? " rt" : "",
xino.i_d.di_flags & XFS_DIFLAG_PREALLOC ? " pre" : "",
xino.i_d.di_flags & XFS_DIFLAG_IMMUTABLE? " imm" : "",
xino.i_d.di_flags & XFS_DIFLAG_APPEND ? " app" : "",
xino.i_d.di_flags & XFS_DIFLAG_SYNC ? " syn" : "",
xino.i_d.di_flags & XFS_DIFLAG_NOATIME ? " noa" : "",
xino.i_d.di_flags & XFS_DIFLAG_NODUMP ? " nod" : "");
security = 0;
switch (mode & S_IFMT) {
case S_IFDIR:
type = DBM_DIR;
if (xino.i_d.di_format == XFS_DINODE_FMT_LOCAL)
break;
blkmap = blkmap_alloc(xino.i_d.di_nextents);
break;
case S_IFREG:
if (xino.i_d.di_flags & XFS_DIFLAG_REALTIME)
type = DBM_RTDATA;
else if (id->ino == mp->m_sb.sb_rbmino) {
type = DBM_RTBITMAP;
blkmap = blkmap_alloc(xino.i_d.di_nextents);
addlink_inode(id);
} else if (id->ino == mp->m_sb.sb_rsumino) {
type = DBM_RTSUM;
blkmap = blkmap_alloc(xino.i_d.di_nextents);
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;
blkmap = blkmap_alloc(xino.i_d.di_nextents);
addlink_inode(id);
}
else
type = DBM_DATA;
if (mode & (S_ISUID | S_ISGID))
security = 1;
break;
case S_IFLNK:
type = DBM_SYMLINK;
break;
default:
security = 1;
type = DBM_UNKNOWN;
break;
}
id->isreflink = !!(xino.i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
setlink_inode(id, VFS_I(&xino)->i_nlink, type == DBM_DIR, security);
switch (xino.i_d.di_format) {
case XFS_DINODE_FMT_LOCAL:
process_lclinode(id, dip, type, &totdblocks, &totiblocks,
&nextents, &blkmap, XFS_DATA_FORK);
break;
case XFS_DINODE_FMT_EXTENTS:
process_exinode(id, dip, type, &totdblocks, &totiblocks,
&nextents, &blkmap, XFS_DATA_FORK);
break;
case XFS_DINODE_FMT_BTREE:
process_btinode(id, dip, type, &totdblocks, &totiblocks,
&nextents, &blkmap, XFS_DATA_FORK);
break;
}
if (XFS_DFORK_Q(dip)) {
sbversion |= XFS_SB_VERSION_ATTRBIT;
switch (xino.i_d.di_aformat) {
case XFS_DINODE_FMT_LOCAL:
process_lclinode(id, dip, DBM_ATTR, &atotdblocks,
&atotiblocks, &anextents, NULL, XFS_ATTR_FORK);
break;
case XFS_DINODE_FMT_EXTENTS:
process_exinode(id, dip, DBM_ATTR, &atotdblocks,
&atotiblocks, &anextents, NULL, XFS_ATTR_FORK);
break;
case XFS_DINODE_FMT_BTREE:
process_btinode(id, dip, DBM_ATTR, &atotdblocks,
&atotiblocks, &anextents, NULL, XFS_ATTR_FORK);
break;
}
}
if (qgdo || qpdo || qudo) {
switch (type) {
case DBM_DATA:
case DBM_DIR:
case DBM_RTBITMAP:
case DBM_RTSUM:
case DBM_SYMLINK:
case DBM_UNKNOWN:
bc = totdblocks + totiblocks +
atotdblocks + atotiblocks;
ic = 1;
break;
case DBM_RTDATA:
bc = totiblocks + atotdblocks + atotiblocks;
rc = totdblocks;
ic = 1;
break;
default:
break;
}
if (ic) {
quota_add(&xino.i_d.di_projid, &xino.i_d.di_gid,
&xino.i_d.di_uid, 0, bc, ic, rc);
}
}
totblocks = totdblocks + totiblocks + atotdblocks + atotiblocks;
if (totblocks != xino.i_d.di_nblocks) {
if (v)
dbprintf(_("bad nblocks %lld for inode %lld, counted "
"%lld\n"),
xino.i_d.di_nblocks, id->ino, totblocks);
error++;
}
if (nextents != xino.i_d.di_nextents) {
if (v)
dbprintf(_("bad nextents %d for inode %lld, counted %d\n"),
xino.i_d.di_nextents, id->ino, nextents);
error++;
}
if (anextents != xino.i_d.di_anextents) {
if (v)
dbprintf(_("bad anextents %d for inode %lld, counted "
"%d\n"),
xino.i_d.di_anextents, id->ino, anextents);
error++;
}
if (type == DBM_DIR)
process_dir(dip, blkmap, id);
else if (type == DBM_RTBITMAP)
process_rtbitmap(blkmap);
else if (type == DBM_RTSUM)
process_rtsummary(blkmap);
/*
* If the CHKD flag is not set, this can legitimately contain garbage;
* xfs_repair may have cleared that bit.
*/
else if (type == DBM_QUOTA) {
if (id->ino == mp->m_sb.sb_uquotino &&
(mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) &&
(mp->m_sb.sb_qflags & XFS_UQUOTA_CHKD))
process_quota(IS_USER_QUOTA, id, blkmap);
else if (id->ino == mp->m_sb.sb_gquotino &&
(mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) &&
(mp->m_sb.sb_qflags & XFS_GQUOTA_CHKD))
process_quota(IS_GROUP_QUOTA, id, blkmap);
else if (id->ino == mp->m_sb.sb_pquotino &&
(mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) &&
(mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD))
process_quota(IS_PROJECT_QUOTA, id, blkmap);
}
if (blkmap)
blkmap_free(blkmap);
}
/* ARGSUSED */
static void
process_lclinode(
inodata_t *id,
xfs_dinode_t *dip,
dbm_t type,
xfs_rfsblock_t *totd,
xfs_rfsblock_t *toti,
xfs_extnum_t *nex,
blkmap_t **blkmapp,
int whichfork)
{
xfs_attr_shortform_t *asf;
xfs_fsblock_t bno;
bno = XFS_INO_TO_FSB(mp, id->ino);
if (whichfork == XFS_DATA_FORK && be64_to_cpu(dip->di_size) >
XFS_DFORK_DSIZE(dip, mp)) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("local inode %lld data is too large (size "
"%lld)\n"),
id->ino, be64_to_cpu(dip->di_size));
error++;
}
else if (whichfork == XFS_ATTR_FORK) {
asf = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
if (be16_to_cpu(asf->hdr.totsize) > XFS_DFORK_ASIZE(dip, mp)) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("local inode %lld attr is too large "
"(size %d)\n"),
id->ino, be16_to_cpu(asf->hdr.totsize));
error++;
}
}
}
static xfs_ino_t
process_leaf_node_dir_v2(
blkmap_t *blkmap,
int *dot,
int *dotdot,
inodata_t *id,
xfs_fsize_t dirsize)
{
xfs_fsblock_t b;
bbmap_t bbmap;
bmap_ext_t *bmp;
xfs_fileoff_t dbno;
freetab_t *freetab;
int i;
xfs_ino_t lino;
int nex;
xfs_ino_t parent;
int t = 0;
int v;
int v2;
v2 = verbose || id->ilist;
v = parent = 0;
dbno = NULLFILEOFF;
freetab = malloc(FREETAB_SIZE(dirsize / mp->m_dir_geo->blksize));
freetab->naents = (int)(dirsize / mp->m_dir_geo->blksize);
freetab->nents = 0;
for (i = 0; i < freetab->naents; i++)
freetab->ents[i] = NULLDATAOFF;
dir_hash_init();
while ((dbno = blkmap_next_off(blkmap, dbno, &t)) != NULLFILEOFF) {
nex = blkmap_getn(blkmap, dbno, mp->m_dir_geo->fsbcount, &bmp);
ASSERT(nex > 0);
for (v = v2, i = 0; !v && i < nex; i++) {
for (b = bmp[i].startblock;
!v && b < bmp[i].startblock + bmp[i].blockcount;
b++)
v = CHECK_BLIST(b);
}
if (v)
dbprintf(_("dir inode %lld block %u=%llu\n"), id->ino,
(uint32_t)dbno,
(xfs_fsblock_t)bmp->startblock);
push_cur();
if (nex > 1)
make_bbmap(&bbmap, nex, bmp);
set_cur(&typtab[TYP_DIR2], XFS_FSB_TO_DADDR(mp, bmp->startblock),
mp->m_dir_geo->fsbcount * blkbb, DB_RING_IGN,
nex > 1 ? &bbmap : NULL);
free(bmp);
if (iocur_top->data == NULL) {
if (!sflag || v)
dbprintf(_("can't read block %u for directory "
"inode %lld\n"),
(uint32_t)dbno, id->ino);
error++;
pop_cur();
dbno += mp->m_dir_geo->fsbcount - 1;
continue;
}
if (dbno < mp->m_dir_geo->leafblk) {
lino = process_data_dir_v2(dot, dotdot, id, v,
(xfs_dablk_t)dbno, &freetab);
if (lino) {
if (parent) {
if (!sflag || v)
dbprintf(_("multiple .. entries "
"in dir %lld\n"),
id->ino);
error++;
} else
parent = lino;
}
} else if (dbno < mp->m_dir_geo->freeblk) {
process_leaf_node_dir_v2_int(id, v, (xfs_dablk_t)dbno,
freetab);
} else {
process_leaf_node_dir_v2_free(id, v, (xfs_dablk_t)dbno,
freetab);
}
pop_cur();
dbno += mp->m_dir_geo->fsbcount - 1;
}
dir_hash_check(id, v);
dir_hash_done();
for (i = 0; i < freetab->nents; i++) {
if (freetab->ents[i] != NULLDATAOFF) {
if (!sflag || v)
dbprintf(_("missing free index for data block %d "
"in dir ino %lld\n"),
xfs_dir2_db_to_da(mp->m_dir_geo, i), id->ino);
error++;
}
}
free(freetab);
return parent;
}
static void
process_leaf_node_dir_v3_free(
inodata_t *id,
int v,
xfs_dablk_t dabno,
freetab_t *freetab)
{
xfs_dir2_data_off_t ent;
struct xfs_dir3_free *free;
int i;
int maxent;
int used;
free = iocur_top->data;
maxent = mp->m_dir_geo->free_max_bests;
if (be32_to_cpu(free->hdr.firstdb) != xfs_dir2_da_to_db(mp->m_dir_geo,
dabno - mp->m_dir_geo->freeblk) * maxent) {
if (!sflag || v)
dbprintf(_("bad free block firstdb %d for dir ino %lld "
"block %d\n"),
be32_to_cpu(free->hdr.firstdb), id->ino, dabno);
error++;
return;
}
if (be32_to_cpu(free->hdr.nvalid) > maxent ||
be32_to_cpu(free->hdr.nused) > maxent ||
be32_to_cpu(free->hdr.nused) >
be32_to_cpu(free->hdr.nvalid)) {
if (!sflag || v)
dbprintf(_("bad free block nvalid/nused %d/%d for dir "
"ino %lld block %d\n"),
be32_to_cpu(free->hdr.nvalid),
be32_to_cpu(free->hdr.nused), id->ino, dabno);
error++;
return;
}
for (used = i = 0; i < be32_to_cpu(free->hdr.nvalid); i++) {
if (freetab->nents <= be32_to_cpu(free->hdr.firstdb) + i)
ent = NULLDATAOFF;
else
ent = freetab->ents[be32_to_cpu(free->hdr.firstdb) + i];
if (ent != be16_to_cpu(free->bests[i])) {
if (!sflag || v)
dbprintf(_("bad free block ent %d is %d should "
"be %d for dir ino %lld block %d\n"),
i, be16_to_cpu(free->bests[i]), ent,
id->ino, dabno);
error++;
}
if (be16_to_cpu(free->bests[i]) != NULLDATAOFF)
used++;
if (ent != NULLDATAOFF)
freetab->ents[be32_to_cpu(free->hdr.firstdb) + i] =
NULLDATAOFF;
}
if (used != be32_to_cpu(free->hdr.nused)) {
if (!sflag || v)
dbprintf(_("bad free block nused %d should be %d for dir "
"ino %lld block %d\n"),
be32_to_cpu(free->hdr.nused), used, id->ino,
dabno);
error++;
}
}
static void
process_leaf_node_dir_v2_free(
inodata_t *id,
int v,
xfs_dablk_t dabno,
freetab_t *freetab)
{
xfs_dir2_data_off_t ent;
xfs_dir2_free_t *free;
int i;
int maxent;
int used;
free = iocur_top->data;
if (be32_to_cpu(free->hdr.magic) != XFS_DIR2_FREE_MAGIC &&
be32_to_cpu(free->hdr.magic) != XFS_DIR3_FREE_MAGIC) {
if (!sflag || v)
dbprintf(_("bad free block magic # %#x for dir ino %lld "
"block %d\n"),
be32_to_cpu(free->hdr.magic), id->ino, dabno);
error++;
return;
}
if (be32_to_cpu(free->hdr.magic) == XFS_DIR3_FREE_MAGIC) {
process_leaf_node_dir_v3_free(id, v, dabno, freetab);
return;
}
maxent = mp->m_dir_geo->free_max_bests;
if (be32_to_cpu(free->hdr.firstdb) != xfs_dir2_da_to_db(mp->m_dir_geo,
dabno - mp->m_dir_geo->freeblk) * maxent) {
if (!sflag || v)
dbprintf(_("bad free block firstdb %d for dir ino %lld "
"block %d\n"),
be32_to_cpu(free->hdr.firstdb), id->ino, dabno);
error++;
return;
}
if (be32_to_cpu(free->hdr.nvalid) > maxent ||
be32_to_cpu(free->hdr.nused) > maxent ||
be32_to_cpu(free->hdr.nused) >
be32_to_cpu(free->hdr.nvalid)) {
if (!sflag || v)
dbprintf(_("bad free block nvalid/nused %d/%d for dir "
"ino %lld block %d\n"),
be32_to_cpu(free->hdr.nvalid),
be32_to_cpu(free->hdr.nused), id->ino, dabno);
error++;
return;
}
for (used = i = 0; i < be32_to_cpu(free->hdr.nvalid); i++) {
if (freetab->nents <= be32_to_cpu(free->hdr.firstdb) + i)
ent = NULLDATAOFF;
else
ent = freetab->ents[be32_to_cpu(free->hdr.firstdb) + i];
if (ent != be16_to_cpu(free->bests[i])) {
if (!sflag || v)
dbprintf(_("bad free block ent %d is %d should "
"be %d for dir ino %lld block %d\n"),
i, be16_to_cpu(free->bests[i]), ent,
id->ino, dabno);
error++;
}
if (be16_to_cpu(free->bests[i]) != NULLDATAOFF)
used++;
if (ent != NULLDATAOFF)
freetab->ents[be32_to_cpu(free->hdr.firstdb) + i] =
NULLDATAOFF;
}
if (used != be32_to_cpu(free->hdr.nused)) {
if (!sflag || v)
dbprintf(_("bad free block nused %d should be %d for dir "
"ino %lld block %d\n"),
be32_to_cpu(free->hdr.nused), used, id->ino,
dabno);
error++;
}
}
/*
* Get address of the bestcount field in the single-leaf block.
*/
static inline int
xfs_dir3_leaf_ents_count(struct xfs_dir2_leaf *lp)
{
if (lp->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAF1_MAGIC) ||
lp->hdr.info.magic == cpu_to_be16(XFS_DIR3_LEAFN_MAGIC)) {
struct xfs_dir3_leaf *lp3 = (struct xfs_dir3_leaf *)lp;
return be16_to_cpu(lp3->hdr.count);
}
return be16_to_cpu(lp->hdr.count);
}
static void
process_leaf_node_dir_v2_int(
inodata_t *id,
int v,
xfs_dablk_t dabno,
freetab_t *freetab)
{
int i;
__be16 *lbp;
xfs_dir2_leaf_t *leaf;
struct xfs_dir3_leaf *leaf3 = NULL;
xfs_dir2_leaf_entry_t *lep;
xfs_dir2_leaf_tail_t *ltp;
xfs_da_intnode_t *node;
int stale;
struct xfs_da3_icnode_hdr nodehdr;
struct xfs_dir3_icleaf_hdr leafhdr;
leaf = iocur_top->data;
libxfs_dir2_leaf_hdr_from_disk(mp, &leafhdr, leaf);
switch (be16_to_cpu(leaf->hdr.info.magic)) {
case XFS_DIR3_LEAF1_MAGIC:
case XFS_DIR3_LEAFN_MAGIC:
case XFS_DA3_NODE_MAGIC:
leaf3 = iocur_top->data;
break;
}
switch (be16_to_cpu(leaf->hdr.info.magic)) {
case XFS_DIR2_LEAF1_MAGIC:
case XFS_DIR3_LEAF1_MAGIC:
if (be32_to_cpu(leaf->hdr.info.forw) ||
be32_to_cpu(leaf->hdr.info.back)) {
if (!sflag || v)
dbprintf(_("bad leaf block forw/back pointers "
"%d/%d for dir ino %lld block %d\n"),
be32_to_cpu(leaf->hdr.info.forw),
be32_to_cpu(leaf->hdr.info.back),
id->ino, dabno);
error++;
}
if (dabno != mp->m_dir_geo->leafblk) {
if (!sflag || v)
dbprintf(_("single leaf block for dir ino %lld "
"block %d should be at block %d\n"),
id->ino, dabno,
(xfs_dablk_t)mp->m_dir_geo->leafblk);
error++;
}
ltp = xfs_dir2_leaf_tail_p(mp->m_dir_geo, leaf);
lbp = xfs_dir2_leaf_bests_p(ltp);
for (i = 0; i < be32_to_cpu(ltp->bestcount); i++) {
if (freetab->nents <= i || freetab->ents[i] !=
be16_to_cpu(lbp[i])) {
if (!sflag || v)
dbprintf(_("bestfree %d for dir ino %lld "
"block %d doesn't match table "
"value %d\n"),
freetab->nents <= i ?
NULLDATAOFF :
freetab->ents[i],
id->ino,
xfs_dir2_db_to_da(mp->m_dir_geo, i),
be16_to_cpu(lbp[i]));
}
if (freetab->nents > i)
freetab->ents[i] = NULLDATAOFF;
}
break;
case XFS_DIR2_LEAFN_MAGIC:
case XFS_DIR3_LEAFN_MAGIC:
/* if it's at the root location then we can check the
* pointers are null XXX */
break;
case XFS_DA_NODE_MAGIC:
case XFS_DA3_NODE_MAGIC:
node = iocur_top->data;
libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
if (nodehdr.level < 1 || nodehdr.level > XFS_DA_NODE_MAXDEPTH) {
if (!sflag || v)
dbprintf(_("bad node block level %d for dir ino "
"%lld block %d\n"),
nodehdr.level, id->ino,
dabno);
error++;
}
return;
default:
if (!sflag || v)
dbprintf(_("bad directory data magic # %#x for dir ino "
"%lld block %d\n"),
be16_to_cpu(leaf->hdr.info.magic), id->ino,
dabno);
error++;
return;
}
lep = leafhdr.ents;
for (i = stale = 0; i < xfs_dir3_leaf_ents_count(leaf); i++) {
if (be32_to_cpu(lep[i].address) == XFS_DIR2_NULL_DATAPTR)
stale++;
else if (dir_hash_see(be32_to_cpu(lep[i].hashval),
be32_to_cpu(lep[i].address))) {
if (!sflag || v)
dbprintf(_("dir %lld block %d extra leaf entry "
"%x %x\n"), id->ino, dabno,
be32_to_cpu(lep[i].hashval),
be32_to_cpu(lep[i].address));
error++;
}
}
if (leaf3 && stale != be16_to_cpu(leaf3->hdr.stale)) {
if (!sflag || v)
dbprintf(_("dir3 %lld block %d stale mismatch "
"%d/%d\n"),
id->ino, dabno, stale,
be16_to_cpu(leaf3->hdr.stale));
error++;
} else if (!leaf && stale != be16_to_cpu(leaf->hdr.stale)) {
if (!sflag || v)
dbprintf(_("dir %lld block %d stale mismatch "
"%d/%d\n"),
id->ino, dabno, stale,
be16_to_cpu(leaf->hdr.stale));
error++;
}
}
static void
process_quota(
qtype_t qtype,
inodata_t *id,
blkmap_t *blkmap)
{
xfs_fsblock_t bno;
int cb;
xfs_dqblk_t *dqb;
xfs_dqid_t dqid;
uint8_t exp_flags = 0;
uint i;
uint perblock;
xfs_fileoff_t qbno;
char *s = NULL;
int scicb;
int t = 0;
switch (qtype) {
case IS_USER_QUOTA:
s = "user";
exp_flags = XFS_DQ_USER;
break;
case IS_PROJECT_QUOTA:
s = "project";
exp_flags = XFS_DQ_PROJ;
break;
case IS_GROUP_QUOTA:
s = "group";
exp_flags = XFS_DQ_GROUP;
break;
default:
ASSERT(0);
}
perblock = (uint)(mp->m_sb.sb_blocksize / sizeof(*dqb));
dqid = 0;
qbno = NULLFILEOFF;
while ((qbno = blkmap_next_off(blkmap, qbno, &t)) != NULLFILEOFF) {
bno = blkmap_get(blkmap, qbno);
dqid = (xfs_dqid_t)qbno * perblock;
cb = CHECK_BLIST(bno);
scicb = !sflag || id->ilist || cb;
push_cur();
set_cur(&typtab[TYP_DQBLK], XFS_FSB_TO_DADDR(mp, bno), blkbb,
DB_RING_IGN, NULL);
if ((dqb = iocur_top->data) == NULL) {
if (scicb)
dbprintf(_("can't read block %lld for %s quota "
"inode (fsblock %lld)\n"),
(xfs_fileoff_t)qbno, s,
(xfs_fsblock_t)bno);
error++;
pop_cur();
continue;
}
for (i = 0; i < perblock; i++, dqid++, dqb++) {
if (verbose || id->ilist || cb)
dbprintf(_("%s dqblk %lld entry %d id %u bc "
"%lld ic %lld rc %lld\n"),
s, (xfs_fileoff_t)qbno, i, dqid,
be64_to_cpu(dqb->dd_diskdq.d_bcount),
be64_to_cpu(dqb->dd_diskdq.d_icount),
be64_to_cpu(dqb->dd_diskdq.d_rtbcount));
if (be16_to_cpu(dqb->dd_diskdq.d_magic) != XFS_DQUOT_MAGIC) {
if (scicb)
dbprintf(_("bad magic number %#x for %s "
"dqblk %lld entry %d id %u\n"),
be16_to_cpu(dqb->dd_diskdq.d_magic), s,
(xfs_fileoff_t)qbno, i, dqid);
error++;
continue;
}
if (dqb->dd_diskdq.d_version != XFS_DQUOT_VERSION) {
if (scicb)
dbprintf(_("bad version number %#x for "
"%s dqblk %lld entry %d id "
"%u\n"),
dqb->dd_diskdq.d_version, s,
(xfs_fileoff_t)qbno, i, dqid);
error++;
continue;
}
if (dqb->dd_diskdq.d_flags != exp_flags) {
if (scicb)
dbprintf(_("bad flags %#x for %s dqblk "
"%lld entry %d id %u\n"),
dqb->dd_diskdq.d_flags, s,
(xfs_fileoff_t)qbno, i, dqid);
error++;
continue;
}
if (be32_to_cpu(dqb->dd_diskdq.d_id) != dqid) {
if (scicb)
dbprintf(_("bad id %u for %s dqblk %lld "
"entry %d id %u\n"),
be32_to_cpu(dqb->dd_diskdq.d_id), s,
(xfs_fileoff_t)qbno, i, dqid);
error++;
continue;
}
quota_add((qtype == IS_PROJECT_QUOTA) ? &dqid : NULL,
(qtype == IS_GROUP_QUOTA) ? &dqid : NULL,
(qtype == IS_USER_QUOTA) ? &dqid : NULL,
1,
be64_to_cpu(dqb->dd_diskdq.d_bcount),
be64_to_cpu(dqb->dd_diskdq.d_icount),
be64_to_cpu(dqb->dd_diskdq.d_rtbcount));
}
pop_cur();
}
}
static void
process_rtbitmap(
blkmap_t *blkmap)
{
int bit;
int bitsperblock;
xfs_fileoff_t bmbno;
xfs_fsblock_t bno;
xfs_rtblock_t extno;
int len;
int log;
int offs;
int prevbit;
xfs_rfsblock_t rtbno;
int start_bmbno;
int start_bit;
int t;
xfs_rtword_t *words;
bitsperblock = mp->m_sb.sb_blocksize * NBBY;
bit = extno = prevbit = start_bmbno = start_bit = 0;
bmbno = NULLFILEOFF;
while ((bmbno = blkmap_next_off(blkmap, bmbno, &t)) !=
NULLFILEOFF) {
bno = blkmap_get(blkmap, bmbno);
if (bno == NULLFSBLOCK) {
if (!sflag)
dbprintf(_("block %lld for rtbitmap inode is "
"missing\n"),
(xfs_fileoff_t)bmbno);
error++;
continue;
}
push_cur();
set_cur(&typtab[TYP_RTBITMAP], XFS_FSB_TO_DADDR(mp, bno), blkbb,
DB_RING_IGN, NULL);
if ((words = iocur_top->data) == NULL) {
if (!sflag)
dbprintf(_("can't read block %lld for rtbitmap "
"inode\n"),
(xfs_fileoff_t)bmbno);
error++;
pop_cur();
continue;
}
for (bit = 0;
bit < bitsperblock && extno < mp->m_sb.sb_rextents;
bit++, extno++) {
if (xfs_isset(words, bit)) {
rtbno = extno * mp->m_sb.sb_rextsize;
set_rdbmap(rtbno, mp->m_sb.sb_rextsize,
DBM_RTFREE);
frextents++;
if (prevbit == 0) {
start_bmbno = (int)bmbno;
start_bit = bit;
prevbit = 1;
}
} else if (prevbit == 1) {
len = ((int)bmbno - start_bmbno) *
bitsperblock + (bit - start_bit);
log = XFS_RTBLOCKLOG(len);
offs = XFS_SUMOFFS(mp, log, start_bmbno);
sumcompute[offs]++;
prevbit = 0;
}
}
pop_cur();
if (extno == mp->m_sb.sb_rextents)
break;
}
if (prevbit == 1) {
len = ((int)bmbno - start_bmbno) * bitsperblock +
(bit - start_bit);
log = XFS_RTBLOCKLOG(len);
offs = XFS_SUMOFFS(mp, log, start_bmbno);
sumcompute[offs]++;
}
}
static void
process_rtsummary(
blkmap_t *blkmap)
{
xfs_fsblock_t bno;
char *bytes;
xfs_fileoff_t sumbno;
int t;
sumbno = NULLFILEOFF;
while ((sumbno = blkmap_next_off(blkmap, sumbno, &t)) != NULLFILEOFF) {
bno = blkmap_get(blkmap, sumbno);
if (bno == NULLFSBLOCK) {
if (!sflag)
dbprintf(_("block %lld for rtsummary inode is "
"missing\n"),
(xfs_fileoff_t)sumbno);
error++;
continue;
}
push_cur();
set_cur(&typtab[TYP_RTSUMMARY], XFS_FSB_TO_DADDR(mp, bno),
blkbb, DB_RING_IGN, NULL);
if ((bytes = iocur_top->data) == NULL) {
if (!sflag)
dbprintf(_("can't read block %lld for rtsummary "
"inode\n"),
(xfs_fileoff_t)sumbno);
error++;
pop_cur();
continue;
}
memcpy((char *)sumfile + sumbno * mp->m_sb.sb_blocksize, bytes,
mp->m_sb.sb_blocksize);
pop_cur();
}
}
static xfs_ino_t
process_sf_dir_v2(
xfs_dinode_t *dip,
int *dot,
int *dotdot,
inodata_t *id)
{
inodata_t *cid;
int i;
int i8;
xfs_ino_t lino;
int offset;
struct xfs_dir2_sf_hdr *sf;
xfs_dir2_sf_entry_t *sfe;
int v;
sf = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip);
addlink_inode(id);
v = verbose || id->ilist;
if (v)
dbprintf(_("dir %lld entry . %lld\n"), id->ino, id->ino);
(*dot)++;
sfe = xfs_dir2_sf_firstentry(sf);
offset = mp->m_dir_geo->data_first_offset;
for (i = sf->count - 1, i8 = 0; i >= 0; i--) {
if ((intptr_t)sfe +
libxfs_dir2_sf_entsize(mp, sf, sfe->namelen) -
(intptr_t)sf > be64_to_cpu(dip->di_size)) {
if (!sflag)
dbprintf(_("dir %llu bad size in entry at %d\n"),
id->ino,
(int)((char *)sfe - (char *)sf));
error++;
break;
}
lino = libxfs_dir2_sf_get_ino(mp, sf, sfe);
if (lino > XFS_DIR2_MAX_SHORT_INUM)
i8++;
cid = find_inode(lino, 1);
if (cid == NULL) {
if (!sflag)
dbprintf(_("dir %lld entry %*.*s bad inode "
"number %lld\n"),
id->ino, sfe->namelen, sfe->namelen,
sfe->name, lino);
error++;
} else {
addlink_inode(cid);
if (!cid->parent)
cid->parent = id;
addname_inode(cid, (char *)sfe->name, sfe->namelen);
}
if (v)
dbprintf(_("dir %lld entry %*.*s offset %d %lld\n"),
id->ino, sfe->namelen, sfe->namelen, sfe->name,
xfs_dir2_sf_get_offset(sfe), lino);
if (xfs_dir2_sf_get_offset(sfe) < offset) {
if (!sflag)
dbprintf(_("dir %lld entry %*.*s bad offset %d\n"),
id->ino, sfe->namelen, sfe->namelen,
sfe->name, xfs_dir2_sf_get_offset(sfe));
error++;
}
offset =
xfs_dir2_sf_get_offset(sfe) +
libxfs_dir2_sf_entsize(mp, sf, sfe->namelen);
sfe = libxfs_dir2_sf_nextentry(mp, sf, sfe);
}
if (i < 0 && (intptr_t)sfe - (intptr_t)sf !=
be64_to_cpu(dip->di_size)) {
if (!sflag)
dbprintf(_("dir %llu size is %lld, should be %u\n"),
id->ino, be64_to_cpu(dip->di_size),
(uint)((char *)sfe - (char *)sf));
error++;
}
if (offset + (sf->count + 2) * sizeof(xfs_dir2_leaf_entry_t) +
sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) {
if (!sflag)
dbprintf(_("dir %llu offsets too high\n"), id->ino);
error++;
}
lino = libxfs_dir2_sf_get_parent_ino(sf);
if (lino > XFS_DIR2_MAX_SHORT_INUM)
i8++;
cid = find_inode(lino, 1);
if (cid)
addlink_inode(cid);
else {
if (!sflag)
dbprintf(_("dir %lld entry .. bad inode number %lld\n"),
id->ino, lino);
error++;
}
if (v)
dbprintf(_("dir %lld entry .. %lld\n"), id->ino, lino);
if (i8 != sf->i8count) {
if (!sflag)
dbprintf(_("dir %lld i8count mismatch is %d should be "
"%d\n"),
id->ino, sf->i8count, i8);
error++;
}
(*dotdot)++;
return cid ? lino : NULLFSINO;
}
static void
quota_add(
xfs_dqid_t *prjid,
xfs_dqid_t *grpid,
xfs_dqid_t *usrid,
int dq,
xfs_qcnt_t bc,
xfs_qcnt_t ic,
xfs_qcnt_t rc)
{
if (qudo && usrid != NULL)
quota_add1(qudata, *usrid, dq, bc, ic, rc);
if (qgdo && grpid != NULL)
quota_add1(qgdata, *grpid, dq, bc, ic, rc);
if (qpdo && prjid != NULL)
quota_add1(qpdata, *prjid, dq, bc, ic, rc);
}
static void
quota_add1(
qdata_t **qt,
xfs_dqid_t id,
int dq,
xfs_qcnt_t bc,
xfs_qcnt_t ic,
xfs_qcnt_t rc)
{
qdata_t *qe;
int qh;
qinfo_t *qi;
qh = (int)(id % QDATA_HASH_SIZE);
qe = qt[qh];
while (qe) {
if (qe->id == id) {
qi = dq ? &qe->dq : &qe->count;
qi->bc += bc;
qi->ic += ic;
qi->rc += rc;
return;
}
qe = qe->next;
}
qe = xmalloc(sizeof(*qe));
qe->id = id;
qi = dq ? &qe->dq : &qe->count;
qi->bc = bc;
qi->ic = ic;
qi->rc = rc;
qi = dq ? &qe->count : &qe->dq;
qi->bc = qi->ic = qi->rc = 0;
qe->next = qt[qh];
qt[qh] = qe;
}
static void
quota_check(
char *s,
qdata_t **qt)
{
int i;
qdata_t *next;
qdata_t *qp;
for (i = 0; i < QDATA_HASH_SIZE; i++) {
qp = qt[i];
while (qp) {
next = qp->next;
if (qp->count.bc != qp->dq.bc ||
qp->count.ic != qp->dq.ic ||
qp->count.rc != qp->dq.rc) {
if (!sflag) {
dbprintf(_("%s quota id %u, have/exp"),
s, qp->id);
if (qp->count.bc != qp->dq.bc)
dbprintf(_(" bc %lld/%lld"),
qp->dq.bc,
qp->count.bc);
if (qp->count.ic != qp->dq.ic)
dbprintf(_(" ic %lld/%lld"),
qp->dq.ic,
qp->count.ic);
if (qp->count.rc != qp->dq.rc)
dbprintf(_(" rc %lld/%lld"),
qp->dq.rc,
qp->count.rc);
dbprintf("\n");
}
error++;
}
xfree(qp);
qp = next;
}
}
xfree(qt);
}
static void
quota_init(void)
{
qudo = mp->m_sb.sb_uquotino != 0 &&
mp->m_sb.sb_uquotino != NULLFSINO &&
(mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) &&
(mp->m_sb.sb_qflags & XFS_UQUOTA_CHKD);
qgdo = mp->m_sb.sb_gquotino != 0 &&
mp->m_sb.sb_gquotino != NULLFSINO &&
(mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) &&
(mp->m_sb.sb_qflags & XFS_GQUOTA_CHKD);
qpdo = mp->m_sb.sb_pquotino != 0 &&
mp->m_sb.sb_pquotino != NULLFSINO &&
(mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) &&
(mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD);
if (qudo)
qudata = xcalloc(QDATA_HASH_SIZE, sizeof(qdata_t *));
if (qgdo)
qgdata = xcalloc(QDATA_HASH_SIZE, sizeof(qdata_t *));
if (qpdo)
qpdata = xcalloc(QDATA_HASH_SIZE, sizeof(qdata_t *));
}
static void
scan_ag(
xfs_agnumber_t agno)
{
xfs_agf_t *agf;
xfs_agi_t *agi;
int i;
xfs_sb_t tsb;
xfs_sb_t *sb = &tsb;
agffreeblks = agflongest = 0;
agfbtreeblks = -2;
agicount = agifreecount = 0;
push_cur(); /* 1 pushed */
set_cur(&typtab[TYP_SB],
XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
if (!iocur_top->data) {
dbprintf(_("can't read superblock for ag %u\n"), agno);
serious_error++;
goto pop1_out;
}
libxfs_sb_from_disk(sb, iocur_top->data);
if (sb->sb_magicnum != XFS_SB_MAGIC) {
if (!sflag)
dbprintf(_("bad sb magic # %#x in ag %u\n"),
sb->sb_magicnum, agno);
error++;
}
if (!xfs_sb_good_version(sb)) {
if (!sflag)
dbprintf(_("bad sb version # %#x in ag %u\n"),
sb->sb_versionnum, agno);
error++;
sbver_err++;
}
if (!lazycount && xfs_sb_version_haslazysbcount(sb)) {
lazycount = 1;
}
if (agno == 0 && sb->sb_inprogress != 0) {
if (!sflag)
dbprintf(_("mkfs not completed successfully\n"));
error++;
}
set_dbmap(agno, XFS_SB_BLOCK(mp), 1, DBM_SB, agno, XFS_SB_BLOCK(mp));
if (sb->sb_logstart && XFS_FSB_TO_AGNO(mp, sb->sb_logstart) == agno)
set_dbmap(agno, XFS_FSB_TO_AGBNO(mp, sb->sb_logstart),
sb->sb_logblocks, DBM_LOG, agno, XFS_SB_BLOCK(mp));
push_cur(); /* 2 pushed */
set_cur(&typtab[TYP_AGF],
XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
if ((agf = iocur_top->data) == NULL) {
dbprintf(_("can't read agf block for ag %u\n"), agno);
serious_error++;
goto pop2_out;
}
if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
if (!sflag)
dbprintf(_("bad agf magic # %#x in ag %u\n"),
be32_to_cpu(agf->agf_magicnum), agno);
error++;
}
if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
if (!sflag)
dbprintf(_("bad agf version # %#x in ag %u\n"),
be32_to_cpu(agf->agf_versionnum), agno);
error++;
}
if (XFS_SB_BLOCK(mp) != XFS_AGF_BLOCK(mp))
set_dbmap(agno, XFS_AGF_BLOCK(mp), 1, DBM_AGF, agno,
XFS_SB_BLOCK(mp));
if (sb->sb_agblocks > be32_to_cpu(agf->agf_length))
set_dbmap(agno, be32_to_cpu(agf->agf_length),
sb->sb_agblocks - be32_to_cpu(agf->agf_length),
DBM_MISSING, agno, XFS_SB_BLOCK(mp));
push_cur(); /* 3 pushed */
set_cur(&typtab[TYP_AGI],
XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
if ((agi = iocur_top->data) == NULL) {
dbprintf(_("can't read agi block for ag %u\n"), agno);
serious_error++;
goto pop3_out;
}
if (be32_to_cpu(agi->agi_magicnum) != XFS_AGI_MAGIC) {
if (!sflag)
dbprintf(_("bad agi magic # %#x in ag %u\n"),
be32_to_cpu(agi->agi_magicnum), agno);
error++;
}
if (!XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum))) {
if (!sflag)
dbprintf(_("bad agi version # %#x in ag %u\n"),
be32_to_cpu(agi->agi_versionnum), agno);
error++;
}
if (XFS_SB_BLOCK(mp) != XFS_AGI_BLOCK(mp) &&
XFS_AGF_BLOCK(mp) != XFS_AGI_BLOCK(mp))
set_dbmap(agno, XFS_AGI_BLOCK(mp), 1, DBM_AGI, agno,
XFS_SB_BLOCK(mp));
scan_freelist(agf);
fdblocks--;
scan_sbtree(agf,
be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]),
be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]),
1, scanfunc_bno, TYP_BNOBT);
fdblocks--;
scan_sbtree(agf,
be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]),
be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]),
1, scanfunc_cnt, TYP_CNTBT);
if (agf->agf_roots[XFS_BTNUM_RMAP]) {
scan_sbtree(agf,
be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]),
be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]),
1, scanfunc_rmap, TYP_RMAPBT);
}
if (agf->agf_refcount_root) {
scan_sbtree(agf,
be32_to_cpu(agf->agf_refcount_root),
be32_to_cpu(agf->agf_refcount_level),
1, scanfunc_refcnt, TYP_REFCBT);
}
scan_sbtree(agf,
be32_to_cpu(agi->agi_root),
be32_to_cpu(agi->agi_level),
1, scanfunc_ino, TYP_INOBT);
if (agi->agi_free_root) {
scan_sbtree(agf,
be32_to_cpu(agi->agi_free_root),
be32_to_cpu(agi->agi_free_level),
1, scanfunc_fino, TYP_FINOBT);
}
if (be32_to_cpu(agf->agf_freeblks) != agffreeblks) {
if (!sflag)
dbprintf(_("agf_freeblks %u, counted %u in ag %u\n"),
be32_to_cpu(agf->agf_freeblks),
agffreeblks, agno);
error++;
}
if (be32_to_cpu(agf->agf_longest) != agflongest) {
if (!sflag)
dbprintf(_("agf_longest %u, counted %u in ag %u\n"),
be32_to_cpu(agf->agf_longest),
agflongest, agno);
error++;
}
if (lazycount &&
be32_to_cpu(agf->agf_btreeblks) != agfbtreeblks) {
if (!sflag)
dbprintf(_("agf_btreeblks %u, counted %u in ag %u\n"),
be32_to_cpu(agf->agf_btreeblks),
agfbtreeblks, agno);
error++;
}
agf_aggr_freeblks += agffreeblks + agfbtreeblks;
if (be32_to_cpu(agi->agi_count) != agicount) {
if (!sflag)
dbprintf(_("agi_count %u, counted %u in ag %u\n"),
be32_to_cpu(agi->agi_count),
agicount, agno);
error++;
}
if (be32_to_cpu(agi->agi_freecount) != agifreecount) {
if (!sflag)
dbprintf(_("agi_freecount %u, counted %u in ag %u\n"),
be32_to_cpu(agi->agi_freecount),
agifreecount, agno);
error++;
}
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
if (be32_to_cpu(agi->agi_unlinked[i]) != NULLAGINO) {
if (!sflag) {
xfs_agino_t agino=be32_to_cpu(agi->agi_unlinked[i]);
dbprintf(_("agi unlinked bucket %d is %u in ag "
"%u (inode=%lld)\n"), i, agino, agno,
XFS_AGINO_TO_INO(mp, agno, agino));
}
error++;
}
}
pop3_out:
pop_cur();
pop2_out:
pop_cur();
pop1_out:
pop_cur();
}
struct agfl_state {
xfs_agnumber_t agno;
unsigned int count;
};
static int
scan_agfl(
struct xfs_mount *mp,
xfs_agblock_t bno,
void *priv)
{
struct agfl_state *as = priv;
set_dbmap(as->agno, bno, 1, DBM_FREELIST, as->agno, XFS_AGFL_BLOCK(mp));
as->count++;
return 0;
}
static void
scan_freelist(
xfs_agf_t *agf)
{
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
struct agfl_state state;
if (XFS_SB_BLOCK(mp) != XFS_AGFL_BLOCK(mp) &&
XFS_AGF_BLOCK(mp) != XFS_AGFL_BLOCK(mp) &&
XFS_AGI_BLOCK(mp) != XFS_AGFL_BLOCK(mp))
set_dbmap(seqno, XFS_AGFL_BLOCK(mp), 1, DBM_AGFL, seqno,
XFS_SB_BLOCK(mp));
if (be32_to_cpu(agf->agf_flcount) == 0)
return;
push_cur();
set_cur(&typtab[TYP_AGFL],
XFS_AG_DADDR(mp, seqno, XFS_AGFL_DADDR(mp)),
XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
if (iocur_top->data == NULL) {
dbprintf(_("can't read agfl block for ag %u\n"), seqno);
serious_error++;
pop_cur();
return;
}
/* verify agf values before proceeding */
if (be32_to_cpu(agf->agf_flfirst) >= libxfs_agfl_size(mp) ||
be32_to_cpu(agf->agf_fllast) >= libxfs_agfl_size(mp)) {
dbprintf(_("agf %d freelist blocks bad, skipping "
"freelist scan\n"), seqno);
pop_cur();
return;
}
/* open coded XFS_BUF_TO_AGFL_BNO */
state.count = 0;
state.agno = seqno;
libxfs_agfl_walk(mp, agf, iocur_top->bp, scan_agfl, &state);
if (state.count != be32_to_cpu(agf->agf_flcount)) {
if (!sflag)
dbprintf(_("freeblk count %u != flcount %u in ag %u\n"),
state.count,
be32_to_cpu(agf->agf_flcount),
seqno);
error++;
}
fdblocks += state.count;
agf_aggr_freeblks += state.count;
pop_cur();
}
static void
scan_lbtree(
xfs_fsblock_t root,
int nlevels,
scan_lbtree_f_t func,
dbm_t type,
inodata_t *id,
xfs_rfsblock_t *totd,
xfs_rfsblock_t *toti,
xfs_extnum_t *nex,
blkmap_t **blkmapp,
int isroot,
typnm_t btype)
{
push_cur();
set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, root), blkbb, DB_RING_IGN,
NULL);
if (iocur_top->data == NULL) {
if (!sflag)
dbprintf(_("can't read btree block %u/%u\n"),
XFS_FSB_TO_AGNO(mp, root),
XFS_FSB_TO_AGBNO(mp, root));
error++;
pop_cur();
return;
}
(*func)(iocur_top->data, nlevels - 1, type, root, id, totd, toti, nex,
blkmapp, isroot, btype);
pop_cur();
}
static void
scan_sbtree(
xfs_agf_t *agf,
xfs_agblock_t root,
int nlevels,
int isroot,
scan_sbtree_f_t func,
typnm_t btype)
{
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
push_cur();
set_cur(&typtab[btype],
XFS_AGB_TO_DADDR(mp, seqno, root), blkbb, DB_RING_IGN, NULL);
if (iocur_top->data == NULL) {
if (!sflag)
dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
error++;
pop_cur();
return;
}
(*func)(iocur_top->data, nlevels - 1, agf, root, isroot);
pop_cur();
}
static void
scanfunc_bmap(
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_bmbt_ptr_t *pp;
xfs_bmbt_rec_t *rp;
agno = XFS_FSB_TO_AGNO(mp, bno);
agbno = XFS_FSB_TO_AGBNO(mp, bno);
if (be32_to_cpu(block->bb_magic) != XFS_BMAP_MAGIC &&
be32_to_cpu(block->bb_magic) != XFS_BMAP_CRC_MAGIC) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("bad magic # %#x in inode %lld bmbt block "
"%u/%u\n"),
be32_to_cpu(block->bb_magic), id->ino, agno, agbno);
error++;
}
if (be16_to_cpu(block->bb_level) != level) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("expected level %d got %d in inode %lld bmbt "
"block %u/%u\n"),
level, be16_to_cpu(block->bb_level), id->ino, 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_bmap_dmxr[0] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_bmap_dmnr[0])) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) "
"in inode %lld bmap block %lld\n"),
be16_to_cpu(block->bb_numrecs), mp->m_bmap_dmnr[0],
mp->m_bmap_dmxr[0], id->ino,
(xfs_fsblock_t)bno);
error++;
return;
}
rp = XFS_BMBT_REC_ADDR(mp, block, 1);
*nex += be16_to_cpu(block->bb_numrecs);
process_bmbt_reclist(rp, be16_to_cpu(block->bb_numrecs), type, id, totd,
blkmapp);
return;
}
if (be16_to_cpu(block->bb_numrecs) > mp->m_bmap_dmxr[1] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_bmap_dmnr[1])) {
if (!sflag || id->ilist || CHECK_BLIST(bno))
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
"inode %lld bmap block %lld\n"),
be16_to_cpu(block->bb_numrecs), mp->m_bmap_dmnr[1],
mp->m_bmap_dmxr[1], id->ino, (xfs_fsblock_t)bno);
error++;
return;
}
pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[0]);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
scan_lbtree(be64_to_cpu(pp[i]), level, scanfunc_bmap, type, id,
totd, toti, nex, blkmapp, 0, btype);
}
static void
scanfunc_bno(
struct xfs_btree_block *block,
int level,
xfs_agf_t *agf,
xfs_agblock_t bno,
int isroot)
{
int i;
xfs_alloc_ptr_t *pp;
xfs_alloc_rec_t *rp;
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
xfs_agblock_t lastblock;
if (be32_to_cpu(block->bb_magic) != XFS_ABTB_MAGIC &&
be32_to_cpu(block->bb_magic) != XFS_ABTB_CRC_MAGIC) {
dbprintf(_("bad magic # %#x in btbno block %u/%u\n"),
be32_to_cpu(block->bb_magic), seqno, bno);
serious_error++;
return;
}
fdblocks++;
agfbtreeblks++;
if (be16_to_cpu(block->bb_level) != level) {
if (!sflag)
dbprintf(_("expected level %d got %d in btbno block "
"%u/%u\n"),
level, be16_to_cpu(block->bb_level), seqno, bno);
error++;
}
set_dbmap(seqno, bno, 1, DBM_BTBNO, seqno, bno);
if (level == 0) {
if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
"btbno block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
mp->m_alloc_mxr[0], seqno, bno);
serious_error++;
return;
}
rp = XFS_ALLOC_REC_ADDR(mp, block, 1);
lastblock = 0;
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
set_dbmap(seqno, be32_to_cpu(rp[i].ar_startblock),
be32_to_cpu(rp[i].ar_blockcount), DBM_FREE1,
seqno, bno);
if (be32_to_cpu(rp[i].ar_startblock) <= lastblock) {
dbprintf(_(
"out-of-order bno btree record %d (%u %u) block %u/%u\n"),
i, be32_to_cpu(rp[i].ar_startblock),
be32_to_cpu(rp[i].ar_blockcount),
be32_to_cpu(agf->agf_seqno), bno);
serious_error++;
} else {
lastblock = be32_to_cpu(rp[i].ar_startblock);
}
}
return;
}
if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
"%u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
mp->m_alloc_mxr[1], seqno, bno);
serious_error++;
return;
}
pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_bno, TYP_BNOBT);
}
static void
scanfunc_cnt(
struct xfs_btree_block *block,
int level,
xfs_agf_t *agf,
xfs_agblock_t bno,
int isroot)
{
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
int i;
xfs_alloc_ptr_t *pp;
xfs_alloc_rec_t *rp;
xfs_extlen_t lastcount;
if (be32_to_cpu(block->bb_magic) != XFS_ABTC_MAGIC &&
be32_to_cpu(block->bb_magic) != XFS_ABTC_CRC_MAGIC) {
dbprintf(_("bad magic # %#x in btcnt block %u/%u\n"),
be32_to_cpu(block->bb_magic), seqno, bno);
serious_error++;
return;
}
fdblocks++;
agfbtreeblks++;
if (be16_to_cpu(block->bb_level) != level) {
if (!sflag)
dbprintf(_("expected level %d got %d in btcnt block "
"%u/%u\n"),
level, be16_to_cpu(block->bb_level), seqno, bno);
error++;
}
set_dbmap(seqno, bno, 1, DBM_BTCNT, seqno, bno);
if (level == 0) {
if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[0] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[0])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
"btbno block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[0],
mp->m_alloc_mxr[0], seqno, bno);
serious_error++;
return;
}
rp = XFS_ALLOC_REC_ADDR(mp, block, 1);
lastcount = 0;
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
check_set_dbmap(seqno, be32_to_cpu(rp[i].ar_startblock),
be32_to_cpu(rp[i].ar_blockcount), DBM_FREE1, DBM_FREE2,
seqno, bno);
fdblocks += be32_to_cpu(rp[i].ar_blockcount);
agffreeblks += be32_to_cpu(rp[i].ar_blockcount);
if (be32_to_cpu(rp[i].ar_blockcount) > agflongest)
agflongest = be32_to_cpu(rp[i].ar_blockcount);
if (be32_to_cpu(rp[i].ar_blockcount) < lastcount) {
dbprintf(_(
"out-of-order cnt btree record %d (%u %u) block %u/%u\n"),
i, be32_to_cpu(rp[i].ar_startblock),
be32_to_cpu(rp[i].ar_blockcount),
be32_to_cpu(agf->agf_seqno), bno);
} else {
lastcount = be32_to_cpu(rp[i].ar_blockcount);
}
}
return;
}
if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[1] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_alloc_mnr[1])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in btbno block "
"%u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_alloc_mnr[1],
mp->m_alloc_mxr[1], seqno, bno);
serious_error++;
return;
}
pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_cnt, TYP_CNTBT);
}
static void
scanfunc_ino(
struct xfs_btree_block *block,
int level,
xfs_agf_t *agf,
xfs_agblock_t bno,
int isroot)
{
xfs_agino_t agino;
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
int i;
int isfree;
int j;
int freecount;
int nfree;
int off;
xfs_inobt_ptr_t *pp;
xfs_inobt_rec_t *rp;
xfs_agblock_t agbno;
xfs_agblock_t end_agbno;
struct xfs_dinode *dip;
int blks_per_buf;
int inodes_per_buf;
int ioff;
struct xfs_ino_geometry *igeo = M_IGEO(mp);
if (xfs_sb_version_hassparseinodes(&mp->m_sb))
blks_per_buf = igeo->blocks_per_cluster;
else
blks_per_buf = igeo->ialloc_blks;
inodes_per_buf = min(XFS_FSB_TO_INO(mp, blks_per_buf),
XFS_INODES_PER_CHUNK);
if (be32_to_cpu(block->bb_magic) != XFS_IBT_MAGIC &&
be32_to_cpu(block->bb_magic) != XFS_IBT_CRC_MAGIC) {
dbprintf(_("bad magic # %#x in inobt block %u/%u\n"),
be32_to_cpu(block->bb_magic), seqno, bno);
serious_error++;
return;
}
if (be16_to_cpu(block->bb_level) != level) {
if (!sflag)
dbprintf(_("expected level %d got %d in inobt block "
"%u/%u\n"),
level, be16_to_cpu(block->bb_level), seqno, bno);
error++;
}
set_dbmap(seqno, bno, 1, DBM_BTINO, seqno, bno);
if (level == 0) {
if (be16_to_cpu(block->bb_numrecs) > igeo->inobt_mxr[0] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < igeo->inobt_mnr[0])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
"inobt block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), igeo->inobt_mnr[0],
igeo->inobt_mxr[0], seqno, bno);
serious_error++;
return;
}
rp = XFS_INOBT_REC_ADDR(mp, block, 1);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
agino = be32_to_cpu(rp[i].ir_startino);
agbno = XFS_AGINO_TO_AGBNO(mp, agino);
off = XFS_AGINO_TO_OFFSET(mp, agino);
end_agbno = agbno + igeo->ialloc_blks;
if (off == 0) {
if ((sbversion & XFS_SB_VERSION_ALIGNBIT) &&
mp->m_sb.sb_inoalignmt &&
(XFS_INO_TO_AGBNO(mp, agino) %
mp->m_sb.sb_inoalignmt))
sbversion &= ~XFS_SB_VERSION_ALIGNBIT;
}
push_cur();
ioff = 0;
nfree = 0;
while (agbno < end_agbno &&
ioff < XFS_INODES_PER_CHUNK) {
if (xfs_inobt_is_sparse_disk(&rp[i], ioff))
goto next_buf;
if (off < XFS_INODES_PER_CHUNK)
set_dbmap(seqno, agbno, blks_per_buf,
DBM_INODE, seqno, bno);
icount += inodes_per_buf;
agicount += inodes_per_buf;
set_cur(&typtab[TYP_INODE],
XFS_AGB_TO_DADDR(mp, seqno, agbno),
XFS_FSB_TO_BB(mp, blks_per_buf),
DB_RING_IGN, NULL);
if (iocur_top->data == NULL) {
if (!sflag)
dbprintf(_("can't read inode block "
"%u/%u\n"), seqno,
agbno);
error++;
goto next_buf;
}
for (j = 0; j < inodes_per_buf; j++) {
isfree = XFS_INOBT_IS_FREE_DISK(&rp[i], ioff + j);
if (isfree)
nfree++;
dip = (xfs_dinode_t *)((char *)iocur_top->data +
((off + j) << mp->m_sb.sb_inodelog));
process_inode(agf, agino + ioff + j, dip, isfree);
}
next_buf:
agbno += blks_per_buf;
ioff += inodes_per_buf;
}
if (xfs_sb_version_hassparseinodes(&mp->m_sb))
freecount = rp[i].ir_u.sp.ir_freecount;
else
freecount = be32_to_cpu(rp[i].ir_u.f.ir_freecount);
ifree += freecount;
agifreecount += freecount;
if (nfree != freecount) {
if (!sflag)
dbprintf(_("ir_freecount/free mismatch, "
"inode chunk %u/%u, freecount "
"%d nfree %d\n"),
seqno, agino, freecount, nfree);
error++;
}
pop_cur();
}
return;
}
if (be16_to_cpu(block->bb_numrecs) > igeo->inobt_mxr[1] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < igeo->inobt_mnr[1])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in inobt block "
"%u/%u\n"),
be16_to_cpu(block->bb_numrecs), igeo->inobt_mnr[1],
igeo->inobt_mxr[1], seqno, bno);
serious_error++;
return;
}
pp = XFS_INOBT_PTR_ADDR(mp, block, 1, igeo->inobt_mxr[1]);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_ino, TYP_INOBT);
}
static void
scanfunc_fino(
struct xfs_btree_block *block,
int level,
struct xfs_agf *agf,
xfs_agblock_t bno,
int isroot)
{
xfs_agino_t agino;
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
int i;
int off;
xfs_inobt_ptr_t *pp;
struct xfs_inobt_rec *rp;
xfs_agblock_t agbno;
xfs_agblock_t end_agbno;
int blks_per_buf;
int inodes_per_buf;
int ioff;
struct xfs_ino_geometry *igeo = M_IGEO(mp);
if (xfs_sb_version_hassparseinodes(&mp->m_sb))
blks_per_buf = igeo->blocks_per_cluster;
else
blks_per_buf = igeo->ialloc_blks;
inodes_per_buf = min(XFS_FSB_TO_INO(mp, blks_per_buf),
XFS_INODES_PER_CHUNK);
if (be32_to_cpu(block->bb_magic) != XFS_FIBT_MAGIC &&
be32_to_cpu(block->bb_magic) != XFS_FIBT_CRC_MAGIC) {
dbprintf(_("bad magic # %#x in finobt block %u/%u\n"),
be32_to_cpu(block->bb_magic), seqno, bno);
serious_error++;
return;
}
if (be16_to_cpu(block->bb_level) != level) {
if (!sflag)
dbprintf(_("expected level %d got %d in finobt block "
"%u/%u\n"),
level, be16_to_cpu(block->bb_level), seqno, bno);
error++;
}
set_dbmap(seqno, bno, 1, DBM_BTFINO, seqno, bno);
if (level == 0) {
if (be16_to_cpu(block->bb_numrecs) > igeo->inobt_mxr[0] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < igeo->inobt_mnr[0])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
"finobt block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), igeo->inobt_mnr[0],
igeo->inobt_mxr[0], seqno, bno);
serious_error++;
return;
}
rp = XFS_INOBT_REC_ADDR(mp, block, 1);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
agino = be32_to_cpu(rp[i].ir_startino);
agbno = XFS_AGINO_TO_AGBNO(mp, agino);
off = XFS_AGINO_TO_OFFSET(mp, agino);
end_agbno = agbno + igeo->ialloc_blks;
if (off == 0) {
if ((sbversion & XFS_SB_VERSION_ALIGNBIT) &&
mp->m_sb.sb_inoalignmt &&
(XFS_INO_TO_AGBNO(mp, agino) %
mp->m_sb.sb_inoalignmt))
sbversion &= ~XFS_SB_VERSION_ALIGNBIT;
}
ioff = 0;
while (agbno < end_agbno &&
ioff < XFS_INODES_PER_CHUNK) {
if (xfs_inobt_is_sparse_disk(&rp[i], ioff))
goto next_buf;
check_set_dbmap(seqno, agbno,
(xfs_extlen_t)max(1,
inodes_per_buf >>
mp->m_sb.sb_inopblog),
DBM_INODE, DBM_INODE, seqno, bno);
next_buf:
agbno += blks_per_buf;
ioff += inodes_per_buf;
}
}
return;
}
if (be16_to_cpu(block->bb_numrecs) > igeo->inobt_mxr[1] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < igeo->inobt_mnr[1])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in finobt block "
"%u/%u\n"),
be16_to_cpu(block->bb_numrecs), igeo->inobt_mnr[1],
igeo->inobt_mxr[1], seqno, bno);
serious_error++;
return;
}
pp = XFS_INOBT_PTR_ADDR(mp, block, 1, igeo->inobt_mxr[1]);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_fino, TYP_FINOBT);
}
static void
scanfunc_rmap(
struct xfs_btree_block *block,
int level,
struct xfs_agf *agf,
xfs_agblock_t bno,
int isroot)
{
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
int i;
xfs_rmap_ptr_t *pp;
struct xfs_rmap_rec *rp;
xfs_agblock_t lastblock;
if (be32_to_cpu(block->bb_magic) != XFS_RMAP_CRC_MAGIC) {
dbprintf(_("bad magic # %#x in rmapbt block %u/%u\n"),
be32_to_cpu(block->bb_magic), seqno, bno);
serious_error++;
return;
}
if (be16_to_cpu(block->bb_level) != level) {
if (!sflag)
dbprintf(_("expected level %d got %d in rmapbt block "
"%u/%u\n"),
level, be16_to_cpu(block->bb_level), seqno, bno);
error++;
}
if (!isroot) {
fdblocks++;
agfbtreeblks++;
}
set_dbmap(seqno, bno, 1, DBM_BTRMAP, seqno, bno);
if (level == 0) {
if (be16_to_cpu(block->bb_numrecs) > mp->m_rmap_mxr[0] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_rmap_mnr[0])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
"rmapbt block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_rmap_mnr[0],
mp->m_rmap_mxr[0], seqno, bno);
serious_error++;
return;
}
rp = XFS_RMAP_REC_ADDR(block, 1);
lastblock = 0;
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
if (be32_to_cpu(rp[i].rm_startblock) < lastblock) {
dbprintf(_(
"out-of-order rmap btree record %d (%u %u) block %u/%u\n"),
i, be32_to_cpu(rp[i].rm_startblock),
be32_to_cpu(rp[i].rm_startblock),
be32_to_cpu(agf->agf_seqno), bno);
} else {
lastblock = be32_to_cpu(rp[i].rm_startblock);
}
}
return;
}
if (be16_to_cpu(block->bb_numrecs) > mp->m_rmap_mxr[1] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_rmap_mnr[1])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in rmapbt "
"block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_rmap_mnr[1],
mp->m_rmap_mxr[1], seqno, bno);
serious_error++;
return;
}
pp = XFS_RMAP_PTR_ADDR(block, 1, mp->m_rmap_mxr[1]);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_rmap,
TYP_RMAPBT);
}
static void
scanfunc_refcnt(
struct xfs_btree_block *block,
int level,
struct xfs_agf *agf,
xfs_agblock_t bno,
int isroot)
{
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
int i;
xfs_refcount_ptr_t *pp;
struct xfs_refcount_rec *rp;
xfs_agblock_t lastblock;
if (be32_to_cpu(block->bb_magic) != XFS_REFC_CRC_MAGIC) {
dbprintf(_("bad magic # %#x in refcntbt block %u/%u\n"),
be32_to_cpu(block->bb_magic), seqno, bno);
serious_error++;
return;
}
if (be16_to_cpu(block->bb_level) != level) {
if (!sflag)
dbprintf(_("expected level %d got %d in refcntbt block "
"%u/%u\n"),
level, be16_to_cpu(block->bb_level), seqno, bno);
error++;
}
set_dbmap(seqno, bno, 1, DBM_BTREFC, seqno, bno);
if (level == 0) {
if (be16_to_cpu(block->bb_numrecs) > mp->m_refc_mxr[0] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_refc_mnr[0])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in "
"refcntbt block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_refc_mnr[0],
mp->m_refc_mxr[0], seqno, bno);
serious_error++;
return;
}
rp = XFS_REFCOUNT_REC_ADDR(block, 1);
lastblock = 0;
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
if (be32_to_cpu(rp[i].rc_refcount) == 1) {
xfs_agblock_t agbno;
char *msg;
agbno = be32_to_cpu(rp[i].rc_startblock);
if (agbno >= XFS_REFC_COW_START) {
agbno -= XFS_REFC_COW_START;
msg = _(
"leftover CoW extent (%u/%u) len %u\n");
} else {
msg = _(
"leftover CoW extent at unexpected address (%u/%u) len %u\n");
}
dbprintf(msg,
seqno,
agbno,
be32_to_cpu(rp[i].rc_blockcount));
set_dbmap(seqno,
agbno,
be32_to_cpu(rp[i].rc_blockcount),
DBM_COWDATA, seqno, bno);
} else {
set_dbmap(seqno,
be32_to_cpu(rp[i].rc_startblock),
be32_to_cpu(rp[i].rc_blockcount),
DBM_RLDATA, seqno, bno);
}
if (be32_to_cpu(rp[i].rc_startblock) < lastblock) {
dbprintf(_(
"out-of-order refcnt btree record %d (%u %u) block %u/%u\n"),
i, be32_to_cpu(rp[i].rc_startblock),
be32_to_cpu(rp[i].rc_startblock),
be32_to_cpu(agf->agf_seqno), 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_refc_mxr[1] ||
(isroot == 0 && be16_to_cpu(block->bb_numrecs) < mp->m_refc_mnr[1])) {
dbprintf(_("bad btree nrecs (%u, min=%u, max=%u) in refcntbt "
"block %u/%u\n"),
be16_to_cpu(block->bb_numrecs), mp->m_refc_mnr[1],
mp->m_refc_mxr[1], seqno, bno);
serious_error++;
return;
}
pp = XFS_REFCOUNT_PTR_ADDR(block, 1, mp->m_refc_mxr[1]);
for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
scan_sbtree(agf, be32_to_cpu(pp[i]), level, 0, scanfunc_refcnt,
TYP_REFCBT);
}
static void
set_dbmap(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_extlen_t len,
dbm_t type,
xfs_agnumber_t c_agno,
xfs_agblock_t c_agbno)
{
check_set_dbmap(agno, agbno, len, DBM_UNKNOWN, type, c_agno, c_agbno);
}
static void
set_inomap(
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_extlen_t len,
inodata_t *id)
{
xfs_extlen_t i;
inodata_t **idp;
int mayprint;
if (!check_inomap(agno, agbno, len, id->ino))
return;
mayprint = verbose | id->ilist | blist_size;
for (i = 0, idp = &inomap[agno][agbno]; i < len; i++, idp++) {
*idp = id;
if (mayprint &&
(verbose || id->ilist || CHECK_BLISTA(agno, agbno + i)))
dbprintf(_("setting inode to %lld for block %u/%u\n"),
id->ino, agno, agbno + i);
}
}
static void
set_rdbmap(
xfs_rfsblock_t bno,
xfs_extlen_t len,
dbm_t type)
{
check_set_rdbmap(bno, len, DBM_UNKNOWN, type);
}
static void
set_rinomap(
xfs_rfsblock_t bno,
xfs_extlen_t len,
inodata_t *id)
{
xfs_extlen_t i;
inodata_t **idp;
int mayprint;
if (!check_rinomap(bno, len, id->ino))
return;
mayprint = verbose | id->ilist | blist_size;
for (i = 0, idp = &inomap[mp->m_sb.sb_agcount][bno];
i < len;
i++, idp++) {
*idp = id;
if (mayprint && (verbose || id->ilist || CHECK_BLIST(bno + i)))
dbprintf(_("setting inode to %lld for rtblock %llu\n"),
id->ino, bno + i);
}
}
static void
setlink_inode(
inodata_t *id,
nlink_t nlink,
int isdir,
int security)
{
id->link_set = nlink;
id->isdir = isdir;
id->security = security;
if (verbose || id->ilist)
dbprintf(_("inode %lld nlink %u %s dir\n"), id->ino, nlink,
isdir ? "is" : "not");
}