From b9b1dae19b2155c07d8ffb6d6283f3a5fd2d4b2c Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Tue, 19 Feb 2019 00:49:07 +0300 Subject: [PATCH] era_copy for dm-era --- Makefile | 3 ++ era_copy.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 Makefile create mode 100644 era_copy.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..caf4618 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +all: era_copy +era_copy: + gcc -o era_copy -Wall era_copy.c diff --git a/era_copy.c b/era_copy.c new file mode 100644 index 0000000..ec7daf5 --- /dev/null +++ b/era_copy.c @@ -0,0 +1,135 @@ +/** + * Parses XML block lists produced by dm-era `era_invalidate` tool + * from `thin-provisioning-tools` and copies these blocks from/to + * specified device(s) + * + * Author: Vitaliy Filippov , 2019 + * License: GNU GPLv3.0 or later + */ + +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XML_BUFSIZE 1024 +#define MAX_COPY 0x10000000 + +void copy_blocks(int src, int dst, off64_t start, off64_t length) +{ + off64_t copied = 0, cur = start; + ssize_t fact = 0; + if (lseek64(dst, start, 0) < 0) + { + fprintf(stderr, "Failed to lseek output file: %s\n", strerror(errno)); + exit(1); + } + while (length > copied) + { + fact = sendfile64(dst, src, &cur, length-copied > MAX_COPY ? MAX_COPY : length-copied); + if (fact < 0) + { + fprintf(stderr, "Failed to copy data: %s\n", strerror(errno)); + exit(1); + } + cur += fact; + copied += fact; + } +} + +void read_era_invalidate_and_copy(FILE *fp, int src, int dst, int metadata_block_size) +{ + // read input XML + char buf[XML_BUFSIZE] = { 0 }; + off64_t start = 0, length = 0; + if (fgets(buf, XML_BUFSIZE, fp) == NULL) + { + fprintf(stderr, "Input block list is empty\n"); + exit(0); + } + if (sscanf(buf, " ") == 0) + { + fprintf(stderr, " expected, but \"%s\" found\n", buf); + exit(1); + } + while (fgets(buf, XML_BUFSIZE, fp) != NULL) + { + if (sscanf(buf, " ", &start, &length) == 2) + { + copy_blocks(src, dst, start*metadata_block_size*512, length*metadata_block_size*512); + } + else if (sscanf(buf, " ", &start) == 1) + { + copy_blocks(src, dst, start*metadata_block_size*512, metadata_block_size*512); + } + else + { + fprintf(stderr, " or expected, but \"%s\" found\n", buf); + exit(1); + } + } + buf[0] = '\0'; + if (fgets(buf, XML_BUFSIZE, fp) == NULL || + sscanf(buf, " ") == 0) + { + fprintf(stderr, " expected, but \"%s\" found\n", buf); + exit(1); + } +} + +void era_copy(char *src_path, const char *dst_path, int metadata_block_size) +{ + int src, dst; + struct stat statbuf; + src = open(src_path, O_RDONLY|O_LARGEFILE); + if (src < 0) + { + fprintf(stderr, "Failed to open %s for reading: %s\n", src_path, strerror(errno)); + exit(1); + } + if (stat(dst_path, &statbuf) != 0) + { + fprintf(stderr, "Failed to stat %s: %s\n", dst_path, strerror(errno)); + exit(1); + } + // prevent writing to mounted devices + dst = open(dst_path, ((statbuf.st_mode & S_IFBLK) ? O_EXCL : 0) | O_WRONLY | O_LARGEFILE); + if (src < 0) + { + fprintf(stderr, "Failed to open %s for writing: %s\n", dst_path, strerror(errno)); + exit(1); + } + read_era_invalidate_and_copy(stdin, src, dst, metadata_block_size); + // fsync and close + fsync(dst); + close(dst); + close(src); +} + +int main(int narg, char *args[]) +{ + if (narg < 4) + { + fprintf(stderr, + "era_copy - parses era_invalidate output and copies specified blocks from one file/device to another\n" + "(c) Vitaliy Filippov, 2019+, distributed under the terms of GNU GPLv3.0 or later license\n" + "USAGE:\nera_invalidate --metadata-snapshot --written-since |\\\n" + " era_copy \n" + ); + exit(1); + } + int bs = atoi(args[1]); + if (bs < 1) + { + fprintf(stderr, "Incorrect metadata_block_size = %d\n", bs); + exit(1); + } + era_copy(args[2], args[3], bs); + return 0; +}