diff --git a/Makefile b/Makefile index 88d8490..951925c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ all: era_copy era_apply +install: all + cp era_copy era_apply /usr/local/bin era_copy: era_copy.c gcc -o era_copy -Wall era_copy.c era_apply: era_apply.c diff --git a/era_apply.c b/era_apply.c index 551f913..9e05e60 100644 --- a/era_apply.c +++ b/era_apply.c @@ -21,7 +21,7 @@ static void* copy_buffer = NULL; -void apply_blocks(int src, int dst, off64_t start, off64_t length) +void apply_blocks(int src, int dst, off64_t start, off64_t length, long long *total, long long *prev_total) { off64_t copied = 0; ssize_t fact = 0, written = 0, fact_write = 0; @@ -59,13 +59,30 @@ void apply_blocks(int src, int dst, off64_t start, off64_t length) written += fact_write; } copied += fact; + if (total != NULL) + { + *total += fact; + if (*total > *prev_total + 1048576) + { + fprintf(stderr, "\r%lld MB...", *total/1048576); + *prev_total = *total; + } + } } } -void read_era_copy_and_apply(int src, int dst) +void read_era_copy_and_apply(int src, int dst, int print_progress) { long long sign = 0, start = 0, length = 0; + long long total = 0, prev_total = 0; // for printing progress + long long *ptr_total = NULL, *ptr_prev_total = NULL; int r; + if (print_progress) + { + setvbuf(stderr, NULL, _IONBF, 0); + ptr_total = &total; + ptr_prev_total = &prev_total; + } while (1) { r = read(src, &sign, 8); @@ -88,11 +105,15 @@ void read_era_copy_and_apply(int src, int dst) fprintf(stderr, "era_copy signature does not match\n"); exit(1); } - apply_blocks(src, dst, start, length); + apply_blocks(src, dst, start, length, ptr_total, ptr_prev_total); + } + if (print_progress) + { + fprintf(stderr, "\n"); } } -void era_apply(char *dst_path) +void era_apply(char *dst_path, int print_progress) { struct stat sb; int dst, flags = O_WRONLY | O_LARGEFILE; @@ -119,7 +140,7 @@ void era_apply(char *dst_path) fprintf(stderr, "Failed to open %s for writing: %s\n", dst_path, strerror(errno)); exit(1); } - read_era_copy_and_apply(0, dst); + read_era_copy_and_apply(0, dst, print_progress); // fsync and close fsync(dst); close(dst); @@ -127,16 +148,17 @@ void era_apply(char *dst_path) int main(int narg, char *args[]) { - if (narg < 2) + int print_progress = 0; + if (narg < 2 || ((print_progress = !strcmp(args[1], "--progress")) && narg < 3)) { fprintf(stderr, "era_apply - applies era_copy output to a file or block device\n" "(c) Vitaliy Filippov, 2019+, distributed under the terms of GNU GPLv3.0 or later license\n" "\n" - "USAGE:\nera_apply DESTINATION < era_copy_output.bin\n" + "USAGE:\nera_apply [--progress] DESTINATION < era_copy_output.bin\n" ); exit(1); } - era_apply(args[1]); + era_apply(args[print_progress ? 2 : 1], print_progress); return 0; } diff --git a/era_copy.c b/era_copy.c index 66d37ae..78cb7e4 100644 --- a/era_copy.c +++ b/era_copy.c @@ -39,12 +39,14 @@ void copy_blocks(int src, int dst, off64_t start, off64_t length) } } -void read_era_invalidate_and_copy(FILE *fp, int src, int era_block_size) +void read_era_invalidate_and_copy(FILE *fp, int src, int era_block_size, int print_progress) { // read input XML char buf[XML_BUFSIZE] = { 0 }; char c = 0; long long start = 0, length = 0, total = 0; + long long *offsets = NULL; + int i, offsets_alloc = 0, offsets_len = 0; if (fgets(buf, XML_BUFSIZE, fp) == NULL) { fprintf(stderr, "Input block list is empty\n"); @@ -71,32 +73,51 @@ void read_era_invalidate_and_copy(FILE *fp, int src, int era_block_size) } start = start*era_block_size*512; length = length*era_block_size*512; - if (src >= 0) + if (offsets_len >= offsets_alloc) { - // write a very simple binary format: SIGNATURE, start, length, data, ... - write(1, SIGNATURE, 8); - write(1, &start, 8); - write(1, &length, 8); - copy_blocks(src, 1, start, length); - } - else - { - // calculate diff size - total += length; + offsets_alloc += 4096/sizeof(long long); + offsets = (long long*)realloc(offsets, offsets_alloc*2*sizeof(long long)); } + offsets[offsets_len++] = start; + offsets[offsets_len++] = length; + // calculate diff size + total += length; } if (sscanf(buf, " %c", &c) == 0 || c != 0) { fprintf(stderr, " expected, but \"%s\" found\n", buf); exit(1); } - if (src < 0) + if (src >= 0) { - printf("%lld bytes\n", total); + long long copied = 0, prev_copied = 0; + if (print_progress) + { + setvbuf(stderr, NULL, _IONBF, 0); + } + for (i = 0; i < offsets_len; i += 2) + { + if (print_progress && (!copied || copied >= prev_copied + 1048576)) + { + fprintf(stderr, "\rDone %lld MB / %lld MB, copying next %lld MB...", copied/1048576, total/1048576, offsets[i+1]/1048576); + prev_copied = copied; + } + // write a very simple binary format: SIGNATURE, start, length, data, ... + write(1, SIGNATURE, 8); + write(1, offsets+i, 8); + write(1, offsets+i+1, 8); + copy_blocks(src, 1, offsets[i], offsets[i+1]); + copied += offsets[i+1]; + } + fprintf(stderr, "\n"); + } + else + { + printf("%lld bytes to copy\n", total); } } -void era_copy(char *src_path, int era_block_size) +void era_copy(char *src_path, int era_block_size, int print_progress) { int src = -1; if (src_path != NULL) @@ -108,7 +129,7 @@ void era_copy(char *src_path, int era_block_size) exit(1); } } - read_era_invalidate_and_copy(stdin, src, era_block_size); + read_era_invalidate_and_copy(stdin, src, era_block_size, print_progress); if (src_path != NULL) { close(src); @@ -117,29 +138,30 @@ void era_copy(char *src_path, int era_block_size) int main(int narg, char *args[]) { - if (narg < 2) + int print_progress = 0; + if (narg < 2 || ((print_progress = !strcmp(args[1], "--progress")) && narg < 3)) { fprintf(stderr, "era_copy - parses era_invalidate output and saves changed blocks to a stream\n" "(c) Vitaliy Filippov, 2019+, distributed under the terms of GNU GPLv3.0 or later license\n" "\n" "USAGE:\nera_invalidate --metadata-snapshot --written-since |\\\n" - " era_copy [] > era_copy.bin\n" + " era_copy [--progress] [] > era_copy.bin\n" "\n" "When invoked without , era_copy only calculates the future diff size and prints it\n" "\n" "ERA_BLOCK_SIZE is dm-era's granularity in 512-byte sectors you used when creating dm-era device\n" "You can take it from `dmsetup table `: it's the last number in DM table\n" - "For example, in `0 3625546496 era 259:3 259:2 65536` it's 65536\n" + "For example, in `0 3625546496 era 259:3 259:2 1024` it's 1024\n" ); exit(1); } - int bs = atoi(args[1]); + int bs = atoi(args[print_progress ? 2 : 1]); if (bs < 1) { fprintf(stderr, "Incorrect era_block_size = %d\n", bs); exit(1); } - era_copy(narg == 2 ? NULL : args[2], bs); + era_copy(narg == (print_progress ? 3 : 2) ? NULL : args[print_progress ? 3 : 2], bs, print_progress); return 0; }