From c787006ee3742ee8384210c4355f73aef7c8d0b8 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Tue, 15 May 2018 17:36:35 -0400 Subject: initial commit --- src/cow-dedupe-range.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/cow-dedupe-range.c (limited to 'src/cow-dedupe-range.c') diff --git a/src/cow-dedupe-range.c b/src/cow-dedupe-range.c new file mode 100644 index 0000000..7508f04 --- /dev/null +++ b/src/cow-dedupe-range.c @@ -0,0 +1,97 @@ +#define _GNU_SOURCE /* for program_invocation_name */ +#include /* for assert(3p) */ +#include /* for errno, program_invocation_name */ +#include /* for error(3gnu) */ +#include /* for O_RDONLY, O_RDWR */ +#include /* for getopt_long(3gnu), struct option, optind */ +#include /* for bool, true, false */ +#include /* for uint64_t */ +#include /* for printf(3gnu), fprintf(3p), stderr */ +#include /* for strtoll(3p), calloc(3p), free(3p) */ +#include /* for strlen(3p) */ +#include /* for dup2(3p) */ + +#include "dedupe-range.h" /* for dedupe_range, struct range */ + +bool atou64(const char *str, uint64_t *ret) { + assert(sizeof(uint64_t) <= sizeof(unsigned long long int)); + if (!('0' <= str[0] && str[0] <= '9')) + return false; + errno = 0; + char *end; + unsigned long long int n = strtoll(str, &end, 10); + if (end[0] != '\0' || errno != 0) + return false; + *ret = (uint64_t)n; + return true; +} + +void usage() { + printf("Usage: %2$*1$s [OPTIONS] SRC_FILENAME SRC_OFFSET SRC_LENGTH \\\n" + " %3$*1$s DST_FILENAME DST_OFFSET \\\n" + " %3$*1$s [DST_FILENAME DST_OFFSET]...\n" + "Submit a file deduplication request to the kernel.\n" + "If the file ranges are not duplicates, the kernel will ignore this request.\n" + "\n" + "OPTIONS:\n" + " -r, --readonly Open destination files read-only, rather than read/write.\n" + " Generically, the destination must be open for writing.\n" + " However, on Btrfs, this allows deduplication to be done\n" + " on read-only subvolumes when invoked as root.\n" + " -h, --help Display this help and exit.\n", + (int)strlen(program_invocation_name), program_invocation_name, ""); +} + +int main(int argc, char *argv[]) { + int ro = 0; + struct option long_options[] = { + {"readonly", no_argument, &ro, 1}, + {"help", no_argument, NULL, 'h'}, + {0} + }; + int flag; + while ((flag = getopt_long(argc, argv, "rh", long_options, NULL)) >= 0) { + switch (flag) { + case 0: + break; + case 'r': + ro = 1; + break; + case 'h': + usage(); + return 0; + case '?': + fprintf(stderr, "Try '%s --help' for more information.\n", program_invocation_name); + return 2; + default: + assert(false); + } + } + + if (argc - optind < 5) { + error(0, 0, "too few arguments"); + fprintf(stderr, "Try '%s --help' for more information.\n", program_invocation_name); + return 2; + } + + struct range src; + src.filename = argv[optind]; + src.flags = O_RDONLY; + if (!atou64(argv[optind+1], &src.offset)) + error(2, errno, "invalid offset '%s'", argv[optind+1]); + if (!atou64(argv[optind+2], &src.length)) + error(2, errno, "invalid length '%s'", argv[optind+2]); + + const size_t dst_count = (argc - optind - 3) / 2; + struct range *dsts = calloc(dst_count + 1, sizeof(struct range)); + for (size_t i = 0; i < dst_count; i++) { + dsts[i].filename = argv[optind+3+(i*2)]; + dsts[i].flags = ro ? O_RDONLY : O_RDWR; + if (!atou64(argv[optind+3+(i*2)+1], &dsts[i].offset)) + error(2, errno, "invalid offset '%s'", argv[optind+3+(i*2)+1]); + } + + dedupe_range(src, dsts); + free(dsts); + return 0; +} -- cgit v1.2.3