[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