[sheepdog] [PATCH 2/4] collie: add vdi backup support
MORITA Kazutaka
morita.kazutaka at lab.ntt.co.jp
Sun Sep 9 18:41:32 CEST 2012
This enables us to create an incremental backup between two snapshots.
$ collie vdi backup
Usage: collie vdi backup [-s snapshot] [-F from] [-a address] [-p port] [-h] <vdiname> <backup>
Options:
-s, --snapshot specify a snapshot id or tag name
-F, --from create a differential backup from the snapshot
-a, --address specify the daemon address (default: localhost)
-p, --port specify the daemon port
-h, --help display this help and exit
The created backup file contains differential data for each data
objects.
Signed-off-by: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>
---
collie/vdi.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
man/collie.8 | 3 +
2 files changed, 187 insertions(+), 0 deletions(-)
diff --git a/collie/vdi.c b/collie/vdi.c
index ca4afcb..d580450 100644
--- a/collie/vdi.c
+++ b/collie/vdi.c
@@ -12,6 +12,9 @@
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include "collie.h"
#include "treeview.h"
@@ -24,6 +27,7 @@ static struct sd_option vdi_options[] = {
{'d', "delete", 0, "delete a key"},
{'w', "writeback", 0, "use writeback mode"},
{'c', "copies", 1, "specify the data redundancy (number of copies)"},
+ {'F', "from", 1, "create a differential backup from the snapshot"},
{ 0, NULL, 0, NULL },
};
@@ -36,6 +40,8 @@ struct vdi_cmd_data {
int prealloc;
int nr_copies;
bool writeback;
+ int from_snapshot_id;
+ char from_snapshot_tag[SD_MAX_VDI_TAG_LEN];
} vdi_cmd_data = { ~0, };
struct get_vdi_info {
@@ -1524,6 +1530,174 @@ out:
return ret;
}
+/* vdi backup format */
+
+#define VDI_BACKUP_FORMAT_VERSION 1
+#define VDI_BACKUP_MAGIC 0x11921192
+
+struct backup_hdr {
+ uint32_t version;
+ uint32_t magic;
+ uint32_t nr_obj_backups;
+ uint32_t reserved;
+};
+
+struct obj_backup {
+ uint32_t idx;
+ uint32_t offset;
+ uint32_t length;
+ uint32_t reserved;
+ uint8_t data[SD_DATA_OBJ_SIZE];
+};
+
+/*
+ * discards redundant area from backup data
+ */
+static void compact_obj_backup(struct obj_backup *backup, uint8_t *from_data)
+{
+ uint8_t *p1, *p2;
+
+ p1 = backup->data;
+ p2 = from_data;
+ while (backup->length > 0 && memcmp(p1, p2, SECTOR_SIZE) == 0) {
+ p1 += SECTOR_SIZE;
+ p2 += SECTOR_SIZE;
+ backup->offset += SECTOR_SIZE;
+ backup->length -= SECTOR_SIZE;
+ }
+
+ p1 = backup->data + SD_DATA_OBJ_SIZE - SECTOR_SIZE;
+ p2 = from_data + SD_DATA_OBJ_SIZE - SECTOR_SIZE;
+ while (backup->length > 0 && memcmp(p1, p2, SECTOR_SIZE) == 0) {
+ p1 -= SECTOR_SIZE;
+ p2 -= SECTOR_SIZE;
+ backup->length -= SECTOR_SIZE;
+ }
+}
+
+static int get_obj_backup(int idx, uint32_t from_vid, uint32_t to_vid,
+ struct obj_backup *backup)
+{
+ int ret;
+ uint8_t *from_data = xzalloc(SD_DATA_OBJ_SIZE);
+
+ backup->idx = idx;
+ backup->offset = 0;
+ backup->length = SD_DATA_OBJ_SIZE;
+
+ if (to_vid) {
+ ret = sd_read_object(vid_to_data_oid(to_vid, idx), backup->data,
+ SD_DATA_OBJ_SIZE, 0);
+ if (ret != SD_RES_SUCCESS) {
+ fprintf(stderr, "Failed to read object %"PRIx32", %d\n",
+ to_vid, idx);
+ return EXIT_FAILURE;
+ }
+ } else
+ memset(backup->data, 0, SD_DATA_OBJ_SIZE);
+
+ if (from_vid) {
+ ret = sd_read_object(vid_to_data_oid(from_vid, idx), from_data,
+ SD_DATA_OBJ_SIZE, 0);
+ if (ret != SD_RES_SUCCESS) {
+ fprintf(stderr, "Failed to read object %"PRIx32", %d\n",
+ from_vid, idx);
+ return EXIT_FAILURE;
+ }
+ }
+
+ compact_obj_backup(backup, from_data);
+
+ free(from_data);
+
+ return EXIT_SUCCESS;
+}
+
+static int vdi_backup(int argc, char **argv)
+{
+ char *vdiname = argv[optind++], *backup_file;
+ int ret = EXIT_SUCCESS, fd = -1, idx, nr_objs;
+ struct sheepdog_inode *from_inode = xzalloc(sizeof(*from_inode));
+ struct sheepdog_inode *to_inode = xzalloc(sizeof(*to_inode));
+ struct backup_hdr hdr = {
+ .version = VDI_BACKUP_FORMAT_VERSION,
+ .magic = VDI_BACKUP_MAGIC,
+ };
+ struct obj_backup *backup = xzalloc(sizeof(*backup));
+
+ backup_file = argv[optind++];
+ if (!backup_file) {
+ fprintf(stderr, "Please specify a backup file\n");
+ ret = EXIT_USAGE;
+ goto out;
+ }
+
+ fd = open(backup_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open %s, %m\n", backup_file);
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ if ((!vdi_cmd_data.snapshot_id && !vdi_cmd_data.snapshot_tag[0]) ||
+ (!vdi_cmd_data.from_snapshot_id &&
+ !vdi_cmd_data.from_snapshot_tag[0])) {
+ fprintf(stderr, "Please specify snapshots with '-F' and '-s'"
+ "options\n");
+ ret = EXIT_USAGE;
+ goto out;
+ }
+
+ ret = read_vdi_obj(vdiname, vdi_cmd_data.from_snapshot_id,
+ vdi_cmd_data.from_snapshot_tag, NULL,
+ from_inode, SD_INODE_SIZE);
+ if (ret != EXIT_SUCCESS)
+ goto out;
+
+ ret = read_vdi_obj(vdiname, vdi_cmd_data.snapshot_id,
+ vdi_cmd_data.snapshot_tag, NULL, to_inode,
+ SD_INODE_SIZE);
+ if (ret != EXIT_SUCCESS)
+ goto out;
+
+ nr_objs = DIV_ROUND_UP(to_inode->vdi_size, SD_DATA_OBJ_SIZE);
+
+ ret = lseek(fd, sizeof(hdr), SEEK_SET);
+ if (ret != sizeof(hdr)) {
+ fprintf(stderr, "failed to seek, %m\n");
+ ret = EXIT_FAILURE;
+ goto out;
+ }
+
+ for (idx = 0; idx < nr_objs; idx++) {
+ uint32_t from_vid = from_inode->data_vdi_id[idx];
+ uint32_t to_vid = to_inode->data_vdi_id[idx];
+
+ if (to_vid == 0 && from_vid == 0)
+ continue;
+
+ ret = get_obj_backup(idx, from_vid, to_vid, backup);
+ if (ret != EXIT_SUCCESS)
+ goto out;
+
+ if (backup->length) {
+ hdr.nr_obj_backups++;
+ xwrite(fd, backup, sizeof(*backup) - sizeof(backup->data));
+ xwrite(fd, backup->data + backup->offset, backup->length);
+ }
+ }
+
+ xpwrite(fd, &hdr, sizeof(hdr), 0);
+out:
+ if (fd >= 0)
+ close(fd);
+
+ free(from_inode);
+ free(to_inode);
+ free(backup);
+ return ret;
+}
+
static struct subcommand vdi_cmd[] = {
{"check", "<vdiname>", "saph", "check and repair image's consistency",
NULL, SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG,
@@ -1573,6 +1747,9 @@ static struct subcommand vdi_cmd[] = {
{"flush", "<vdiname>", "aph", "flush data to cluster",
NULL, SUBCMD_FLAG_NEED_THIRD_ARG,
vdi_flush, vdi_options},
+ {"backup", "<vdiname> <backup>", "sFaph", "create an incremental backup between two snapshots",
+ NULL, SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG,
+ vdi_backup, vdi_options},
{NULL,},
};
@@ -1617,6 +1794,13 @@ static int vdi_parser(int ch, char *opt)
exit(EXIT_FAILURE);
}
vdi_cmd_data.nr_copies = nr_copies;
+ case 'F':
+ vdi_cmd_data.from_snapshot_id = strtol(opt, &p, 10);
+ if (opt == p) {
+ vdi_cmd_data.from_snapshot_id = 0;
+ strncpy(vdi_cmd_data.from_snapshot_tag, opt,
+ sizeof(vdi_cmd_data.from_snapshot_tag));
+ }
}
return 0;
diff --git a/man/collie.8 b/man/collie.8
index 9061bc8..7659e07 100644
--- a/man/collie.8
+++ b/man/collie.8
@@ -109,6 +109,9 @@ This command reads data from an image.
.BI "vdi write [-a address] [-p port] [-h] <vdiname> [<offset> [<len>]]"
This command writes data to an image.
.TP
+.BI "vdi backup [-s snapshot] [-F from] [-a address] [-p port] [-h] <vdiname> <backup>"
+This command creates an incremental backup between two snapshots.
+.TP
.BI "node kill [-a address] [-p port] [-r] [-h] <node id>"
This command kills the specified node.
.TP
--
1.7.2.5
More information about the sheepdog
mailing list