diff --git a/Makefile b/Makefile index 742aea1..88d8490 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ -all: era_copy +all: era_copy era_apply era_copy: era_copy.c gcc -o era_copy -Wall era_copy.c +era_apply: era_apply.c + gcc -o era_apply -Wall era_apply.c diff --git a/era_apply.c b/era_apply.c new file mode 100644 index 0000000..1871df7 --- /dev/null +++ b/era_apply.c @@ -0,0 +1,131 @@ +/** + * Applies `era_copy` output to a file or block device + * + * Author: Vitaliy Filippov , 2019 + * License: GNU GPLv3.0 or later + */ + +#define _LARGEFILE64_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#define XML_BUFSIZE 1024 +#define COPY_BUFSIZE 0x400000 +#define SIGNATURE "ERARANGE" + +static void* copy_buffer = NULL; + +void apply_blocks(int src, int dst, off64_t start, off64_t length) +{ + off64_t copied = 0; + ssize_t fact = 0, written = 0, fact_write = 0; + if (!copy_buffer) + { + copy_buffer = malloc(COPY_BUFSIZE); + if (!copy_buffer) + { + fprintf(stderr, "Failed to allocate %d bytes\n", COPY_BUFSIZE); + exit(1); + } + } + if (lseek64(dst, start, 0) < 0) + { + fprintf(stderr, "Failed to lseek in output file: %s\n", strerror(errno)); + exit(1); + } + while (length > copied) + { + fact = read(src, copy_buffer, length-copied > COPY_BUFSIZE ? COPY_BUFSIZE : length-copied); + if (fact <= 0) + { + fprintf(stderr, "Read error: %s\n", strerror(errno)); + exit(1); + } + written = 0; + while (written < fact) + { + fact_write = write(dst, copy_buffer, fact-written); + if (fact_write <= 0) + { + fprintf(stderr, "Write error: %s\n", strerror(errno)); + exit(1); + } + written += fact_write; + } + copied += fact; + } +} + +void read_era_copy_and_apply(int src, int dst) +{ + long long sign = 0, start = 0, length = 0; + int r; + while (1) + { + r = read(src, &sign, 8); + if (r == 0) + { + break; + } + if (r < 8 || + read(src, &start, 8) < 8 || + read(src, &length, 8) < 8) + { + if (errno) + fprintf(stderr, "read error: %s\n", strerror(errno)); + else + fprintf(stderr, "premature end of file\n"); + exit(1); + } + if (sign != *((long long*)SIGNATURE)) + { + fprintf(stderr, "era_copy signature does not match\n"); + exit(1); + } + apply_blocks(src, dst, start, length); + } +} + +void era_apply(char *dst_path) +{ + struct stat sb; + int dst; + if (stat(dst_path, &sb) != 0) + { + fprintf(stderr, "Failed to stat %s: %s\n", dst_path, strerror(errno)); + exit(1); + } + // prevent writing to mounted devices + dst = open(dst_path, ((sb.st_mode & S_IFBLK) ? O_EXCL : 0) | O_WRONLY | O_LARGEFILE); + if (dst < 0) + { + fprintf(stderr, "Failed to open %s for writing: %s\n", dst_path, strerror(errno)); + exit(1); + } + read_era_copy_and_apply(0, dst); + // fsync and close + fsync(dst); + close(dst); +} + +int main(int narg, char *args[]) +{ + if (narg < 2) + { + 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" + ); + exit(1); + } + era_apply(args[1]); + return 0; +} diff --git a/era_copy.c b/era_copy.c index 2a4903e..26a6e06 100644 --- a/era_copy.c +++ b/era_copy.c @@ -21,6 +21,7 @@ #define XML_BUFSIZE 1024 #define MAX_COPY 0x10000000 +#define SIGNATURE "ERARANGE" void copy_blocks(int src, int dst, off64_t start, off64_t length) { @@ -42,7 +43,6 @@ 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) { // read input XML - char* signature = "ERARANGE"; char buf[XML_BUFSIZE] = { 0 }; char c = 0; long long start = 0, length = 0; @@ -72,8 +72,8 @@ void read_era_invalidate_and_copy(FILE *fp, int src, int era_block_size) } start = start*era_block_size; length = length*era_block_size; - // write a very simple binary format: signature, start, length, data, ... - write(1, signature, 8); + // 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); @@ -103,11 +103,11 @@ int main(int narg, char *args[]) if (narg < 3) { fprintf(stderr, - "era_copy - parses era_invalidate output and copies specified blocks from one file/device to another\n" + "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 \n" + " era_copy > era_copy.bin\n" "\n" "ERA_BLOCK_SIZE is the block size you used when creating dm-era device\n" "You can take it from `dmsetup table `: it's the last number in the table\n"