From 9891d6597770ed884c989885d04da46db5952f0a Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 16 May 2018 10:30:03 -0400 Subject: first pass at cow-extent-map --- Makefile | 6 +++- lib/extent-map.c | 41 +++++++++++++++++++++++ lib/extent-map.h | 4 +++ src/cow-extent-map.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 lib/extent-map.c create mode 100644 lib/extent-map.h create mode 100644 src/cow-extent-map.c diff --git a/Makefile b/Makefile index e6dffd6..6cf34d9 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,14 @@ CFLAGS += -Wall -Werror -Wextra CPPFLAGS += -O2 -D_FORTIFY_SOURCE=2 all: cow-dedupe-range +all: cow-extent-map .PHONY: all -cow-dedupe-range: src/cow-dedupe-range.o lib/dedupe-range.o +%: src/%.o $(CC) $(LDFLAGS) -o $@ $^ +cow-dedupe-range: lib/dedupe-range.o +cow-extent-map: lib/extent-map.o + .SECONDARY: .DELETE_ON_ERROR: diff --git a/lib/extent-map.c b/lib/extent-map.c new file mode 100644 index 0000000..b698fa1 --- /dev/null +++ b/lib/extent-map.c @@ -0,0 +1,41 @@ +#include /* for errno */ +#include /* for error(3gnu) */ +#include /* for all of the other fiemap stuff */ +#include /* for FS_IOC_FIEMAP */ +#include /* for calloc(3p), exit(3p), EXIT_SUCCESS, EXIT_FAILURE */ +#include /* for ioctl(2) */ +#include /* for sysconf(3p), _SC_PAGESIZE */ + +#include "extent-map.h" + +void fiemap(int fd, uint32_t flags, int (*handle_extent)(struct fiemap_extent)) { + const size_t fm_size = sysconf(_SC_PAGESIZE);; + struct fiemap *fm = calloc(1, fm_size); + if (!fm) + error(EXIT_FAILURE, errno, "malloc"); + uint64_t bytes_mapped = 0; + while (1) { + *fm = (struct fiemap){ + .fm_start = bytes_mapped, + .fm_length = FIEMAP_MAX_OFFSET - bytes_mapped, + .fm_flags = flags, + .fm_extent_count = (fm_size - sizeof(struct fiemap)) / sizeof(struct fiemap_extent), + }; + + if (ioctl(fd, FS_IOC_FIEMAP, fm) < 0) + error(EXIT_FAILURE, errno, "ioctl (FS_IOC_FIEMAP)"); + + for (size_t i = 0; i < fm->fm_extent_count; i++) { + int r = handle_extent(fm->fm_extents[i]); + if (r) { + free(fm); + exit(r); + } + + if (fm->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) { + free(fm); + exit(EXIT_SUCCESS); + } + } + } +} diff --git a/lib/extent-map.h b/lib/extent-map.h new file mode 100644 index 0000000..4f873a0 --- /dev/null +++ b/lib/extent-map.h @@ -0,0 +1,4 @@ +#include /* for uint32_t */ +#include /* for struct fiemap_extent */ + +void fiemap(int fd, uint32_t flags, int (*handle_extent)(struct fiemap_extent)); diff --git a/src/cow-extent-map.c b/src/cow-extent-map.c new file mode 100644 index 0000000..7cee70c --- /dev/null +++ b/src/cow-extent-map.c @@ -0,0 +1,93 @@ +#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 printf(3p), fprintf(3p), stderr */ +#include /* exit(3p), EXIT_SUCCESS, EXIT_FAILURE */ +#include /* for strcmp(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) + +int print_extent(struct fiemap_extent extent) { + printf("logical=%"PRIu64" " + "physical=%"PRIu64" " + "length=%"PRIu64" " + "flags=0x%"PRIx32"\n", + extent.fe_logical, + extent.fe_physical, + extent.fe_length, + extent.fe_flags); + 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); + + fiemap(fd, flags, print_extent); + return EXIT_SUCCESS;; +} -- cgit v1.2.3