blob: a9ad87ca183fed645c2eb059ac7ca7b520b99c07 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*/
#include "xfs.h"
#include "platform_defs.h"
#include "command.h"
#include "init.h"
#include "libfrog/logging.h"
#include "libfrog/fsgeom.h"
#include "libfrog/bulkstat.h"
#include "libfrog/paths.h"
#include "io.h"
#include "input.h"
static void
dump_bulkstat_time(
const char *tag,
uint64_t sec,
uint32_t nsec)
{
printf("\t%s = %"PRIu64".%"PRIu32"\n", tag, sec, nsec);
}
static void
dump_bulkstat(
struct xfs_bulkstat *bstat)
{
printf("bs_ino = %"PRIu64"\n", bstat->bs_ino);
printf("\tbs_size = %"PRIu64"\n", bstat->bs_size);
printf("\tbs_blocks = %"PRIu64"\n", bstat->bs_blocks);
printf("\tbs_xflags = 0x%"PRIx64"\n", bstat->bs_xflags);
dump_bulkstat_time("bs_atime", bstat->bs_atime, bstat->bs_atime_nsec);
dump_bulkstat_time("bs_ctime", bstat->bs_ctime, bstat->bs_ctime_nsec);
dump_bulkstat_time("bs_mtime", bstat->bs_mtime, bstat->bs_mtime_nsec);
dump_bulkstat_time("bs_btime", bstat->bs_btime, bstat->bs_btime_nsec);
printf("\tbs_gen = 0x%"PRIx32"\n", bstat->bs_gen);
printf("\tbs_uid = %"PRIu32"\n", bstat->bs_uid);
printf("\tbs_gid = %"PRIu32"\n", bstat->bs_gid);
printf("\tbs_projectid = %"PRIu32"\n", bstat->bs_projectid);
printf("\tbs_blksize = %"PRIu32"\n", bstat->bs_blksize);
printf("\tbs_rdev = %"PRIu32"\n", bstat->bs_rdev);
printf("\tbs_cowextsize_blks = %"PRIu32"\n", bstat->bs_cowextsize_blks);
printf("\tbs_extsize_blks = %"PRIu32"\n", bstat->bs_extsize_blks);
printf("\tbs_nlink = %"PRIu32"\n", bstat->bs_nlink);
printf("\tbs_extents = %"PRIu32"\n", bstat->bs_extents);
printf("\tbs_aextents = %"PRIu32"\n", bstat->bs_aextents);
printf("\tbs_version = %"PRIu16"\n", bstat->bs_version);
printf("\tbs_forkoff = %"PRIu16"\n", bstat->bs_forkoff);
printf("\tbs_sick = 0x%"PRIx16"\n", bstat->bs_sick);
printf("\tbs_checked = 0x%"PRIx16"\n", bstat->bs_checked);
printf("\tbs_mode = 0%"PRIo16"\n", bstat->bs_mode);
printf("\tbs_extents64 = %"PRIu64"\n", bstat->bs_extents64);
};
static void
bulkstat_help(void)
{
printf(_(
"Bulk-queries the filesystem for inode stat information and prints it.\n"
"\n"
" -a <agno> Only iterate this AG.\n"
" -d Print debugging output.\n"
" -q Be quiet, no output.\n"
" -e <ino> Stop after this inode.\n"
" -n <nr> Ask for this many results at once.\n"
" -s <ino> Inode to start with.\n"
" -v <ver> Use this version of the ioctl (1 or 5).\n"));
}
static void
set_xfd_flags(
struct xfs_fd *xfd,
uint32_t ver)
{
switch (ver) {
case 1:
xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V1;
break;
case 5:
xfd->flags |= XFROG_FLAG_BULKSTAT_FORCE_V5;
break;
default:
break;
}
}
static int
bulkstat_f(
int argc,
char **argv)
{
struct xfs_fd xfd = XFS_FD_INIT(file->fd);
struct xfs_bulkstat_req *breq;
uint64_t startino = 0;
uint64_t endino = -1ULL;
uint32_t batch_size = 4096;
uint32_t agno = 0;
uint32_t ver = 0;
bool has_agno = false;
bool debug = false;
bool quiet = false;
unsigned int i;
int c;
int ret;
while ((c = getopt(argc, argv, "a:de:n:qs:v:")) != -1) {
switch (c) {
case 'a':
agno = cvt_u32(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
has_agno = true;
break;
case 'd':
debug = true;
break;
case 'e':
endino = cvt_u64(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
break;
case 'n':
batch_size = cvt_u32(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
break;
case 'q':
quiet = true;
break;
case 's':
startino = cvt_u64(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
break;
case 'v':
ver = cvt_u32(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
if (ver != 1 && ver != 5) {
fprintf(stderr, "version must be 1 or 5.\n");
return 1;
}
break;
default:
bulkstat_help();
return 0;
}
}
if (optind != argc) {
bulkstat_help();
return 0;
}
ret = -xfd_prepare_geometry(&xfd);
if (ret) {
xfrog_perror(ret, "xfd_prepare_geometry");
exitcode = 1;
return 0;
}
ret = -xfrog_bulkstat_alloc_req(batch_size, startino, &breq);
if (ret) {
xfrog_perror(ret, "alloc bulkreq");
exitcode = 1;
return 0;
}
if (has_agno)
xfrog_bulkstat_set_ag(breq, agno);
set_xfd_flags(&xfd, ver);
while ((ret = -xfrog_bulkstat(&xfd, breq)) == 0) {
if (debug)
printf(
_("bulkstat: startino=%lld flags=0x%x agno=%u ret=%d icount=%u ocount=%u\n"),
(long long)breq->hdr.ino,
(unsigned int)breq->hdr.flags,
(unsigned int)breq->hdr.agno,
ret,
(unsigned int)breq->hdr.icount,
(unsigned int)breq->hdr.ocount);
if (breq->hdr.ocount == 0)
break;
for (i = 0; i < breq->hdr.ocount; i++) {
if (breq->bulkstat[i].bs_ino > endino)
break;
if (quiet)
continue;
dump_bulkstat(&breq->bulkstat[i]);
}
}
if (ret) {
xfrog_perror(ret, "xfrog_bulkstat");
exitcode = 1;
}
free(breq);
return 0;
}
static void
bulkstat_single_help(void)
{
printf(_(
"Queries the filesystem for a single inode's stat information and prints it.\n"
"If a given inode is not allocated, information about the next allocated \n"
"inode will be printed instead.\n"
"\n"
" -v (ver) Use this version of the ioctl (1 or 5).\n"
" -d Print debugging information.\n"
"\n"
"Pass in inode numbers or a special inode name:\n"
" root Root directory.\n"));
}
struct single_map {
const char *tag;
uint64_t code;
};
struct single_map tags[] = {
{"root", XFS_BULK_IREQ_SPECIAL_ROOT},
{NULL, 0},
};
static int
bulkstat_single_f(
int argc,
char **argv)
{
struct xfs_fd xfd = XFS_FD_INIT(file->fd);
struct xfs_bulkstat bulkstat;
unsigned long ver = 0;
unsigned int i;
bool debug = false;
int c;
int ret;
while ((c = getopt(argc, argv, "dv:")) != -1) {
switch (c) {
case 'd':
debug = true;
break;
case 'v':
errno = 0;
ver = strtoull(optarg, NULL, 10);
if (errno) {
perror(optarg);
return 1;
}
if (ver != 1 && ver != 5) {
fprintf(stderr, "version must be 1 or 5.\n");
return 1;
}
break;
default:
bulkstat_single_help();
return 0;
}
}
ret = -xfd_prepare_geometry(&xfd);
if (ret) {
xfrog_perror(ret, "xfd_prepare_geometry");
exitcode = 1;
return 0;
}
set_xfd_flags(&xfd, ver);
for (i = optind; i < argc; i++) {
struct single_map *sm = tags;
uint64_t ino;
unsigned int flags = 0;
/* Try to look up our tag... */
for (sm = tags; sm->tag; sm++) {
if (!strcmp(argv[i], sm->tag)) {
ino = sm->code;
flags |= XFS_BULK_IREQ_SPECIAL;
break;
}
}
/* ...or else it's an inode number. */
if (sm->tag == NULL) {
errno = 0;
ino = strtoull(argv[i], NULL, 10);
if (errno) {
perror(argv[i]);
exitcode = 1;
return 0;
}
}
ret = -xfrog_bulkstat_single(&xfd, ino, flags, &bulkstat);
if (ret) {
xfrog_perror(ret, "xfrog_bulkstat_single");
continue;
}
if (debug)
printf(
_("bulkstat_single: startino=%"PRIu64" flags=0x%"PRIx32" ret=%d\n"),
ino, flags, ret);
dump_bulkstat(&bulkstat);
}
return 0;
}
static void
dump_inumbers(
struct xfs_inumbers *inumbers)
{
printf("xi_startino = %"PRIu64"\n", inumbers->xi_startino);
printf("\txi_allocmask = 0x%"PRIx64"\n", inumbers->xi_allocmask);
printf("\txi_alloccount = %"PRIu8"\n", inumbers->xi_alloccount);
printf("\txi_version = %"PRIu8"\n", inumbers->xi_version);
}
static void
inumbers_help(void)
{
printf(_(
"Queries the filesystem for inode group information and prints it.\n"
"\n"
" -a <agno> Only iterate this AG.\n"
" -d Print debugging output.\n"
" -e <ino> Stop after this inode.\n"
" -n <nr> Ask for this many results at once.\n"
" -s <ino> Inode to start with.\n"
" -v <ver> Use this version of the ioctl (1 or 5).\n"));
}
static int
inumbers_f(
int argc,
char **argv)
{
struct xfs_fd xfd = XFS_FD_INIT(file->fd);
struct xfs_inumbers_req *ireq;
uint64_t startino = 0;
uint64_t endino = -1ULL;
uint32_t batch_size = 4096;
uint32_t agno = 0;
uint32_t ver = 0;
bool has_agno = false;
bool debug = false;
unsigned int i;
int c;
int ret;
while ((c = getopt(argc, argv, "a:de:n:s:v:")) != -1) {
switch (c) {
case 'a':
agno = cvt_u32(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
has_agno = true;
break;
case 'd':
debug = true;
break;
case 'e':
endino = cvt_u64(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
break;
case 'n':
batch_size = cvt_u32(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
break;
case 's':
startino = cvt_u64(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
break;
case 'v':
ver = cvt_u32(optarg, 10);
if (errno) {
perror(optarg);
return 1;
}
if (ver != 1 && ver != 5) {
fprintf(stderr, "version must be 1 or 5.\n");
return 1;
}
break;
default:
bulkstat_help();
return 0;
}
}
if (optind != argc) {
bulkstat_help();
return 0;
}
ret = -xfd_prepare_geometry(&xfd);
if (ret) {
xfrog_perror(ret, "xfd_prepare_geometry");
exitcode = 1;
return 0;
}
ret = -xfrog_inumbers_alloc_req(batch_size, startino, &ireq);
if (ret) {
xfrog_perror(ret, "alloc inumbersreq");
exitcode = 1;
return 0;
}
if (has_agno)
xfrog_inumbers_set_ag(ireq, agno);
set_xfd_flags(&xfd, ver);
while ((ret = -xfrog_inumbers(&xfd, ireq)) == 0) {
if (debug)
printf(
_("bulkstat: startino=%"PRIu64" flags=0x%"PRIx32" agno=%"PRIu32" ret=%d icount=%"PRIu32" ocount=%"PRIu32"\n"),
ireq->hdr.ino,
ireq->hdr.flags,
ireq->hdr.agno,
ret,
ireq->hdr.icount,
ireq->hdr.ocount);
if (ireq->hdr.ocount == 0)
break;
for (i = 0; i < ireq->hdr.ocount; i++) {
if (ireq->inumbers[i].xi_startino > endino)
break;
dump_inumbers(&ireq->inumbers[i]);
}
}
if (ret) {
xfrog_perror(ret, "xfrog_inumbers");
exitcode = 1;
}
free(ireq);
return 0;
}
static cmdinfo_t bulkstat_cmd = {
.name = "bulkstat",
.cfunc = bulkstat_f,
.argmin = 0,
.argmax = -1,
.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
.help = bulkstat_help,
};
static cmdinfo_t bulkstat_single_cmd = {
.name = "bulkstat_single",
.cfunc = bulkstat_single_f,
.argmin = 1,
.argmax = -1,
.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
.help = bulkstat_single_help,
};
static cmdinfo_t inumbers_cmd = {
.name = "inumbers",
.cfunc = inumbers_f,
.argmin = 0,
.argmax = -1,
.flags = CMD_NOMAP_OK | CMD_FLAG_ONESHOT,
.help = inumbers_help,
};
void
bulkstat_init(void)
{
bulkstat_cmd.args =
_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino] [-v version]");
bulkstat_cmd.oneline = _("Bulk stat of inodes in a filesystem");
bulkstat_single_cmd.args = _("[-d] [-v version] inum...");
bulkstat_single_cmd.oneline = _("Stat one inode in a filesystem");
inumbers_cmd.args =
_("[-a agno] [-d] [-e endino] [-n batchsize] [-s startino] [-v version]");
inumbers_cmd.oneline = _("Query inode groups in a filesystem");
add_command(&bulkstat_cmd);
add_command(&bulkstat_single_cmd);
add_command(&inumbers_cmd);
}