| // 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); |
| } |