blob: fdc70e95eb9f58c3e597edeef216c318ba3bef07 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include "command.h"
#include "type.h"
#include "fprint.h"
#include "faddr.h"
#include "field.h"
#include "bmap.h"
#include "io.h"
#include "inode.h"
#include "output.h"
#include "init.h"
static int bmap_f(int argc, char **argv);
static int bmap_one_extent(xfs_bmbt_rec_t *ep,
xfs_fileoff_t *offp, xfs_fileoff_t eoff,
int *idxp, bmap_ext_t *bep);
static xfs_fsblock_t select_child(xfs_fileoff_t off, xfs_bmbt_key_t *kp,
xfs_bmbt_ptr_t *pp, int nrecs);
static const cmdinfo_t bmap_cmd =
{ "bmap", NULL, bmap_f, 0, 3, 0, N_("[-ad] [block [len]]"),
N_("show block map for current file"), NULL };
void
bmap(
xfs_fileoff_t offset,
xfs_filblks_t len,
int whichfork,
int *nexp,
bmap_ext_t *bep)
{
struct xfs_btree_block *block;
xfs_fsblock_t bno;
xfs_fileoff_t curoffset;
xfs_dinode_t *dip;
xfs_fileoff_t eoffset;
xfs_bmbt_rec_t *ep;
enum xfs_dinode_fmt fmt;
int fsize;
xfs_bmbt_key_t *kp;
int n;
int nex;
xfs_fsblock_t nextbno;
int nextents;
xfs_bmbt_ptr_t *pp;
xfs_bmdr_block_t *rblock;
typnm_t typ;
xfs_bmbt_rec_t *xp;
push_cur();
set_cur_inode(iocur_top->ino);
nex = *nexp;
*nexp = 0;
ASSERT(nex > 0);
dip = iocur_top->data;
n = 0;
eoffset = offset + len - 1;
curoffset = offset;
fmt = (enum xfs_dinode_fmt)XFS_DFORK_FORMAT(dip, whichfork);
typ = whichfork == XFS_DATA_FORK ? TYP_BMAPBTD : TYP_BMAPBTA;
ASSERT(typtab[typ].typnm == typ);
ASSERT(fmt == XFS_DINODE_FMT_LOCAL || fmt == XFS_DINODE_FMT_EXTENTS ||
fmt == XFS_DINODE_FMT_BTREE);
if (fmt == XFS_DINODE_FMT_EXTENTS) {
nextents = XFS_DFORK_NEXTENTS(dip, whichfork);
xp = (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork);
for (ep = xp; ep < &xp[nextents] && n < nex; ep++) {
if (!bmap_one_extent(ep, &curoffset, eoffset, &n, bep))
break;
}
} else if (fmt == XFS_DINODE_FMT_BTREE) {
push_cur();
rblock = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
fsize = XFS_DFORK_SIZE(dip, mp, whichfork);
pp = XFS_BMDR_PTR_ADDR(rblock, 1, libxfs_bmdr_maxrecs(fsize, 0));
kp = XFS_BMDR_KEY_ADDR(rblock, 1);
bno = select_child(curoffset, kp, pp,
be16_to_cpu(rblock->bb_numrecs));
for (;;) {
set_cur(&typtab[typ], XFS_FSB_TO_DADDR(mp, bno),
blkbb, DB_RING_IGN, NULL);
block = (struct xfs_btree_block *)iocur_top->data;
if (be16_to_cpu(block->bb_level) == 0)
break;
pp = XFS_BMBT_PTR_ADDR(mp, block, 1,
libxfs_bmbt_maxrecs(mp, mp->m_sb.sb_blocksize, 0));
kp = XFS_BMBT_KEY_ADDR(mp, block, 1);
bno = select_child(curoffset, kp, pp,
be16_to_cpu(block->bb_numrecs));
}
for (;;) {
nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib);
nextents = be16_to_cpu(block->bb_numrecs);
xp = (xfs_bmbt_rec_t *)
XFS_BMBT_REC_ADDR(mp, block, 1);
for (ep = xp; ep < &xp[nextents] && n < nex; ep++) {
if (!bmap_one_extent(ep, &curoffset, eoffset,
&n, bep)) {
nextbno = NULLFSBLOCK;
break;
}
}
bno = nextbno;
if (bno == NULLFSBLOCK)
break;
set_cur(&typtab[typ], XFS_FSB_TO_DADDR(mp, bno),
blkbb, DB_RING_IGN, NULL);
block = (struct xfs_btree_block *)iocur_top->data;
}
pop_cur();
}
pop_cur();
*nexp = n;
}
static int
bmap_f(
int argc,
char **argv)
{
int afork = 0;
bmap_ext_t be;
int c;
xfs_fileoff_t co, cosave;
int dfork = 0;
xfs_dinode_t *dip;
xfs_fileoff_t eo;
xfs_filblks_t len;
int nex;
char *p;
int whichfork;
if (iocur_top->ino == NULLFSINO) {
dbprintf(_("no current inode\n"));
return 0;
}
optind = 0;
if (argc) while ((c = getopt(argc, argv, "ad")) != EOF) {
switch (c) {
case 'a':
afork = 1;
break;
case 'd':
dfork = 1;
break;
default:
dbprintf(_("bad option for bmap command\n"));
return 0;
}
}
if (afork + dfork == 0) {
push_cur();
set_cur_inode(iocur_top->ino);
dip = iocur_top->data;
if (be32_to_cpu(dip->di_nextents))
dfork = 1;
if (be16_to_cpu(dip->di_anextents))
afork = 1;
pop_cur();
}
if (optind < argc) {
co = (xfs_fileoff_t)strtoull(argv[optind], &p, 0);
if (*p != '\0') {
dbprintf(_("bad block number for bmap %s\n"),
argv[optind]);
return 0;
}
optind++;
if (optind < argc) {
len = (xfs_filblks_t)strtoull(argv[optind], &p, 0);
if (*p != '\0') {
dbprintf(_("bad len for bmap %s\n"), argv[optind]);
return 0;
}
eo = co + len - 1;
} else
eo = co;
} else {
co = 0;
eo = -1;
}
cosave = co;
for (whichfork = XFS_DATA_FORK;
whichfork <= XFS_ATTR_FORK;
whichfork++) {
if (whichfork == XFS_DATA_FORK && !dfork)
continue;
if (whichfork == XFS_ATTR_FORK && !afork)
continue;
for (;;) {
nex = 1;
bmap(co, eo - co + 1, whichfork, &nex, &be);
if (nex == 0)
break;
dbprintf(_("%s offset %lld startblock %llu (%u/%u) count "
"%llu flag %u\n"),
whichfork == XFS_DATA_FORK ? _("data") : _("attr"),
be.startoff, be.startblock,
XFS_FSB_TO_AGNO(mp, be.startblock),
XFS_FSB_TO_AGBNO(mp, be.startblock),
be.blockcount, be.flag);
co = be.startoff + be.blockcount;
}
co = cosave;
}
return 0;
}
void
bmap_init(void)
{
add_command(&bmap_cmd);
}
static int
bmap_one_extent(
xfs_bmbt_rec_t *ep,
xfs_fileoff_t *offp,
xfs_fileoff_t eoff,
int *idxp,
bmap_ext_t *bep)
{
xfs_filblks_t c;
xfs_fileoff_t curoffset;
int f;
int idx;
xfs_fileoff_t o;
xfs_fsblock_t s;
convert_extent(ep, &o, &s, &c, &f);
curoffset = *offp;
idx = *idxp;
if (o + c <= curoffset)
return 1;
if (o > eoff)
return 0;
if (o < curoffset) {
c -= curoffset - o;
s += curoffset - o;
o = curoffset;
}
if (o + c - 1 > eoff)
c -= (o + c - 1) - eoff;
bep[idx].startoff = o;
bep[idx].startblock = s;
bep[idx].blockcount = c;
bep[idx].flag = f;
*idxp = idx + 1;
*offp = o + c;
return 1;
}
void
convert_extent(
xfs_bmbt_rec_t *rp,
xfs_fileoff_t *op,
xfs_fsblock_t *sp,
xfs_filblks_t *cp,
int *fp)
{
struct xfs_bmbt_irec irec;
libxfs_bmbt_disk_get_all(rp, &irec);
*fp = irec.br_state == XFS_EXT_UNWRITTEN;
*op = irec.br_startoff;
*sp = irec.br_startblock;
*cp = irec.br_blockcount;
}
void
make_bbmap(
bbmap_t *bbmap,
int nex,
bmap_ext_t *bmp)
{
int i;
for (i = 0; i < nex; i++) {
bbmap->b[i].bm_bn = XFS_FSB_TO_DADDR(mp, bmp[i].startblock);
bbmap->b[i].bm_len = XFS_FSB_TO_BB(mp, bmp[i].blockcount);
}
bbmap->nmaps = nex;
}
static xfs_fsblock_t
select_child(
xfs_fileoff_t off,
xfs_bmbt_key_t *kp,
xfs_bmbt_ptr_t *pp,
int nrecs)
{
int i;
for (i = 0; i < nrecs; i++) {
if (be64_to_cpu(kp[i].br_startoff) == off)
return be64_to_cpu(pp[i]);
if (be64_to_cpu(kp[i].br_startoff) > off) {
if (i == 0)
return be64_to_cpu(pp[i]);
else
return be64_to_cpu(pp[i - 1]);
}
}
return be64_to_cpu(pp[nrecs - 1]);
}