Just copy to standard output

master
Vitaliy Filippov 2019-02-19 01:31:09 +03:00
parent b9b1dae19b
commit 4dda5a44df
2 changed files with 26 additions and 40 deletions

View File

@ -1,3 +1,3 @@
all: era_copy all: era_copy
era_copy: era_copy: era_copy.c
gcc -o era_copy -Wall era_copy.c gcc -o era_copy -Wall era_copy.c

View File

@ -1,7 +1,8 @@
/** /**
* Parses XML block lists produced by dm-era `era_invalidate` tool * Parses XML block lists produced by dm-era `era_invalidate` tool
* from `thin-provisioning-tools` and copies these blocks from/to * from `thin-provisioning-tools` from standard input, reads specified blocks
* specified device(s) * from the data device and creates a stream with copied blocks on standard output.
* The stream can then be consumed by `era_apply`.
* *
* Author: Vitaliy Filippov <vitalif@yourcmc.ru>, 2019 * Author: Vitaliy Filippov <vitalif@yourcmc.ru>, 2019
* License: GNU GPLv3.0 or later * License: GNU GPLv3.0 or later
@ -25,11 +26,6 @@ void copy_blocks(int src, int dst, off64_t start, off64_t length)
{ {
off64_t copied = 0, cur = start; off64_t copied = 0, cur = start;
ssize_t fact = 0; 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) while (length > copied)
{ {
fact = sendfile64(dst, src, &cur, length-copied > MAX_COPY ? MAX_COPY : length-copied); fact = sendfile64(dst, src, &cur, length-copied > MAX_COPY ? MAX_COPY : length-copied);
@ -43,17 +39,19 @@ void copy_blocks(int src, int dst, off64_t start, off64_t length)
} }
} }
void read_era_invalidate_and_copy(FILE *fp, int src, int dst, int metadata_block_size) void read_era_invalidate_and_copy(FILE *fp, int src, int metadata_block_size)
{ {
// read input XML // read input XML
char* signature = "ERARANGE";
char buf[XML_BUFSIZE] = { 0 }; char buf[XML_BUFSIZE] = { 0 };
off64_t start = 0, length = 0; char c = 0;
long long start = 0, length = 0;
if (fgets(buf, XML_BUFSIZE, fp) == NULL) if (fgets(buf, XML_BUFSIZE, fp) == NULL)
{ {
fprintf(stderr, "Input block list is empty\n"); fprintf(stderr, "Input block list is empty\n");
exit(0); exit(0);
} }
if (sscanf(buf, " <blocks >") == 0) if (sscanf(buf, " <blocks > %c", &c) == 0 || c != 0)
{ {
fprintf(stderr, "<blocks> expected, but \"%s\" found\n", buf); fprintf(stderr, "<blocks> expected, but \"%s\" found\n", buf);
exit(1); exit(1);
@ -62,65 +60,53 @@ void read_era_invalidate_and_copy(FILE *fp, int src, int dst, int metadata_block
{ {
if (sscanf(buf, " <range begin = \" %lld \" end = \" %lld \" />", &start, &length) == 2) if (sscanf(buf, " <range begin = \" %lld \" end = \" %lld \" />", &start, &length) == 2)
{ {
copy_blocks(src, dst, start*metadata_block_size*512, length*metadata_block_size*512); length = length-start;
} }
else if (sscanf(buf, " <block block = \" %lld \" />", &start) == 1) else if (sscanf(buf, " <block block = \" %lld \" />", &start) == 1)
{ {
copy_blocks(src, dst, start*metadata_block_size*512, metadata_block_size*512); length = 1;
} }
else else
{ {
fprintf(stderr, "<range begin=NUMBER end=NUMBER /> or <block block=NUMBER /> expected, but \"%s\" found\n", buf); break;
exit(1);
} }
start = start*metadata_block_size*512;
length = length*metadata_block_size*512;
// 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);
} }
buf[0] = '\0'; if (sscanf(buf, " </blocks > %c", &c) == 0 || c != 0)
if (fgets(buf, XML_BUFSIZE, fp) == NULL ||
sscanf(buf, " </blocks >") == 0)
{ {
fprintf(stderr, "</blocks> expected, but \"%s\" found\n", buf); fprintf(stderr, "</blocks> expected, but \"%s\" found\n", buf);
exit(1); exit(1);
} }
} }
void era_copy(char *src_path, const char *dst_path, int metadata_block_size) void era_copy(char *src_path, int metadata_block_size)
{ {
int src, dst; int src;
struct stat statbuf;
src = open(src_path, O_RDONLY|O_LARGEFILE); src = open(src_path, O_RDONLY|O_LARGEFILE);
if (src < 0) if (src < 0)
{ {
fprintf(stderr, "Failed to open %s for reading: %s\n", src_path, strerror(errno)); fprintf(stderr, "Failed to open %s for reading: %s\n", src_path, strerror(errno));
exit(1); exit(1);
} }
if (stat(dst_path, &statbuf) != 0) read_era_invalidate_and_copy(stdin, src, metadata_block_size);
{
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); close(src);
} }
int main(int narg, char *args[]) int main(int narg, char *args[])
{ {
if (narg < 4) if (narg < 3)
{ {
fprintf(stderr, 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 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" "(c) Vitaliy Filippov, 2019+, distributed under the terms of GNU GPLv3.0 or later license\n"
"USAGE:\nera_invalidate --metadata-snapshot --written-since <ERA> <META_DEVICE> |\\\n" "USAGE:\nera_invalidate --metadata-snapshot --written-since <ERA> <META_DEVICE> |\\\n"
" era_copy <METADATA_BLOCK_SIZE> <DATA_DEVICE> <DEST_DEVICE>\n" " era_copy <METADATA_BLOCK_SIZE> <DATA_DEVICE>\n"
); );
exit(1); exit(1);
} }
@ -130,6 +116,6 @@ int main(int narg, char *args[])
fprintf(stderr, "Incorrect metadata_block_size = %d\n", bs); fprintf(stderr, "Incorrect metadata_block_size = %d\n", bs);
exit(1); exit(1);
} }
era_copy(args[2], args[3], bs); era_copy(args[2], bs);
return 0; return 0;
} }