#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 /* PRI* */ #include /* for bool, true, false */ #include /* for printf(3p), fprintf(3p), sprintf(3p), stderr */ #include /* malloc(3p), freep(3p), exit(3p), EXIT_SUCCESS, EXIT_FAILURE */ #include /* for strcmp(3p), strlen(3p), strcpy(3p) */ #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) struct flag { char *name; uint32_t value; bool active; }; int print_extent(struct fiemap_extent extent) { struct flag known_flags[] = { {"LAST", FIEMAP_EXTENT_LAST, false}, {"UNKNOWN", FIEMAP_EXTENT_UNKNOWN, false}, {"DELALLOC", FIEMAP_EXTENT_DELALLOC, false}, {"ENCODED", FIEMAP_EXTENT_ENCODED, false}, {"DATA_ENCRYPTED", FIEMAP_EXTENT_DATA_ENCRYPTED, false}, {"NOT_ALIGNED", FIEMAP_EXTENT_NOT_ALIGNED, false}, {"DATA_INLINE", FIEMAP_EXTENT_DATA_INLINE, false}, {"DATA_TAIL", FIEMAP_EXTENT_DATA_TAIL, false}, {"UNWRITTEN", FIEMAP_EXTENT_UNWRITTEN, false}, {"MERGED", FIEMAP_EXTENT_MERGED, false}, {"SHARED", FIEMAP_EXTENT_SHARED, false}, }; uint32_t flags = extent.fe_flags; size_t flagstr_cap = 0; for (size_t i = 0; i < sizeof(known_flags)/sizeof(known_flags[0]); i++) { if (!(flags & known_flags[i].value)) continue; known_flags[i].active = true; flags ^= known_flags[i].value; switch (known_flags[i].value) { case FIEMAP_EXTENT_UNKNOWN: if (flags & FIEMAP_EXTENT_DELALLOC) { known_flags[i].name = "(UNKNOWN|DELALLOC)"; flags ^= FIEMAP_EXTENT_DELALLOC; } break; case FIEMAP_EXTENT_ENCODED: if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED) { known_flags[i].name = "(ENCODED|DATA_ENCRYPTED)"; flags ^= FIEMAP_EXTENT_DATA_ENCRYPTED; } break; case FIEMAP_EXTENT_NOT_ALIGNED: switch (flags & (FIEMAP_EXTENT_DATA_INLINE|FIEMAP_EXTENT_DATA_TAIL)) { case FIEMAP_EXTENT_DATA_INLINE: known_flags[i].name = "(NOT_ALIGNED|DATA_INLINE)"; flags ^= FIEMAP_EXTENT_DATA_INLINE; break; case FIEMAP_EXTENT_DATA_TAIL: known_flags[i].name = "(NOT_ALIGNED|DATA_TAIL)"; flags ^= FIEMAP_EXTENT_DATA_TAIL; break; case FIEMAP_EXTENT_DATA_INLINE|FIEMAP_EXTENT_DATA_TAIL: known_flags[i].name = "(NOT_ALIGNED|DATA_INLINE|DATA_TAIL)"; flags ^= FIEMAP_EXTENT_DATA_INLINE|FIEMAP_EXTENT_DATA_TAIL; break; } break; } if (flagstr_cap) flagstr_cap++; flagstr_cap += strlen(known_flags[i].name); } if (flags) flagstr_cap += (flagstr_cap ? 1 : 0) + 10; char *flagstr = malloc(flagstr_cap+1); if (!flagstr) error(EXIT_FAILURE, errno, "malloc"); size_t flagstr_len = 0; for (size_t i = 0; i < sizeof(known_flags)/sizeof(known_flags[0]); i++) { if (!known_flags[i].active) continue; if (flagstr_len) flagstr[flagstr_len++] = '|'; strcpy(&flagstr[flagstr_len], known_flags[i].name); flagstr_len += strlen(known_flags[i].name); } if (flags) { if (flagstr_len) flagstr[flagstr_len++] = '|'; sprintf(&flagstr[flagstr_len], "0x%08"PRIx32, flags); }; printf("logical=%"PRIu64" " "physical=%"PRIu64" " "length=%"PRIu64" " "flags=0x%08"PRIx32" (%s)\n", 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" " -h, --help Display this help and exit.\n", program_invocation_name); } int main(int argc, char *argv[]) { char *stream = "data"; 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, "h", long_options, NULL)) >= 0) { switch (flag) { case 0: break; case 1: stream = optarg; 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"); } char *filename = argv[optind]; int fd = open(filename, O_RDONLY); if (fd < 0) error(EXIT_FAILURE, errno, "%s", filename); return fiemap(fd, flags, print_extent); }