blob: 3014367e7d7652ca82a39ac78d181cbe8555897b [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 "output.h"
#include "init.h"
#include "block.h"
#define M(A) (1 << CT_ ## A)
#define agblock_to_bytes(x) \
((uint64_t)(x) << mp->m_sb.sb_blocklog)
#define agino_to_bytes(x) \
((uint64_t)(x) << mp->m_sb.sb_inodelog)
#define agnumber_to_bytes(x) \
agblock_to_bytes((uint64_t)(x) * mp->m_sb.sb_agblocks)
#define daddr_to_bytes(x) \
((uint64_t)(x) << BBSHIFT)
#define fsblock_to_bytes(x) \
(agnumber_to_bytes(XFS_FSB_TO_AGNO(mp, (x))) + \
agblock_to_bytes(XFS_FSB_TO_AGBNO(mp, (x))))
#define ino_to_bytes(x) \
(agnumber_to_bytes(XFS_INO_TO_AGNO(mp, (x))) + \
agino_to_bytes(XFS_INO_TO_AGINO(mp, (x))))
#define inoidx_to_bytes(x) \
((uint64_t)(x) << mp->m_sb.sb_inodelog)
#define rtblock_to_bytes(x) \
((uint64_t)(x) << mp->m_sb.sb_blocklog)
#define rtx_to_rtblock(x) \
((uint64_t)(x) * mp->m_sb.sb_rextsize)
#define rbmblock_to_bytes(x) \
rtblock_to_bytes(rtx_to_rtblock(xfs_rbmblock_to_rtx(mp, (uint64_t)x)))
#define rbmword_to_bytes(x) \
rtblock_to_bytes(rtx_to_rtblock((uint64_t)(x) << XFS_NBWORDLOG))
typedef enum {
CT_NONE = -1,
CT_AGBLOCK, /* xfs_agblock_t */
CT_AGINO, /* xfs_agino_t */
CT_AGNUMBER, /* xfs_agno_t */
CT_BBOFF, /* byte offset in daddr */
CT_BLKOFF, /* byte offset in fsb/agb */
CT_BYTE, /* byte in filesystem */
CT_DADDR, /* daddr_t */
CT_FSBLOCK, /* xfs_fsblock_t */
CT_INO, /* xfs_ino_t */
CT_INOIDX, /* index of inode in fsblock */
CT_INOOFF, /* byte offset in inode */
CT_RTBLOCK, /* realtime block */
CT_RTX, /* realtime extent */
CT_RBMBLOCK, /* block within rt bitmap */
CT_RBMWORD, /* word within rt bitmap */
CT_RSUMBLOCK, /* block within rt summary */
CT_RSUMLOG, /* log level for rtsummary computations */
CT_RSUMINFO, /* info word within rt summary */
NCTS
} ctype_t;
typedef struct ctydesc {
int allowed;
const char **names;
} ctydesc_t;
typedef union {
xfs_agblock_t agblock;
xfs_agino_t agino;
xfs_agnumber_t agnumber;
int bboff;
int blkoff;
uint64_t byte;
xfs_daddr_t daddr;
xfs_fsblock_t fsblock;
xfs_ino_t ino;
int inoidx;
int inooff;
xfs_rtblock_t rtblock;
xfs_rtblock_t rtx;
xfs_fileoff_t rbmblock;
unsigned int rbmword;
xfs_fileoff_t rsumblock;
} cval_t;
static uint64_t bytevalue(ctype_t ctype, cval_t *val);
static int rtconvert_f(int argc, char **argv);
static int convert_f(int argc, char **argv);
static int getvalue(char *s, ctype_t ctype, cval_t *val);
static ctype_t lookupcty(const struct ctydesc *descs,
const char *ctyname);
static const char *agblock_names[] = { "agblock", "agbno", NULL };
static const char *agino_names[] = { "agino", "aginode", NULL };
static const char *agnumber_names[] = { "agnumber", "agno", NULL };
static const char *bboff_names[] = { "bboff", "daddroff", NULL };
static const char *blkoff_names[] = { "blkoff", "fsboff", "agboff",
NULL };
static const char *rtblkoff_names[] = { "blkoff", "rtboff",
NULL };
static const char *byte_names[] = { "byte", "fsbyte", NULL };
static const char *daddr_names[] = { "daddr", "bb", NULL };
static const char *fsblock_names[] = { "fsblock", "fsb", "fsbno", NULL };
static const char *ino_names[] = { "ino", "inode", NULL };
static const char *inoidx_names[] = { "inoidx", "offset", NULL };
static const char *inooff_names[] = { "inooff", "inodeoff", NULL };
static const char *rtblock_names[] = { "rtblock", "rtb", "rtbno", NULL };
static const char *rtx_names[] = { "rtx", "rtextent", NULL };
static const char *rbmblock_names[] = { "rbmblock", "rbmb", NULL };
static const char *rbmword_names[] = { "rbmword", "rbmw", NULL };
static const char *rsumblock_names[] = { "rsumblock", "rsmb", NULL };
static const char *rsumlog_names[] = { "rsumlog", "rsml", NULL };
static const char *rsumword_names[] = { "rsuminfo", "rsmi", NULL };
static int rsuminfo;
static int rsumlog;
static const ctydesc_t ctydescs[NCTS] = {
[CT_AGBLOCK] = {
.allowed = M(AGNUMBER) |
M(BBOFF) |
M(BLKOFF) |
M(INOIDX) |
M(INOOFF),
.names = agblock_names,
},
[CT_AGINO] = {
.allowed = M(AGNUMBER) |
M(INOOFF),
.names = agino_names,
},
[CT_AGNUMBER] = {
.allowed = M(AGBLOCK) |
M(AGINO) |
M(BBOFF) |
M(BLKOFF) |
M(INOIDX) |
M(INOOFF),
.names = agnumber_names,
},
[CT_BBOFF] = {
.allowed = M(AGBLOCK) |
M(AGNUMBER) |
M(DADDR) |
M(FSBLOCK),
.names = bboff_names,
},
[CT_BLKOFF] = {
.allowed = M(AGBLOCK) |
M(AGNUMBER) |
M(FSBLOCK),
.names = blkoff_names,
},
[CT_BYTE] = {
.allowed = 0,
.names = byte_names,
},
[CT_DADDR] = {
.allowed = M(BBOFF),
.names = daddr_names,
},
[CT_FSBLOCK] = {
.allowed = M(BBOFF) |
M(BLKOFF) |
M(INOIDX),
.names = fsblock_names,
},
[CT_INO] = {
.allowed = M(INOOFF),
.names = ino_names,
},
[CT_INOIDX] = {
.allowed = M(AGBLOCK) |
M(AGNUMBER) |
M(FSBLOCK) |
M(INOOFF),
.names = inoidx_names,
},
[CT_INOOFF] = {
.allowed = M(AGBLOCK) |
M(AGINO) |
M(AGNUMBER) |
M(FSBLOCK) |
M(INO) |
M(INOIDX),
.names = inooff_names,
},
};
static const ctydesc_t ctydescs_rt[NCTS] = {
[CT_BBOFF] = {
.allowed = M(DADDR) |
M(RTBLOCK) |
M(RSUMLOG),
.names = bboff_names,
},
[CT_BLKOFF] = {
.allowed = M(RTBLOCK) |
M(RSUMLOG),
.names = rtblkoff_names,
},
[CT_BYTE] = {
.allowed = M(RSUMLOG),
.names = byte_names,
},
[CT_DADDR] = {
.allowed = M(BBOFF) |
M(RSUMLOG),
.names = daddr_names,
},
[CT_RTBLOCK] = {
.allowed = M(BBOFF) |
M(BLKOFF) |
M(RSUMLOG),
.names = rtblock_names,
},
[CT_RTX] = {
.allowed = M(BBOFF) |
M(BLKOFF) |
M(RSUMLOG),
.names = rtx_names,
},
[CT_RBMBLOCK] = {
.allowed = M(RBMWORD) |
M(RSUMLOG),
.names = rbmblock_names,
},
[CT_RBMWORD] = {
.allowed = M(RBMBLOCK) |
M(RSUMLOG),
.names = rbmword_names,
},
/* must be specified in order rsumlog -> rsuminfo -> rsumblock */
[CT_RSUMBLOCK] = {
.allowed = 0,
.names = rsumblock_names,
},
[CT_RSUMLOG] = {
.allowed = M(RSUMINFO) |
M(RSUMBLOCK),
.names = rsumlog_names,
},
[CT_RSUMINFO] = {
.allowed = M(RSUMBLOCK),
.names = rsumword_names,
},
};
static const cmdinfo_t convert_cmd =
{ "convert", NULL, convert_f, 3, 9, 0, "type num [type num]... type",
"convert from one address form to another", NULL };
static const cmdinfo_t rtconvert_cmd =
{ "rtconvert", NULL, rtconvert_f, 3, 9, 0, "type num [type num]... type",
"convert from one realtime address form to another", NULL };
static inline uint64_t
rsumblock_to_bytes(
xfs_fileoff_t rsumblock)
{
/*
* We compute the rt summary file block with this formula:
* sumoffs = (log2len * sb_rbmblocks) + rbmblock;
* sumblock = sumoffs / blockwsize;
*
* Hence the return value is the inverse of this:
* sumoffs = (rsumblock * blockwsize) + rsuminfo;
* rbmblock = sumoffs % (log2len * sb_rbmblocks);
*/
xfs_rtsumoff_t sumoff;
xfs_fileoff_t rbmblock;
if (rsumlog < 0) {
dbprintf(_("need to set rsumlog\n"));
return 0;
}
if (rsuminfo < 0) {
dbprintf(_("need to set rsuminfo\n"));
return 0;
}
sumoff = rsuminfo + (rsumblock * mp->m_blockwsize);
if (rsumlog)
rbmblock = sumoff % (rsumlog * mp->m_sb.sb_rbmblocks);
else
rbmblock = sumoff;
return rbmblock_to_bytes(rbmblock);
}
static uint64_t
bytevalue(ctype_t ctype, cval_t *val)
{
switch (ctype) {
case CT_AGBLOCK:
return agblock_to_bytes(val->agblock);
case CT_AGINO:
return agino_to_bytes(val->agino);
case CT_AGNUMBER:
return agnumber_to_bytes(val->agnumber);
case CT_BBOFF:
return (uint64_t)val->bboff;
case CT_BLKOFF:
return (uint64_t)val->blkoff;
case CT_BYTE:
return val->byte;
case CT_DADDR:
return daddr_to_bytes(val->daddr);
case CT_FSBLOCK:
return fsblock_to_bytes(val->fsblock);
case CT_INO:
return ino_to_bytes(val->ino);
case CT_INOIDX:
return inoidx_to_bytes(val->inoidx);
case CT_INOOFF:
return (uint64_t)val->inooff;
case CT_RTBLOCK:
return rtblock_to_bytes(val->rtblock);
case CT_RTX:
return rtblock_to_bytes(rtx_to_rtblock(val->rtx));
case CT_RBMBLOCK:
return rbmblock_to_bytes(val->rbmblock);
case CT_RBMWORD:
return rbmword_to_bytes(val->rbmword);
case CT_RSUMBLOCK:
return rsumblock_to_bytes(val->rbmblock);
case CT_RSUMLOG:
case CT_RSUMINFO:
/*
* These have to specified before rsumblock, and are stored in
* global variables. Hence they do not adjust the disk address
* value.
*/
return 0;
case CT_NONE:
case NCTS:
break;
}
/* NOTREACHED */
return 0;
}
static int
convert_f(int argc, char **argv)
{
ctype_t c;
int conmask;
cval_t cvals[NCTS] = {};
int i;
int mask;
uint64_t v;
ctype_t wtype;
/* move past the "convert" command */
argc--;
argv++;
if ((argc % 2) != 1) {
dbprintf(_("bad argument count %d to convert, expected 3,5,7,9 "
"arguments\n"), argc);
return 0;
}
if ((wtype = lookupcty(ctydescs, argv[argc - 1])) == CT_NONE) {
dbprintf(_("unknown conversion type %s\n"), argv[argc - 1]);
return 0;
}
for (i = mask = conmask = 0; i < (argc - 1) / 2; i++) {
c = lookupcty(ctydescs, argv[i * 2]);
if (c == CT_NONE) {
dbprintf(_("unknown conversion type %s\n"), argv[i * 2]);
return 0;
}
if (c == wtype) {
dbprintf(_("result type same as argument\n"));
return 0;
}
if (conmask & (1 << c)) {
dbprintf(_("conflicting conversion type %s\n"),
argv[i * 2]);
return 0;
}
if (!getvalue(argv[i * 2 + 1], c, &cvals[c]))
return 0;
mask |= 1 << c;
conmask |= ~ctydescs[c].allowed;
}
if (cur_agno != NULLAGNUMBER && (conmask & M(AGNUMBER)) == 0) {
cvals[CT_AGNUMBER].agnumber = cur_agno;
mask |= M(AGNUMBER);
}
v = 0;
for (c = (ctype_t)0; c < NCTS; c++) {
if (!(mask & (1 << c)))
continue;
v += bytevalue(c, &cvals[c]);
}
switch (wtype) {
case CT_AGBLOCK:
v = xfs_daddr_to_agbno(mp, v >> BBSHIFT);
break;
case CT_AGINO:
v = (v >> mp->m_sb.sb_inodelog) %
XFS_AGB_TO_AGINO(mp, mp->m_sb.sb_agblocks);
break;
case CT_AGNUMBER:
v = xfs_daddr_to_agno(mp, v >> BBSHIFT);
break;
case CT_BBOFF:
v &= BBMASK;
break;
case CT_BLKOFF:
v &= mp->m_blockmask;
break;
case CT_BYTE:
break;
case CT_DADDR:
v >>= BBSHIFT;
break;
case CT_FSBLOCK:
v = XFS_DADDR_TO_FSB(mp, v >> BBSHIFT);
break;
case CT_INO:
v = XFS_AGINO_TO_INO(mp, xfs_daddr_to_agno(mp, v >> BBSHIFT),
(v >> mp->m_sb.sb_inodelog) %
XFS_AGB_TO_AGINO(mp, mp->m_sb.sb_agblocks));
break;
case CT_INOIDX:
v = (v >> mp->m_sb.sb_inodelog) & (mp->m_sb.sb_inopblock - 1);
break;
case CT_INOOFF:
v &= mp->m_sb.sb_inodesize - 1;
break;
case CT_RTBLOCK:
case CT_RTX:
case CT_RBMBLOCK:
case CT_RBMWORD:
case CT_RSUMBLOCK:
case CT_RSUMLOG:
case CT_RSUMINFO:
/* shouldn't get here */
ASSERT(0);
break;
case CT_NONE:
case NCTS:
/* NOTREACHED */
break;
}
dbprintf("0x%llx (%llu)\n", v, v);
return 0;
}
static inline uint64_t
rt_daddr_to_rsumblock(
struct xfs_mount *mp,
uint64_t input)
{
xfs_rtblock_t rtbno;
xfs_rtxnum_t rtx;
xfs_fileoff_t rbmblock;
xfs_rtsumoff_t rsumoff;
if (rsumlog < 0) {
dbprintf(_("need to set rsumlog\n"));
return 0;
}
rtbno = xfs_daddr_to_rtb(mp, input >> BBSHIFT);
rtx = xfs_rtb_to_rtx(mp, rtbno);
rbmblock = xfs_rtx_to_rbmblock(mp, rtx);
rsumoff = xfs_rtsumoffs(mp, rsumlog, rbmblock);
return xfs_rtsumoffs_to_block(mp, rsumoff);
}
static inline uint64_t
rt_daddr_to_rsuminfo(
struct xfs_mount *mp,
uint64_t input)
{
xfs_rtblock_t rtbno;
xfs_rtxnum_t rtx;
xfs_fileoff_t rbmblock;
xfs_rtsumoff_t rsumoff;
if (rsumlog < 0) {
dbprintf(_("need to set rsumlog\n"));
return 0;
}
rtbno = xfs_daddr_to_rtb(mp, input >> BBSHIFT);
rtx = xfs_rtb_to_rtx(mp, rtbno);
rbmblock = xfs_rtx_to_rbmblock(mp, rtx);
rsumoff = xfs_rtsumoffs(mp, rsumlog, rbmblock);
return xfs_rtsumoffs_to_infoword(mp, rsumoff);
}
static int
rtconvert_f(int argc, char **argv)
{
ctype_t c;
int conmask;
cval_t cvals[NCTS] = {};
int i;
int mask;
uint64_t v;
ctype_t wtype;
rsumlog = -1;
rsuminfo = -1;
/* move past the "rtconvert" command */
argc--;
argv++;
if ((argc % 2) != 1) {
dbprintf(_("bad argument count %d to rtconvert, expected 3,5,7,9 "
"arguments\n"), argc);
return 0;
}
if ((wtype = lookupcty(ctydescs_rt, argv[argc - 1])) == CT_NONE) {
dbprintf(_("unknown conversion type %s\n"), argv[argc - 1]);
return 0;
}
for (i = mask = conmask = 0; i < (argc - 1) / 2; i++) {
c = lookupcty(ctydescs_rt, argv[i * 2]);
if (c == CT_NONE) {
dbprintf(_("unknown conversion type %s\n"), argv[i * 2]);
return 0;
}
if (c == wtype) {
dbprintf(_("result type same as argument\n"));
return 0;
}
if (conmask & (1 << c)) {
dbprintf(_("conflicting conversion type %s\n"),
argv[i * 2]);
return 0;
}
if (!getvalue(argv[i * 2 + 1], c, &cvals[c]))
return 0;
mask |= 1 << c;
conmask |= ~ctydescs_rt[c].allowed;
}
v = 0;
for (c = (ctype_t)0; c < NCTS; c++) {
if (!(mask & (1 << c)))
continue;
v += bytevalue(c, &cvals[c]);
}
switch (wtype) {
case CT_BBOFF:
v &= BBMASK;
break;
case CT_BLKOFF:
v &= mp->m_blockmask;
break;
case CT_BYTE:
break;
case CT_DADDR:
v >>= BBSHIFT;
break;
case CT_RTBLOCK:
v = xfs_daddr_to_rtb(mp, v >> BBSHIFT);
break;
case CT_RTX:
v = xfs_daddr_to_rtb(mp, v >> BBSHIFT) / mp->m_sb.sb_rextsize;
break;
case CT_RBMBLOCK:
v = xfs_rtx_to_rbmblock(mp,
xfs_rtb_to_rtx(mp,
xfs_daddr_to_rtb(mp, v >> BBSHIFT)));
break;
case CT_RBMWORD:
v = xfs_rtx_to_rbmword(mp,
xfs_rtb_to_rtx(mp,
xfs_daddr_to_rtb(mp, v >> BBSHIFT)));
break;
case CT_RSUMBLOCK:
v = rt_daddr_to_rsumblock(mp, v);
break;
case CT_RSUMLOG:
dbprintf(_("cannot convert to rsumlog\n"));
return 0;
break;
case CT_RSUMINFO:
v = rt_daddr_to_rsuminfo(mp, v);
break;
case CT_AGBLOCK:
case CT_AGINO:
case CT_AGNUMBER:
case CT_FSBLOCK:
case CT_INO:
case CT_INOIDX:
case CT_INOOFF:
/* shouldn't get here */
ASSERT(0);
break;
case CT_NONE:
case NCTS:
/* NOTREACHED */
break;
}
dbprintf("0x%llx (%llu)\n", v, v);
return 0;
}
void
convert_init(void)
{
add_command(&convert_cmd);
add_command(&rtconvert_cmd);
}
static int
getvalue(char *s, ctype_t ctype, cval_t *val)
{
char *p;
uint64_t v;
v = strtoull(s, &p, 0);
if (*p != '\0') {
dbprintf(_("%s is not a number\n"), s);
return 0;
}
switch (ctype) {
case CT_AGBLOCK:
val->agblock = (xfs_agblock_t)v;
break;
case CT_AGINO:
val->agino = (xfs_agino_t)v;
break;
case CT_AGNUMBER:
val->agnumber = (xfs_agnumber_t)v;
break;
case CT_BBOFF:
val->bboff = (int)v;
break;
case CT_BLKOFF:
val->blkoff = (int)v;
break;
case CT_BYTE:
val->byte = (uint64_t)v;
break;
case CT_DADDR:
val->daddr = (xfs_daddr_t)v;
break;
case CT_FSBLOCK:
val->fsblock = (xfs_fsblock_t)v;
break;
case CT_INO:
val->ino = (xfs_ino_t)v;
break;
case CT_INOIDX:
val->inoidx = (int)v;
break;
case CT_INOOFF:
val->inooff = (int)v;
break;
case CT_RTBLOCK:
val->rtblock = (xfs_rtblock_t)v;
break;
case CT_RTX:
val->rtx = (xfs_rtblock_t)v;
break;
case CT_RBMBLOCK:
val->rbmblock = (xfs_fileoff_t)v;
break;
case CT_RBMWORD:
val->rbmword = (unsigned int)v;
break;
case CT_RSUMBLOCK:
val->rsumblock = (xfs_fileoff_t)v;
break;
case CT_RSUMLOG:
rsumlog = (unsigned int)v;
break;
case CT_RSUMINFO:
rsuminfo = (unsigned int)v;
break;
case CT_NONE:
case NCTS:
/* NOTREACHED */
break;
}
return 1;
}
static ctype_t
lookupcty(
const struct ctydesc *descs,
const char *ctyname)
{
ctype_t cty;
const char **name;
for (cty = (ctype_t)0; cty < NCTS; cty++) {
for (name = descs[cty].names; name && *name; name++) {
if (strcmp(ctyname, *name) == 0)
return cty;
}
}
return CT_NONE;
}