era_copy/era_copy.c

127 lines
3.2 KiB
C
Raw Normal View History

2019-02-19 00:49:07 +03:00
/**
* Parses XML block lists produced by dm-era `era_invalidate` tool
2019-02-19 01:31:09 +03:00
* from `thin-provisioning-tools` from standard input, reads specified blocks
* from the data device and creates a stream with copied blocks on standard output.
* The stream can then be consumed by `era_apply`.
2019-02-19 00:49:07 +03:00
*
* Author: Vitaliy Filippov <vitalif@yourcmc.ru>, 2019
* License: GNU GPLv3.0 or later
*/
#define _LARGEFILE64_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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;
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 era_block_size)
2019-02-19 00:49:07 +03:00
{
// read input XML
2019-02-19 01:31:09 +03:00
char* signature = "ERARANGE";
2019-02-19 00:49:07 +03:00
char buf[XML_BUFSIZE] = { 0 };
2019-02-19 01:31:09 +03:00
char c = 0;
long long start = 0, length = 0;
2019-02-19 00:49:07 +03:00
if (fgets(buf, XML_BUFSIZE, fp) == NULL)
{
fprintf(stderr, "Input block list is empty\n");
exit(0);
}
2019-02-19 01:31:09 +03:00
if (sscanf(buf, " <blocks > %c", &c) == 0 || c != 0)
2019-02-19 00:49:07 +03:00
{
fprintf(stderr, "<blocks> expected, but \"%s\" found\n", buf);
exit(1);
}
while (fgets(buf, XML_BUFSIZE, fp) != NULL)
{
if (sscanf(buf, " <range begin = \" %lld \" end = \" %lld \" />", &start, &length) == 2)
{
2019-02-19 01:31:09 +03:00
length = length-start;
2019-02-19 00:49:07 +03:00
}
else if (sscanf(buf, " <block block = \" %lld \" />", &start) == 1)
{
2019-02-19 01:31:09 +03:00
length = 1;
2019-02-19 00:49:07 +03:00
}
else
{
2019-02-19 01:31:09 +03:00
break;
2019-02-19 00:49:07 +03:00
}
start = start*era_block_size;
length = length*era_block_size;
2019-02-19 01:31:09 +03:00
// 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);
2019-02-19 00:49:07 +03:00
}
2019-02-19 01:31:09 +03:00
if (sscanf(buf, " </blocks > %c", &c) == 0 || c != 0)
2019-02-19 00:49:07 +03:00
{
fprintf(stderr, "</blocks> expected, but \"%s\" found\n", buf);
exit(1);
}
}
void era_copy(char *src_path, int era_block_size)
2019-02-19 00:49:07 +03:00
{
2019-02-19 01:31:09 +03:00
int src;
2019-02-19 00:49:07 +03:00
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);
}
read_era_invalidate_and_copy(stdin, src, era_block_size);
2019-02-19 00:49:07 +03:00
close(src);
}
int main(int narg, char *args[])
{
2019-02-19 01:31:09 +03:00
if (narg < 3)
2019-02-19 00:49:07 +03:00
{
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"
"\n"
2019-02-19 00:49:07 +03:00
"USAGE:\nera_invalidate --metadata-snapshot --written-since <ERA> <META_DEVICE> |\\\n"
" era_copy <ERA_BLOCK_SIZE> <DATA_DEVICE>\n"
"\n"
"ERA_BLOCK_SIZE is the block size you used when creating dm-era device\n"
"You can take it from `dmsetup table <DATA_DEVICE>`: it's the last number in the table\n"
"For example, in `0 3625546496 era 259:3 259:2 65536` it's 65536\n"
2019-02-19 00:49:07 +03:00
);
exit(1);
}
int bs = atoi(args[1]);
if (bs < 1)
{
fprintf(stderr, "Incorrect era_block_size = %d\n", bs);
2019-02-19 00:49:07 +03:00
exit(1);
}
2019-02-19 01:31:09 +03:00
era_copy(args[2], bs);
2019-02-19 00:49:07 +03:00
return 0;
}