blob: 2f1978c41f30945c3f62381abaef8a7b3e49103d [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "libxfs.h"
#include "block.h"
#include "bmap.h"
#include "command.h"
#include "type.h"
#include "faddr.h"
#include "fprint.h"
#include "field.h"
#include "inode.h"
#include "io.h"
#include "output.h"
#include "init.h"
static int ablock_f(int argc, char **argv);
static void ablock_help(void);
static int daddr_f(int argc, char **argv);
static void daddr_help(void);
static int dblock_f(int argc, char **argv);
static void dblock_help(void);
static int fsblock_f(int argc, char **argv);
static void fsblock_help(void);
static int rtblock_f(int argc, char **argv);
static void rtblock_help(void);
static int rtextent_f(int argc, char **argv);
static void rtextent_help(void);
static int logblock_f(int argc, char **argv);
static void logblock_help(void);
static void print_rawdata(void *data, int len);
static const cmdinfo_t ablock_cmd =
{ "ablock", NULL, ablock_f, 1, 1, 1, N_("filoff"),
N_("set address to file offset (attr fork)"), ablock_help };
static const cmdinfo_t daddr_cmd =
{ "daddr", NULL, daddr_f, 0, -1, 1, N_("[d]"),
N_("set address to daddr value"), daddr_help };
static const cmdinfo_t dblock_cmd =
{ "dblock", NULL, dblock_f, 1, 1, 1, N_("filoff"),
N_("set address to file offset (data fork)"), dblock_help };
static const cmdinfo_t fsblock_cmd =
{ "fsblock", "fsb", fsblock_f, 0, 1, 1, N_("[fsb]"),
N_("set address to fsblock value"), fsblock_help };
static const cmdinfo_t rtblock_cmd =
{ "rtblock", "rtbno", rtblock_f, 0, 1, 1, N_("[rtbno]"),
N_("set address to rtblock value"), rtblock_help };
static const cmdinfo_t rtextent_cmd =
{ "rtextent", "rtx", rtextent_f, 0, 1, 1, N_("[rtxno]"),
N_("set address to rtextent value"), rtextent_help };
static const cmdinfo_t logblock_cmd =
{ "logblock", "lsb", logblock_f, 0, 1, 1, N_("[logbno]"),
N_("set address to logblock value"), logblock_help };
static void
ablock_help(void)
{
dbprintf(_(
"\n Example:\n"
"\n"
" 'ablock 23' - sets the file position to the 23rd filesystem block in\n"
" the inode's attribute fork. The filesystem block size is specified in\n"
" the superblock.\n\n"
));
}
/*ARGSUSED*/
static int
ablock_f(
int argc,
char **argv)
{
bmap_ext_t bm;
xfs_fileoff_t bno;
xfs_fsblock_t dfsbno;
int haveattr;
xfs_extnum_t nex;
char *p;
struct xfs_dinode *dip = iocur_top->data;
bno = (xfs_fileoff_t)strtoull(argv[1], &p, 0);
if (*p != '\0') {
dbprintf(_("bad block number %s\n"), argv[1]);
return 0;
}
push_cur();
set_cur_inode(iocur_top->ino);
if (!dip) {
pop_cur();
dbprintf(_("no current inode\n"));
return 0;
}
haveattr = dip->di_forkoff;
pop_cur();
if (!haveattr) {
dbprintf(_("no attribute data for file\n"));
return 0;
}
nex = 1;
bmap(bno, 1, XFS_ATTR_FORK, &nex, &bm);
if (nex == 0) {
dbprintf(_("file attr block is unmapped\n"));
return 0;
}
dfsbno = bm.startblock + (bno - bm.startoff);
ASSERT(typtab[TYP_ATTR].typnm == TYP_ATTR);
set_cur(&typtab[TYP_ATTR], (int64_t)XFS_FSB_TO_DADDR(mp, dfsbno),
blkbb, DB_RING_ADD, NULL);
return 0;
}
void
block_init(void)
{
add_command(&ablock_cmd);
add_command(&daddr_cmd);
add_command(&dblock_cmd);
add_command(&fsblock_cmd);
add_command(&rtblock_cmd);
add_command(&rtextent_cmd);
add_command(&logblock_cmd);
}
static void
daddr_help(void)
{
dbprintf(_(
"\n Example:\n"
"\n"
" 'daddr 102' - sets position to the 102nd absolute disk block\n"
" (512 byte block).\n"
));
}
enum daddr_target {
DT_DATA,
DT_RT,
DT_LOG,
};
static int
daddr_f(
int argc,
char **argv)
{
xfs_rfsblock_t max_daddrs = mp->m_sb.sb_dblocks;
int64_t d;
char *p;
int bb_count = BTOBB(mp->m_sb.sb_sectsize);
int c;
enum daddr_target tgt = DT_DATA;
while ((c = getopt(argc, argv, "rl")) != -1) {
switch (c) {
case 'r':
tgt = DT_RT;
max_daddrs = mp->m_sb.sb_rblocks;
break;
case 'l':
tgt = DT_LOG;
max_daddrs = mp->m_sb.sb_logblocks;
break;
default:
daddr_help();
return 0;
}
}
if (tgt == DT_LOG && mp->m_sb.sb_logstart > 0) {
dbprintf(_("filesystem has internal log\n"));
return 0;
}
if (optind == argc) {
xfs_daddr_t daddr = iocur_top->off >> BBSHIFT;
if (iocur_is_ddev(iocur_top))
dbprintf(_("datadev daddr is %lld\n"), daddr);
else if (iocur_is_extlogdev(iocur_top))
dbprintf(_("logdev daddr is %lld\n"), daddr);
else if (iocur_is_rtdev(iocur_top))
dbprintf(_("rtdev daddr is %lld\n"), daddr);
else
dbprintf(_("current daddr is %lld\n"), daddr);
return 0;
}
if (optind != argc - 1) {
daddr_help();
return 0;
}
d = (int64_t)strtoull(argv[optind], &p, 0);
if (*p != '\0' ||
d >= max_daddrs << (mp->m_sb.sb_blocklog - BBSHIFT)) {
dbprintf(_("bad daddr %s\n"), argv[1]);
return 0;
}
ASSERT(typtab[TYP_DATA].typnm == TYP_DATA);
switch (tgt) {
case DT_DATA:
set_cur(&typtab[TYP_DATA], d, bb_count, DB_RING_ADD, NULL);
break;
case DT_RT:
set_rt_cur(&typtab[TYP_DATA], d, bb_count, DB_RING_ADD, NULL);
break;
case DT_LOG:
set_log_cur(&typtab[TYP_DATA], d, bb_count, DB_RING_ADD, NULL);
break;
}
return 0;
}
static void
dblock_help(void)
{
dbprintf(_(
"\n Example:\n"
"\n"
" 'dblock 23' - sets the file position to the 23rd filesystem block in\n"
" the inode's data fork. The filesystem block size is specified in the\n"
" superblock.\n\n"
));
}
static inline bool
is_rtfile(
struct xfs_dinode *dip)
{
return dip->di_flags & cpu_to_be16(XFS_DIFLAG_REALTIME);
}
static int
dblock_f(
int argc,
char **argv)
{
bbmap_t bbmap;
bmap_ext_t *bmp;
xfs_fileoff_t bno;
xfs_fsblock_t dfsbno;
int nb;
xfs_extnum_t nex;
char *p;
bool isrt;
typnm_t type;
bno = (xfs_fileoff_t)strtoull(argv[1], &p, 0);
if (*p != '\0') {
dbprintf(_("bad block number %s\n"), argv[1]);
return 0;
}
push_cur();
set_cur_inode(iocur_top->ino);
isrt = is_rtfile(iocur_top->data);
type = inode_next_type();
pop_cur();
if (type == TYP_NONE) {
dbprintf(_("no type for file data\n"));
return 0;
}
nex = nb = type == TYP_DIR2 ? mp->m_dir_geo->fsbcount : 1;
bmp = malloc(nb * sizeof(*bmp));
bmap(bno, nb, XFS_DATA_FORK, &nex, bmp);
if (nex == 0) {
dbprintf(_("file data block is unmapped\n"));
free(bmp);
return 0;
}
dfsbno = bmp->startblock + (bno - bmp->startoff);
ASSERT(typtab[type].typnm == type);
if (nex > 1)
make_bbmap(&bbmap, nex, bmp);
if (isrt)
set_rt_cur(&typtab[type], xfs_rtb_to_daddr(mp, dfsbno),
nb * blkbb, DB_RING_ADD,
nex > 1 ? &bbmap : NULL);
else
set_cur(&typtab[type], (int64_t)XFS_FSB_TO_DADDR(mp, dfsbno),
nb * blkbb, DB_RING_ADD,
nex > 1 ? &bbmap : NULL);
free(bmp);
return 0;
}
static void
fsblock_help(void)
{
dbprintf(_(
"\n Example:\n"
"\n"
" 'fsblock 1023' - sets the file position to the 1023rd filesystem block.\n"
" The filesystem block size is specified in the superblock and set during\n"
" mkfs time. Offset is absolute (not AG relative).\n\n"
));
}
static int
fsblock_f(
int argc,
char **argv)
{
xfs_agblock_t agbno;
xfs_agnumber_t agno;
xfs_fsblock_t d;
char *p;
if (argc == 1) {
if (!iocur_is_ddev(iocur_top)) {
dbprintf(_("cursor does not point to data device\n"));
return 0;
}
dbprintf(_("current fsblock is %lld\n"),
XFS_DADDR_TO_FSB(mp, iocur_top->off >> BBSHIFT));
return 0;
}
d = strtoull(argv[1], &p, 0);
if (*p != '\0') {
dbprintf(_("bad fsblock %s\n"), argv[1]);
return 0;
}
agno = XFS_FSB_TO_AGNO(mp, d);
agbno = XFS_FSB_TO_AGBNO(mp, d);
if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks) {
dbprintf(_("bad fsblock %s\n"), argv[1]);
return 0;
}
ASSERT(typtab[TYP_DATA].typnm == TYP_DATA);
set_cur(&typtab[TYP_DATA], XFS_AGB_TO_DADDR(mp, agno, agbno),
blkbb, DB_RING_ADD, NULL);
return 0;
}
static void
rtblock_help(void)
{
dbprintf(_(
"\n Example:\n"
"\n"
" 'rtblock 1023' - sets the file position to the 1023rd block on the realtime\n"
" volume. The filesystem block size is specified in the superblock and set\n"
" during mkfs time.\n\n"
));
}
static int
rtblock_f(
int argc,
char **argv)
{
xfs_rtblock_t rtbno;
char *p;
if (argc == 1) {
if (!iocur_is_rtdev(iocur_top)) {
dbprintf(_("cursor does not point to rt device\n"));
return 0;
}
dbprintf(_("current rtblock is %lld\n"),
xfs_daddr_to_rtb(mp, iocur_top->off >> BBSHIFT));
return 0;
}
rtbno = strtoull(argv[1], &p, 0);
if (*p != '\0') {
dbprintf(_("bad rtblock %s\n"), argv[1]);
return 0;
}
if (xfs_has_rtgroups(mp)) {
xfs_rgnumber_t rgno = xfs_rtb_to_rgno(mp, rtbno);
xfs_rgblock_t rgbno = xfs_rtb_to_rgbno(mp, rtbno);
if (rgno >= mp->m_sb.sb_rgcount ||
rgbno >= mp->m_sb.sb_rgextents * mp->m_sb.sb_rextsize) {
dbprintf(_("bad rtblock %s\n"), argv[1]);
return 0;
}
} else {
if (rtbno >= mp->m_sb.sb_rblocks) {
dbprintf(_("bad rtblock %s\n"), argv[1]);
return 0;
}
}
ASSERT(typtab[TYP_DATA].typnm == TYP_DATA);
set_rt_cur(&typtab[TYP_DATA], xfs_rtb_to_daddr(mp, rtbno), blkbb,
DB_RING_ADD, NULL);
return 0;
}
static void
rtextent_help(void)
{
dbprintf(_(
"\n Example:\n"
"\n"
" 'rtextent 10' - sets the file position to the 10th extent on the realtime\n"
" volume. The realtime extent size is specified in the superblock and set\n"
" during mkfs or growfs time.\n\n"
));
}
/*
* Move the cursor to a specific location on the realtime block device given
* a linear address in units of realtime extents.
*
* NOTE: The user interface assumes a global RT extent number, while the
* in-kernel rtx is per-RTG now, thus the odd conversions here.
*/
static int
rtextent_f(
int argc,
char **argv)
{
uint64_t rfsbno;
uint64_t rtx;
char *p;
if (argc == 1) {
if (!iocur_is_rtdev(iocur_top)) {
dbprintf(_("cursor does not point to rt device\n"));
return 0;
}
rfsbno = XFS_BB_TO_FSB(mp, iocur_top->off >> BBSHIFT);
dbprintf(_("current rtextent is %lld\n"),
xfs_blen_to_rtbxlen(mp, rfsbno));
return 0;
}
rtx = strtoull(argv[1], &p, 0);
if (*p != '\0') {
dbprintf(_("bad rtextent %s\n"), argv[1]);
return 0;
}
if (rtx >= mp->m_sb.sb_rextents) {
dbprintf(_("bad rtextent %s\n"), argv[1]);
return 0;
}
rfsbno = xfs_rtbxlen_to_blen(mp, rtx);
ASSERT(typtab[TYP_DATA].typnm == TYP_DATA);
set_rt_cur(&typtab[TYP_DATA], XFS_FSB_TO_BB(mp, rfsbno),
mp->m_sb.sb_rextsize * blkbb, DB_RING_ADD, NULL);
return 0;
}
static void
logblock_help(void)
{
dbprintf(_(
"\n Example:\n"
"\n"
" 'logblock 1023' - sets the file position to the 1023rd log block.\n"
" The external log device or the block offset within the internal log will be\n"
" chosen as appropriate.\n"
));
}
static int
logblock_f(
int argc,
char **argv)
{
xfs_fsblock_t logblock;
char *p;
if (argc == 1) {
if (mp->m_sb.sb_logstart > 0 && iocur_is_ddev(iocur_top)) {
logblock = XFS_DADDR_TO_FSB(mp,
iocur_top->off >> BBSHIFT);
if (logblock < mp->m_sb.sb_logstart ||
logblock >= mp->m_sb.sb_logstart +
mp->m_sb.sb_logblocks) {
dbprintf(
_("current address not within internal log\n"));
return 0;
}
dbprintf(_("current logblock is %lld\n"),
logblock - mp->m_sb.sb_logstart);
return 0;
}
if (mp->m_sb.sb_logstart == 0 &&
iocur_is_extlogdev(iocur_top)) {
logblock = XFS_BB_TO_FSB(mp,
iocur_top->off >> BBSHIFT);
if (logblock >= mp->m_sb.sb_logblocks) {
dbprintf(
_("current address not within external log\n"));
return 0;
}
dbprintf(_("current logblock is %lld\n"), logblock);
return 0;
}
dbprintf(_("current address does not point to log\n"));
return 0;
}
logblock = strtoull(argv[1], &p, 0);
if (*p != '\0') {
dbprintf(_("bad logblock %s\n"), argv[1]);
return 0;
}
if (logblock >= mp->m_sb.sb_logblocks) {
dbprintf(_("bad logblock %s\n"), argv[1]);
return 0;
}
ASSERT(typtab[TYP_DATA].typnm == TYP_DATA);
if (mp->m_sb.sb_logstart) {
logblock += mp->m_sb.sb_logstart;
set_cur(&typtab[TYP_DATA], XFS_FSB_TO_DADDR(mp, logblock),
blkbb, DB_RING_ADD, NULL);
} else {
set_log_cur(&typtab[TYP_DATA], XFS_FSB_TO_BB(mp, logblock),
blkbb, DB_RING_ADD, NULL);
}
return 0;
}
void
print_block(
const field_t *fields,
int argc,
char **argv)
{
print_rawdata(iocur_top->data, iocur_top->len);
}
static void
print_rawdata(
void *data,
int len)
{
int i;
int j;
int lastaddr;
int offchars;
unsigned char *p;
lastaddr = (len - 1) & ~(32 - 1);
if (lastaddr < 0x10)
offchars = 1;
else if (lastaddr < 0x100)
offchars = 2;
else if (lastaddr < 0x1000)
offchars = 3;
else
offchars = 4;
for (i = 0, p = data; i < len; i += 32) {
dbprintf("%-0*.*x:", offchars, offchars, i);
for (j = 0; j < 32 && i + j < len; j++, p++) {
if ((j & 3) == 0)
dbprintf(" ");
dbprintf("%02x", *p);
}
dbprintf("\n");
}
}