#define _GNU_SOURCE /* for program_invocation_name */ #include /* for assert(3p) */ #include /* for errno, program_invocation_name */ #include /* for error(3gnu) */ #include /* for open(2) and O_RDONLY */ #include /* for getopt_long(3gnu), struct option, optind, optarg */ #include /* for PRI* */ #include /* for bool, true, false */ #include /* for printf(3p), fprintf(3p), stderr */ #include /* for free(3p), exit(3p), EXIT_SUCCESS, EXIT_FAILURE */ #include /* for strcmp(3p) */ #include /* for close(2) */ #include "extent-map.h" /* for uint32_t, fiemap stuff */ #define EXIT_INVALIDARGUMENT 2 #define errusage(format, ...) do { \ error(0, 0, format, ## __VA_ARGS__); \ fprintf(stderr, "Try '%s --help' for more information.\n", program_invocation_name); \ exit(EXIT_INVALIDARGUMENT); \ } while(0) bool indent; int print_extent(struct fiemap_extent extent) { char *flagstr = strextentflags(extent.fe_flags); printf("%s" "logical=%"PRIu64" " "physical=%"PRIu64" " "length=%"PRIu64" " "flags=0x%08"PRIx32" (%s)\n", indent ? "\t" : "", extent.fe_logical, extent.fe_physical, extent.fe_length, extent.fe_flags, flagstr); free(flagstr); return 0; } void usage() { printf("Usage: %s [OPTIONS] FILENAME...\n" "Print a file extent map.\n" "\n" "OPTIONS:\n" " --stream=data Map extents in the data stream (default).\n" " --stream=xattr Map extents in the extended attribute stream.\n" " --sync Sync the file before mapping extents (default).\n" " --no-sync Don't sync the file before mapping extents.\n" " -m, --machine Display the output in a more machine-friendly format.\n" " -h, --help Display this help and exit.\n", program_invocation_name); } int main(int argc, char *argv[]) { char *stream = "data"; bool machine = false; int sync = 1; struct option long_options[] = { {"stream", optional_argument, NULL, 1}, {"sync", no_argument, &sync, 1}, {"no-sync", no_argument, &sync, 0}, {"help", no_argument, NULL, 'h'}, {0}, }; int flag; while ((flag = getopt_long(argc, argv, "mh", long_options, NULL)) >= 0) { switch (flag) { case 0: break; case 1: stream = optarg; break; case 'm': machine = true; break; case 'h': usage(); return EXIT_SUCCESS; case '?': fprintf(stderr, "Try '%s --help' for more information.\n", program_invocation_name); return EXIT_INVALIDARGUMENT; default: assert(0); } } if (argc - optind < 1) errusage("wrong number of arguments"); uint32_t flags = sync ? FIEMAP_FLAG_SYNC : 0; if (strcmp(stream, "data") == 0) { /* do nothing */ } else if (strcmp(stream, "xattr") == 0) { flags |= FIEMAP_FLAG_XATTR; } else { errusage("invalid stream name"); } indent = (argc - optind) > 1 && !machine; int status = 0; for (int i = optind; i < argc; i++) { char *filename = argv[i]; int fd = open(filename, O_RDONLY); if (fd < 0) { error(0, errno, "%s", filename); status |= EXIT_FAILURE; continue; } if (machine) { printf("%s", filename); putchar('\0'); } else if (argc - optind > 1) printf("%s:\n", filename); int r = fiemap(fd, flags, print_extent); if (r < 0) { error(0, -r, "%s: FS_IOC_FIEMAP", filename); status |= EXIT_FAILURE; } else { status |= r; } if (machine) putchar('\0'); close(fd); } return status; }