[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[lvm-devel] [PATCH] binary backups & pvcreate --undo (take 5)



Hi,

I am attaching yet another rebase of the binary backup code. Nothing new
other than the rebase since the last time.

*Please* review. The patch has already celebrated its 3rd birthday...

Yours,
   Petr.

Sat Nov 13 16:42:03 CET 2010  Petr Rockai <me mornfall net>
  * Implementation of binary backups (pvcreate --undo).
diff -rN -u -p old-binbackup/doc/example.conf.in new-binbackup/doc/example.conf.in
--- old-binbackup/doc/example.conf.in	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/doc/example.conf.in	2010-11-13 16:45:27.000000000 +0100
@@ -217,6 +217,11 @@ backup {
     # Remember to back up this directory regularly!
     archive_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_ARCHIVE_SUBDIR@"
 
+    # Should we make binary backups of areas overwritten when writing
+    # metadata? (currently only affects pvcreate). Required for
+    # pvcreate --undo.
+    binary = 1
+
     # What is the minimum number of archive files you wish to keep ?
     retain_min = 10
 
diff -rN -u -p old-binbackup/include/.symlinks.in new-binbackup/include/.symlinks.in
--- old-binbackup/include/.symlinks.in	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/include/.symlinks.in	2010-11-13 16:45:27.000000000 +0100
@@ -41,6 +41,7 @@
 @top_srcdir@/lib/mm/memlock.h
 @top_srcdir@/lib/mm/xlate.h
 @top_builddir@/lib/misc/configure.h
+ top_srcdir@/lib/misc/base64.h
 @top_srcdir@/lib/misc/crc.h
 @top_srcdir@/lib/misc/intl.h
 @top_srcdir@/lib/misc/util.h
diff -rN -u -p old-binbackup/lib/commands/binary_backup.c new-binbackup/lib/commands/binary_backup.c
--- old-binbackup/lib/commands/binary_backup.c	1970-01-01 01:00:00.000000000 +0100
+++ new-binbackup/lib/commands/binary_backup.c	2010-11-13 16:45:27.000000000 +0100
@@ -0,0 +1,200 @@
+#include "lib.h"
+#include "toolcontext.h"
+#include "archiver.h"
+#include "config.h"
+#include "defaults.h"
+
+#include "assert.h"
+#include "xlate.h"
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+
+int binary_backup_init(struct cmd_context *cmd, const char *uuid,
+			const char *type)
+{
+	int backupfd;
+	char *file = NULL;
+	char idstr[128] = "tmp.XXXXXX";
+
+	assert(cmd->device_backup_fd == -1);
+
+	if (!cmd->current_settings.binbackup)
+		return 1;
+
+	file = dm_malloc(strlen(cmd->archive_params->dir)
+			 + 129 + strlen(type) + 1);
+	if (!file)
+		return 0;
+
+	if (uuid)
+		strncpy(idstr, uuid, 128);
+
+	idstr[127] = 0;
+
+	sprintf(file, "%s/%s-%s.bin", cmd->archive_params->dir, type, idstr);
+
+	if (uuid) {
+		backupfd = open(file, O_RDWR | O_CREAT | O_EXCL);
+	} else {
+		file[strlen(file)-4] = 0;
+		backupfd = mkstemp(file);
+		cmd->device_backup_file = file;
+	}
+
+	if (backupfd < 0) {
+		log_error("Failed to create backup file %s: %s", file, strerror(errno));
+		dm_free(cmd->device_backup_file);
+		cmd->device_backup_file = NULL;
+		return 0;
+	}
+
+	if (uuid)
+		dm_free(file);
+
+	cmd->device_backup_fd = backupfd;
+	return 1;
+}
+
+static int _str_ends_with(const char *in, const char *suff)
+{
+	int start = strlen(in) - strlen(suff);
+	return strcmp(in+start, suff) == 0;
+}
+
+static int _cleanup_binary(struct cmd_context *cmd)
+{
+	int maxsize;
+	DIR *archive_handle;
+	struct dirent *dir;
+	const char *archive_dir;
+	char entry[PATH_MAX], selected_entry[PATH_MAX];
+	struct stat st;
+	time_t oldest = 0;
+	int total = 0;
+
+	log_verbose("_cleanup_binary called");
+
+	maxsize = find_config_tree_int(cmd, "backup/binary_max_size", 40);
+	maxsize *= 1024; /* specified in KB in configuration */
+    
+	archive_dir = get_archive_dir(cmd, "backup/archive_dir", DEFAULT_ARCHIVE_SUBDIR);
+	if (!archive_dir)
+		return 0;
+
+	archive_handle = opendir(archive_dir);
+	if (!archive_handle) {
+		log_error("Failed to open archive directory \"%s\".", archive_dir);
+		return 0;
+	}
+
+	enum { SUM, DROP } phase = SUM;
+
+	/* In first iteration, we sum up the binary archive size. In
+	   the following iterations, we drop oldest file, until the
+	   constraint is again satisfied (total <=
+	   maxsize). O(n^2). */
+
+	do {
+		rewinddir(archive_handle);
+		while ((dir = readdir(archive_handle))) {
+			dm_snprintf(entry, PATH_MAX, "%s/%s", archive_dir,
+				    dir->d_name);
+			if (_str_ends_with(dir->d_name, ".bin")) {
+				if (stat(entry, &st)) {
+					log_error("Failed to stat %s", entry);
+					return 0;
+				}
+				if (phase == SUM) {
+					total += st.st_size;
+				} else if (phase == DROP) {
+					if (oldest == 0 || st.st_mtime < oldest)
+					{
+						strcpy(selected_entry, entry);
+						oldest = st.st_mtime;
+					}
+				}
+			}
+		}
+
+		if (phase == DROP) {
+			if (oldest == 0) /* selected_entry is invalid */
+				return 0;
+			if (stat(selected_entry, &st)) {
+				log_error("Failed to stat %s", selected_entry);
+				return 0;
+			}
+			log_verbose("Removing : %s (%lld bytes)",
+				    selected_entry, st.st_size);
+			if (unlink(selected_entry) < 0) {
+				log_error("Failed to unlink %s", selected_entry);
+				return 0;
+			}
+			total -= st.st_size;
+			oldest = 0;
+		}
+		log_verbose("Current usage: %d/%d", total, maxsize);
+
+		phase = DROP;
+	} while (total > 0 && total > maxsize);
+
+	return 1;
+}
+
+int binary_backup_fini(struct cmd_context *cmd, const char *uuid)
+{
+	char *oldfile = NULL;
+	char *newfile = NULL;
+	char *tmp;
+	int error = 0;
+
+	if (cmd->device_backup_file) {
+		assert(uuid);
+		oldfile = dm_strdup(cmd->device_backup_file);
+		if (!oldfile)
+			return 0;
+		newfile = cmd->device_backup_file;
+		cmd->device_backup_file = NULL;
+
+		tmp = strstr(newfile, "tmp.");
+		strncpy(tmp, uuid, 128);
+		tmp[127] = 0;
+		strcat(tmp, ".bin");
+
+		log_debug("Linking backup file: %s -> %s", oldfile, newfile);
+
+		unlink(newfile); // XXX
+		if (link(oldfile, newfile) < 0) {
+			log_error("Failed to create %s, keeping %s around",
+                                  newfile, oldfile);
+			error = 1;
+		}
+
+		if (!error) {
+			if (unlink(oldfile) < 0) {
+				log_error("Failed to unlink %s", oldfile);
+				error = 1;
+			}
+		}
+
+		dm_free(oldfile);
+		dm_free(newfile);
+	}
+
+	if (cmd->device_backup_fd != -1) {
+		if (close(cmd->device_backup_fd) < 0) {
+			log_error("Failed to close backup file");
+                        error = 1;
+		}
+	}
+
+	cmd->device_backup_fd = -1;
+
+        _cleanup_binary(cmd); /* FIXME we ignore failure here. What
+				 should we do instead? */
+
+        if (error)
+            return 0;
+	return 1;
+}
diff -rN -u -p old-binbackup/lib/commands/toolcontext.c new-binbackup/lib/commands/toolcontext.c
--- old-binbackup/lib/commands/toolcontext.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/commands/toolcontext.c	2010-11-13 16:45:27.000000000 +0100
@@ -1027,11 +1027,27 @@ static int _init_hostname(struct cmd_con
 	return 1;
 }
 
-static int _init_backup(struct cmd_context *cmd)
+char *get_archive_dir(struct cmd_context *cmd, const char *key, const char *def)
 {
-	uint32_t days, min;
 	char default_dir[PATH_MAX];
 	const char *dir;
+    
+	if (dm_snprintf
+	    (default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir, def) == -1) {
+		log_err("Couldn't create default archive path '%s/%s'.",
+			cmd->system_dir, def);
+		return NULL;
+	}
+
+	dir = find_config_tree_str(cmd, key, default_dir);
+
+	return dm_strdup(dir);
+}
+
+static int _init_backup(struct cmd_context *cmd)
+{
+	uint32_t days, min;
+	char *dir;
 
 	if (!cmd->system_dir) {
 		log_warn("WARNING: Metadata changes will NOT be backed up");
@@ -1051,21 +1067,15 @@ static int _init_backup(struct cmd_conte
 	min = (uint32_t) find_config_tree_int(cmd, "backup/retain_min",
 					 DEFAULT_ARCHIVE_NUMBER);
 
-	if (dm_snprintf
-	    (default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
-	     DEFAULT_ARCHIVE_SUBDIR) == -1) {
-		log_error("Couldn't create default archive path '%s/%s'.",
-			  cmd->system_dir, DEFAULT_ARCHIVE_SUBDIR);
-		return 0;
-	}
-
-	dir = find_config_tree_str(cmd, "backup/archive_dir",
-			      default_dir);
+	dir = get_archive_dir(cmd, "backup/archive_dir", DEFAULT_ARCHIVE_SUBDIR);
+	if (!dir)
+		return_0;
 
 	if (!archive_init(cmd, dir, days, min,
 			  cmd->default_settings.archive)) {
 		log_debug("archive_init failed.");
-		return 0;
+		dm_free(dir);
+		return_0;
 	}
 
 	/* set up the backup */
@@ -1073,21 +1083,22 @@ static int _init_backup(struct cmd_conte
 	    find_config_tree_bool(cmd, "backup/backup",
 			     DEFAULT_BACKUP_ENABLED);
 
-	if (dm_snprintf
-	    (default_dir, sizeof(default_dir), "%s/%s", cmd->system_dir,
-	     DEFAULT_BACKUP_SUBDIR) == -1) {
-		log_error("Couldn't create default backup path '%s/%s'.",
-			  cmd->system_dir, DEFAULT_BACKUP_SUBDIR);
-		return 0;
-	}
+	cmd->default_settings.binbackup =
+	    find_config_tree_bool(cmd, "backup/binary",
+			     DEFAULT_BACKUP_ENABLED);
 
-	dir = find_config_tree_str(cmd, "backup/backup_dir", default_dir);
+	dm_free(dir);
+	dir = get_archive_dir(cmd, "backup/backup_dir", DEFAULT_BACKUP_SUBDIR);
+	if (!dir)
+		return_0;
 
 	if (!backup_init(cmd, dir, cmd->default_settings.backup)) {
 		log_debug("backup_init failed.");
+		dm_free(dir);
 		return 0;
 	}
 
+	dm_free(dir);
 	return 1;
 }
 
@@ -1136,6 +1147,8 @@ struct cmd_context *create_toolcontext(u
 	cmd->handles_missing_pvs = 0;
 	cmd->handles_unknown_segments = 0;
 	cmd->hosttags = 0;
+	cmd->device_backup_fd = -1;
+	cmd->device_backup_file = NULL;
 	dm_list_init(&cmd->formats);
 	dm_list_init(&cmd->segtypes);
 	dm_list_init(&cmd->tags);
@@ -1315,6 +1328,11 @@ int refresh_toolcontext(struct cmd_conte
 
 	cmd->config_valid = 0;
 
+	/* FIXME? */
+	cmd->device_backup_fd = -1;
+	dm_free(cmd->device_backup_file);
+	cmd->device_backup_file = NULL;
+
 	cmd->hosttags = 0;
 
 	if (!_init_lvm_conf(cmd))
diff -rN -u -p old-binbackup/lib/commands/toolcontext.h new-binbackup/lib/commands/toolcontext.h
--- old-binbackup/lib/commands/toolcontext.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/commands/toolcontext.h	2010-11-13 16:45:27.000000000 +0100
@@ -33,6 +33,7 @@ struct config_info {
 	int suffix;
 	int archive;		/* should we archive ? */
 	int backup;		/* should we backup ? */
+	int binbackup;		/* should we do binary backups ? */
 	int read_ahead;		/* DM_READ_AHEAD_NONE or _AUTO */
 	int udev_rules;
 	int udev_sync;
@@ -97,6 +98,9 @@ struct cmd_context {
 	char dev_dir[PATH_MAX];
 	char proc_dir[PATH_MAX];
 	char sysfs_dir[PATH_MAX]; /* FIXME Use global value instead. */
+
+	int device_backup_fd;
+	char *device_backup_file;
 };
 
 /*
@@ -113,4 +117,12 @@ int init_lvmcache_orphans(struct cmd_con
 
 struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format);
 
+/* Returns a dm_alloc'ated string with the archive or backup dir,
+   depending on key and def. */
+char *get_archive_dir(struct cmd_context *cmd, const char *key, const char *def);
+
+int binary_backup_init(struct cmd_context *cmd, const char*id,
+		       const char *type);
+int binary_backup_fini(struct cmd_context *cmd, const char*id);
+
 #endif
diff -rN -u -p old-binbackup/lib/config/config.c new-binbackup/lib/config/config.c
--- old-binbackup/lib/config/config.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/config/config.c	2010-11-13 16:45:27.000000000 +0100
@@ -46,18 +46,6 @@ enum {
 	TOK_EOF
 };
 
-struct parser {
-	const char *fb, *fe;		/* file limits */
-
-	int t;			/* token limits and type */
-	const char *tb, *te;
-
-	int fd;			/* descriptor for file being parsed */
-	int line;		/* line number we are on */
-
-	struct dm_pool *mem;
-};
-
 struct cs {
 	struct config_tree cft;
 	struct dm_pool *mem;
@@ -68,23 +56,15 @@ struct cs {
 	struct device *dev;
 };
 
-struct output_line {
-	FILE *fp;
-	struct dm_pool *mem;
-	putline_fn putline;
-	void *putline_baton;
-};
-
-static void _get_token(struct parser *p, int tok_prev);
-static void _eat_space(struct parser *p);
-static struct config_node *_file(struct parser *p);
-static struct config_node *_section(struct parser *p);
-static struct config_value *_value(struct parser *p);
-static struct config_value *_type(struct parser *p);
-static int _match_aux(struct parser *p, int t);
+static void _get_token(struct config_parser *p, int tok_prev);
+static void _eat_space(struct config_parser *p);
+static struct config_node *_file(struct config_parser *p);
+static struct config_value *_value(struct config_parser *p);
+static struct config_value *_type(struct config_parser *p);
+static int _match_aux(struct config_parser *p, int t);
 static struct config_value *_create_value(struct dm_pool *mem);
 static struct config_node *_create_node(struct dm_pool *mem);
-static char *_dup_tok(struct parser *p);
+static char *_dup_tok(struct config_parser *p);
 
 static const int sep = '/';
 
@@ -148,7 +128,7 @@ void destroy_config_tree(struct config_t
 	dm_pool_destroy(c->mem);
 }
 
-static int _parse_config_file(struct parser *p, struct config_tree *cft)
+static int _parse_config_file(struct config_parser *p, struct config_tree *cft)
 {
 	p->tb = p->te = p->fb;
 	p->line = 1;
@@ -164,7 +144,7 @@ struct config_tree *create_config_tree_f
 {
 	struct cs *c;
 	struct config_tree *cft;
-	struct parser *p;
+	struct config_parser *p;
 
 	if (!(cft = create_config_tree(NULL, 0)))
 		return_NULL;
@@ -204,7 +184,7 @@ int read_config_fd(struct config_tree *c
 		   checksum_fn_t checksum_fn, uint32_t checksum)
 {
 	struct cs *c = (struct cs *) cft;
-	struct parser *p;
+	struct config_parser *p;
 	int r = 0;
 	int use_mmap = 1;
 	off_t mmap_offset = 0;
@@ -266,6 +246,62 @@ int read_config_fd(struct config_tree *c
 	return r;
 }
 
+struct config_parser *create_config_parser(const char *fn)
+{
+	struct config_parser *p;
+	struct dm_pool *mem;
+	struct stat info;
+
+	if (stat(fn, &info)) {
+		log_sys_error("stat", fn);
+		return NULL;
+	}
+
+	if (!S_ISREG(info.st_mode)) {
+		log_error("%s is not a regular file", fn);
+		return NULL;
+	}
+
+	if (info.st_size == 0) {
+		log_verbose("%s is empty", fn);
+		return NULL;
+	}
+
+	if (!(mem = dm_pool_create(fn, 4096)))
+		return_NULL;
+	if (!(p = dm_pool_alloc(mem, sizeof(struct config_parser))))
+		return_NULL;
+	p->mem = mem;
+
+	if ((p->fd = open(fn, O_RDONLY)) < 0)
+		goto_bad;
+
+	p->fb = mmap((caddr_t) 0, info.st_size, PROT_READ, MAP_PRIVATE, p->fd, 0);
+	if (p->fb == (caddr_t) (-1)) {
+		log_sys_error("mmap", fn);
+		close(p->fd);
+		goto_bad;
+	}
+
+	p->fe = p->fb + info.st_size;
+
+	p->tb = p->te = p->fb;
+	p->line = 1;
+	_get_token(p, TOK_SECTION_E);
+
+	return p;
+ bad:
+	dm_pool_destroy(mem);
+	return NULL;
+}
+
+void destroy_config_parser(struct config_parser *p)
+{
+	munmap((void *) p->fb, p->fe - p->fb);
+	close(p->fd);
+	dm_pool_destroy(p->mem);
+}
+
 int read_config_file(struct config_tree *cft)
 {
 	struct cs *c = (struct cs *) cft;
@@ -453,8 +489,8 @@ static int _write_value(struct output_li
 	return 1;
 }
 
-static int _write_config(const struct config_node *n, int only_one,
-			 struct output_line *outline, int level)
+int write_config(const struct config_node *n, int only_one,
+		 struct output_line *outline, int level)
 {
 	char space[MAX_INDENT + 1];
 	int l = (level < MAX_INDENT) ? level : MAX_INDENT;
@@ -476,7 +512,7 @@ static int _write_config(const struct co
 			line_append(" {");
 			if (!_line_end(outline))
 				return_0;
-			_write_config(n->child, 0, outline, level + 1);
+			write_config(n->child, 0, outline, level + 1);
 			if (!_line_start(outline))
 				return_0;
 			line_append("%s}", space);
@@ -513,7 +549,7 @@ int write_config_node(const struct confi
 	outline.mem = dm_pool_create("config_line", 1024);
 	outline.putline = putline;
 	outline.putline_baton = baton;
-	if (!_write_config(cn, 0, &outline, 0)) {
+	if (!write_config(cn, 0, &outline, 0)) {
 		dm_pool_destroy(outline.mem);
 		return_0;
 	}
@@ -541,13 +577,13 @@ int write_config_file(struct config_tree
 
 	log_verbose("Dumping configuration to %s", file);
 	if (!argc) {
-		if (!_write_config(cft->root, 0, &outline, 0)) {
+		if (!write_config(cft->root, 0, &outline, 0)) {
 			log_error("Failure while writing to %s", file);
 			r = 0;
 		}
 	} else while (argc--) {
 		if ((cn = find_config_node(cft->root, *argv))) {
-			if (!_write_config(cn, 1, &outline, 0)) {
+			if (!write_config(cn, 1, &outline, 0)) {
 				log_error("Failure while writing to %s", file);
 				r = 0;
 			}
@@ -570,11 +606,11 @@ int write_config_file(struct config_tree
 /*
  * parser
  */
-static struct config_node *_file(struct parser *p)
+static struct config_node *_file(struct config_parser *p)
 {
 	struct config_node *root = NULL, *n, *l = NULL;
 	while (p->t != TOK_EOF) {
-		if (!(n = _section(p)))
+		if (!(n = parse_config_section(p)))
 			return_0;
 
 		if (!root)
@@ -587,10 +623,14 @@ static struct config_node *_file(struct
 	return root;
 }
 
-static struct config_node *_section(struct parser *p)
+struct config_node *parse_config_section(struct config_parser *p)
 {
 	/* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
 	struct config_node *root, *n, *l = NULL;
+
+	if (p->t == TOK_EOF)
+		return NULL;
+
 	if (!(root = _create_node(p->mem)))
 		return_0;
 
@@ -602,7 +642,7 @@ static struct config_node *_section(stru
 	if (p->t == TOK_SECTION_B) {
 		match(TOK_SECTION_B);
 		while (p->t != TOK_SECTION_E) {
-			if (!(n = _section(p)))
+			if (!(n = parse_config_section(p)))
 				return_0;
 
 			if (!root->child)
@@ -622,7 +662,7 @@ static struct config_node *_section(stru
 	return root;
 }
 
-static struct config_value *_value(struct parser *p)
+static struct config_value *_value(struct config_parser *p)
 {
 	/* '[' TYPE* ']' | TYPE */
 	struct config_value *h = NULL, *l, *ll = NULL;
@@ -658,7 +698,7 @@ static struct config_value *_value(struc
 	return h;
 }
 
-static struct config_value *_type(struct parser *p)
+static struct config_value *_type(struct config_parser *p)
 {
 	/* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
 	struct config_value *v = _create_value(p->mem);
@@ -708,7 +748,7 @@ static struct config_value *_type(struct
 	return v;
 }
 
-static int _match_aux(struct parser *p, int t)
+static int _match_aux(struct config_parser *p, int t)
 {
 	if (p->t != t)
 		return 0;
@@ -720,7 +760,7 @@ static int _match_aux(struct parser *p,
 /*
  * tokeniser
  */
-static void _get_token(struct parser *p, int tok_prev)
+static void _get_token(struct config_parser *p, int tok_prev)
 {
 	int values_allowed = 0;
 
@@ -838,7 +878,7 @@ static void _get_token(struct parser *p,
 	p->te = te;
 }
 
-static void _eat_space(struct parser *p)
+static void _eat_space(struct config_parser *p)
 {
 	while ((p->tb != p->fe) && (*p->tb)) {
 		if (*p->te == '#')
@@ -873,7 +913,7 @@ static struct config_node *_create_node(
 	return dm_pool_zalloc(mem, sizeof(struct config_node));
 }
 
-static char *_dup_tok(struct parser *p)
+static char *_dup_tok(struct config_parser *p)
 {
 	size_t len = p->te - p->tb;
 	char *str = dm_pool_alloc(p->mem, len + 1);
diff -rN -u -p old-binbackup/lib/config/config.h new-binbackup/lib/config/config.h
--- old-binbackup/lib/config/config.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/config/config.h	2010-11-13 16:45:27.000000000 +0100
@@ -120,4 +120,34 @@ const char *config_parent_name(const str
 
 struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
 				      int siblings);
+
+struct output_line {
+	FILE *fp;
+	struct dm_pool *mem;
+	putline_fn putline;
+	void *putline_baton;
+};
+
+int write_config(const struct config_node *n, int only_one,
+		 struct output_line *outline, int level);
+
+struct config_parser;
+
+struct config_parser {
+	const char *fb, *fe;		/* file limits */
+
+	int t;			/* token limits and type */
+	const char *tb, *te;
+
+	int fd;			/* descriptor for file being parsed */
+	int line;		/* line number we are on */
+
+	struct dm_pool *mem;
+};
+
+struct config_parser *create_config_parser(const char *file);
+void destroy_config_parser(struct config_parser *);
+
+struct config_node *parse_config_section(struct config_parser *p);
+
 #endif
diff -rN -u -p old-binbackup/lib/device/dev-backup.c new-binbackup/lib/device/dev-backup.c
--- old-binbackup/lib/device/dev-backup.c	1970-01-01 01:00:00.000000000 +0100
+++ new-binbackup/lib/device/dev-backup.c	2010-11-13 16:45:27.000000000 +0100
@@ -0,0 +1,273 @@
+#include "lib.h"
+#include "lvm-types.h"
+#include "device.h"
+#include "config.h"
+
+#include "base64.h"
+#include <assert.h>
+#include <unistd.h>
+
+struct block_list {
+	struct dm_list list;
+	int64_t offset, length;
+	char *new;
+	char *old;
+};
+
+static int _dev_restore(struct device *dev, struct block_list *blocks)
+{
+	struct block_list *b;
+	dm_list_iterate_back_items(b, &blocks->list) {
+		if (!dev_write(dev, b->offset, b->length, b->old))
+			return_0;
+	}
+	return 1;
+}
+
+static int _dev_restore_check_block(struct device *dev,
+				    struct dm_pool *mem,
+				    struct block_list block,
+				    struct block_list *done)
+{
+	struct block_list before, after, *test;
+	int start_test, start_block, overlap;
+	char *buf;
+
+	dm_list_iterate_items(test, &done->list) {
+		start_block = start_test = -1;
+		if (test->offset <= block.offset &&
+		    test->offset + test->length >= block.offset) {
+			start_test = block.offset - test->offset;
+			start_block = 0;
+		}
+		if (block.offset <= test->offset &&
+		    block.offset + block.length >= test->offset) {
+			start_block = test->offset - block.offset;
+			start_test = 0;
+		}
+		overlap = min(test->length - start_test, block.length - start_block);
+		if (start_block >= 0 && start_test >= 0 && overlap > 0) {
+			if (memcmp(block.new + start_block, test->old + start_test, overlap))
+				return 0;
+		} else
+			continue; /* No overlap, try next block. */
+
+		/*
+		 * Check the parts of the "block" that are before and after
+		 * what we already matched (against the "test" block above).
+		 */
+		before.offset = block.offset;
+		before.length = test->offset - block.offset;
+		before.old = block.old;
+		before.new = block.new;
+
+		after.offset = test->offset + test->length;
+		after.length = block.offset + block.length - (test->offset + test->length);
+		after.old = block.old + (after.offset - block.offset);
+		after.new = block.new + (after.offset - block.offset);
+
+		if (after.length > 0 && !_dev_restore_check_block(dev, mem, after, done))
+			return 0;
+		if (before.length > 0 && !_dev_restore_check_block(dev, mem, before, done))
+			return 0;
+		return 1;
+	}
+
+	/* No match in the block list, check against the device. */
+	buf = alloca(block.length);
+	if (!dev_read(dev, block.offset, block.length, buf))
+		return 0;
+	if (memcmp(block.new, buf, block.length))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * TODO, this check needs to be relaxed for MDA blocks, ie. MDA block may be
+ * overwritten, whenever an MDA block of same size is expected; in other words,
+ * MDA payload needs to be disregarded in the comparison; this is necessary to
+ * facilitate restores after MDA has been modified; MDA contents are backed up
+ * via the metadata backup system, semi-orthogonal to binary backups, so we
+ * will rely on it for those purposes.
+ */
+static int _dev_restore_check(struct device *dev, struct block_list *blocks)
+{
+	struct block_list *b, *tmp, done;
+	struct dm_pool *mem = dm_pool_create("dev_restore", 4096);
+
+	if (!mem)
+		return_0;
+
+	dm_list_init(&done.list);
+
+	dm_list_iterate_back_items(b, &blocks->list) {
+		if (!_dev_restore_check_block(dev, mem, *b, &done))
+			goto_bad;
+		tmp = dm_pool_alloc(mem, sizeof(struct block_list));
+		*tmp = *b;
+		dm_list_add(&done.list, &tmp->list);
+	}
+
+	dm_pool_destroy(mem);
+	return 1;
+ bad:
+	dm_pool_destroy(mem);
+	return 0;
+}
+
+static int _read_block(struct config_parser *parse, struct block_list **_block)
+{
+	int64_t old_len, new_len;
+	struct config_node *_offset, *_old, *_new;
+	struct block_list *block;
+
+	block = *_block = dm_pool_alloc(parse->mem, sizeof(struct block_list));
+	if (!block)
+		return_0;
+
+	_offset = parse_config_section(parse);
+	_old = parse_config_section(parse);
+	_new = parse_config_section(parse);
+
+	/* FIXME we assume EOF here */
+	if (!_offset || !_old || !_new) {
+		*_block = 0;
+		return 1;
+	}
+
+	if (_offset->v->type != CFG_INT ||
+	    _old->v->type != CFG_STRING ||
+	    _new->v->type != CFG_STRING)
+		return_0;
+
+	block->offset = _offset->v->v.i;
+	block->old = dm_pool_alloc(parse->mem, sizeof(char) * 512);
+	block->new = dm_pool_alloc(parse->mem, sizeof(char) * 512);
+	old_len = base64_decode(_old->v->v.str, block->old, 512);
+	new_len = base64_decode(_new->v->v.str, block->new, 512);
+
+	if (old_len != new_len) {
+		log_error("ERROR: Size mismatch parsing backup file '%s'", "FIXME");
+		return_0;
+	}
+	block->length = old_len;
+
+	return 1;
+}
+
+/* write the blocks from the backup file in reverse order */
+int dev_restore(struct device *dev, const char *file, int skip)
+{
+	struct block_list list;
+	struct config_parser *cp;
+
+	if (!(cp = create_config_parser(file)))
+		return_0;
+
+	dm_list_init(&list.list);
+	while (1) {
+		struct block_list *new;
+		if (!_read_block(cp, &new))
+			goto_bad;
+		if (new == NULL)
+			break;
+		dm_list_add(&list.list, &new->list);
+	}
+
+	/* we now have a reverse list of blocks in "list" */
+
+	if (!_dev_restore_check(dev, &list))
+		goto_bad;
+
+	if (!_dev_restore(dev, &list))
+		goto_bad;
+
+	destroy_config_parser(cp);
+	return 1;
+ bad:
+	destroy_config_parser(cp);
+	return 0;
+}
+
+/* We expect that length <= 512. See dev_backup. */
+static int _dev_backup(struct device *dev, uint64_t offset, size_t length,
+		       void *write_buf, struct output_line *out)
+{
+	struct config_node _n_offset, _n_old, _n_new;
+	struct config_value _offset, _old, _new;
+	char read_buf[512], old_buf[685], new_buf[685];
+
+	if (!dev_read(dev, offset, length, read_buf))
+		return_0;
+	base64_encode(read_buf, old_buf, length);
+	base64_encode(write_buf, new_buf, length);
+
+	_n_offset.key = dm_strdup("offset");
+	_n_offset.parent = _n_offset.child = 0;
+	_n_offset.v = &_offset;
+	_n_offset.sib = &_n_old;
+	_offset.type = CFG_INT;
+	_offset.v.i = offset;
+	_offset.next = 0;
+
+	_n_old.key = dm_strdup("old");
+	_n_old.parent = _n_old.child = 0;
+	_n_old.sib = &_n_new;
+	_n_old.v = &_old;
+	_old.type = CFG_STRING;
+	_old.v.str = old_buf;
+	_old.next = 0;
+
+	_n_new.key = dm_strdup("new");
+	_n_new.parent = _n_new.child = 0;
+	_n_new.sib = 0;
+	_n_new.v = &_new;
+	_new.type = CFG_STRING;
+	_new.v.str = new_buf;
+	_new.next = 0;
+
+	if (!write_config(&_n_offset, 0, out, 0))
+		return_0;
+
+	return 1;
+}
+
+int dev_backup(struct device *dev, uint64_t offset, size_t length,
+	       void *write_buf, int fd)
+{
+	size_t window = 0;
+	const size_t bs = 512;
+	struct output_line out;
+
+	out.fp = fdopen(fd, "w+");
+	out.mem = dm_pool_create("foobar", 4096);
+	out.putline = NULL;
+
+	while (window < length) {
+		if (!_dev_backup(dev, offset + window,
+				 min(bs, length - window),
+				 write_buf + window,
+				 &out)) {
+			dm_pool_destroy(out.mem);
+			return_0;
+		}
+		window += bs;
+	}
+
+	if (fflush(out.fp) || fsync(fd))
+		return 0;
+
+	dm_pool_destroy(out.mem);
+
+	return 1;
+}
+
+int dev_write_backup(struct device *dev, uint64_t offset, size_t len,
+		     void *buffer, int backupfd)
+{
+	if (backupfd > 0)
+		if (!dev_backup(dev, offset, len, buffer, backupfd))
+			return 0;
+	return dev_write(dev, offset, len, buffer);
+}
diff -rN -u -p old-binbackup/lib/device/device.h new-binbackup/lib/device/device.h
--- old-binbackup/lib/device/device.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/device/device.h	2010-11-13 16:45:27.000000000 +0100
@@ -84,9 +84,18 @@ const char *dev_name(const struct device
 int dev_read(struct device *dev, uint64_t offset, size_t len, void *buffer);
 int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
 		      uint64_t offset2, size_t len2, void *buf);
+
 int dev_write(struct device *dev, uint64_t offset, size_t len, void *buffer);
 int dev_append(struct device *dev, size_t len, void *buffer);
 int dev_set(struct device *dev, uint64_t offset, size_t len, int value);
+
+int dev_restore(struct device *dev, const char *file, int skip);
+int dev_backup(struct device *dev, uint64_t offset, size_t len, void *buffer, int fd);
+int dev_write_backup(struct device *dev, uint64_t offset, size_t len,
+		     void *buffer, int backupfd);
+int dev_set_backup(struct device *dev, uint64_t offset, size_t len, int value,
+		   int backupfd);
+
 void dev_flush(struct device *dev);
 
 struct device *dev_create_file(const char *filename, struct device *dev,
diff -rN -u -p old-binbackup/lib/device/dev-io.c new-binbackup/lib/device/dev-io.c
--- old-binbackup/lib/device/dev-io.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/device/dev-io.c	2010-11-13 16:45:27.000000000 +0100
@@ -26,6 +26,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
+#include <byteswap.h>
 
 #ifdef linux
 #  define u64 uint64_t		/* Missing without __KERNEL__ */
@@ -45,6 +46,16 @@
 #  define BLKSIZE_SHIFT 0
 #endif
 
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define ntohll(x)       (x)
+#define htonll(x)       (x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define ntohll(x)       __bswap_64(x)
+#define htonll(x)       __bswap_64(x)
+#else
+#error Define ntohll and htonll macros for your platform
+#endif
+
 #ifdef O_DIRECT_SUPPORT
 #  ifndef O_DIRECT
 #    error O_DIRECT support configured but O_DIRECT definition not found in headers
@@ -715,6 +726,12 @@ int dev_write(struct device *dev, uint64
 
 int dev_set(struct device *dev, uint64_t offset, size_t len, int value)
 {
+	return dev_set_backup(dev, offset, len, value, -1);
+}
+
+int dev_set_backup(struct device *dev, uint64_t offset, size_t len, int value,
+		   int backupfd)
+{
 	size_t s;
 	char buffer[4096] __attribute__((aligned(8)));
 
@@ -732,7 +749,7 @@ int dev_set(struct device *dev, uint64_t
 	memset(buffer, value, sizeof(buffer));
 	while (1) {
 		s = len > sizeof(buffer) ? sizeof(buffer) : len;
-		if (!dev_write(dev, offset, s, buffer))
+		if (!dev_write_backup(dev, offset, s, buffer, backupfd))
 			break;
 
 		len -= s;
diff -rN -u -p old-binbackup/lib/format_text/archiver.c new-binbackup/lib/format_text/archiver.c
--- old-binbackup/lib/format_text/archiver.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/format_text/archiver.c	2010-11-13 16:45:27.000000000 +0100
@@ -24,13 +24,6 @@
 
 #include <unistd.h>
 
-struct archive_params {
-	int enabled;
-	char *dir;
-	unsigned int keep_days;
-	unsigned int keep_number;
-};
-
 struct backup_params {
 	int enabled;
 	char *dir;
diff -rN -u -p old-binbackup/lib/format_text/archiver.h new-binbackup/lib/format_text/archiver.h
--- old-binbackup/lib/format_text/archiver.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/format_text/archiver.h	2010-11-13 16:45:27.000000000 +0100
@@ -18,6 +18,13 @@
 
 #include "metadata-exported.h"
 
+struct archive_params {
+	int enabled;
+	char *dir;
+	unsigned int keep_days;
+	unsigned int keep_number;
+};
+
 /*
  * There are two operations that come under the general area of
  * backups.  'Archiving' occurs just before a volume group
diff -rN -u -p old-binbackup/lib/format_text/format-text.c new-binbackup/lib/format_text/format-text.c
--- old-binbackup/lib/format_text/format-text.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/format_text/format-text.c	2010-11-13 16:45:27.000000000 +0100
@@ -384,7 +384,8 @@ static int _raw_write_mda_header(const s
 					     MDA_HEADER_SIZE -
 					     sizeof(mdah->checksum_xl)));
 
-	if (!dev_write(dev, start_byte, MDA_HEADER_SIZE, mdah))
+	if (!dev_write_backup(dev, start_byte, MDA_HEADER_SIZE, mdah,
+			      fmt->cmd->device_backup_fd))
 		return_0;
 
 	return 1;
@@ -1342,9 +1343,10 @@ static int _mda_setup(const struct forma
 			     mda_size1, metadataignore))
 			return 0;
 
-		if (!dev_set((struct device *) pv->dev, start1,
-			     (size_t) (mda_size1 >
-				       wipe_size ? : mda_size1), 0)) {
+		if (!dev_set_backup((struct device *) pv->dev, start1,
+				    (size_t) (mda_size1 >
+					      wipe_size ? : mda_size1), 0,
+				    fmt->cmd->device_backup_fd)) {
 			log_error("Failed to wipe new metadata area");
 			return 0;
 		}
@@ -1387,9 +1389,10 @@ static int _mda_setup(const struct forma
 	if (mda_size2) {
 		if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start2,
 			     mda_size2, metadataignore)) return 0;
-		if (!dev_set(pv->dev, start2,
-			     (size_t) (mda_size1 >
-				       wipe_size ? : mda_size1), 0)) {
+		if (!dev_set_backup(pv->dev, start2,
+				    (size_t) (mda_size1 >
+					      wipe_size ? : mda_size1), 0,
+				    fmt->cmd->device_backup_fd)) {
 			log_error("Failed to wipe new metadata area");
 			return 0;
 		}
@@ -1541,7 +1544,7 @@ static int _text_pv_write(const struct f
 		}
 	}
 
-	if (!label_write(pv->dev, label)) {
+	if (!label_write_backup(pv->dev, label, fmt->cmd->device_backup_fd)) {
 		dev_close(pv->dev);
 		return_0;
 	}
diff -rN -u -p old-binbackup/lib/label/label.c new-binbackup/lib/label/label.c
--- old-binbackup/lib/label/label.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/label/label.c	2010-11-13 16:45:27.000000000 +0100
@@ -190,6 +190,12 @@ static struct labeller *_find_labeller(s
 /* FIXME Also wipe associated metadata area headers? */
 int label_remove(struct device *dev)
 {
+	return label_remove_backup(dev, -1);
+}
+
+/* FIXME Also wipe associated metadata area headers? */
+int label_remove_backup(struct device *dev, int backupfd)
+{
 	char buf[LABEL_SIZE] __attribute__((aligned(8)));
 	char readbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
 	int r = 1;
@@ -240,8 +246,8 @@ int label_remove(struct device *dev)
 		if (wipe) {
 			log_info("%s: Wiping label at sector %" PRIu64,
 				 dev_name(dev), sector);
-			if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE,
-				       buf)) {
+			if (!dev_write_backup(dev, sector << SECTOR_SHIFT,
+					      LABEL_SIZE, buf, backupfd)) {
 				log_error("Failed to remove label from %s at "
 					  "sector %" PRIu64, dev_name(dev),
 					  sector);
@@ -297,7 +303,7 @@ int label_read(struct device *dev, struc
 }
 
 /* Caller may need to use label_get_handler to create label struct! */
-int label_write(struct device *dev, struct label *label)
+int label_write_backup(struct device *dev, struct label *label, int backupfd)
 {
 	char buf[LABEL_SIZE] __attribute__((aligned(8)));
 	struct label_header *lh = (struct label_header *) buf;
@@ -332,7 +338,8 @@ int label_write(struct device *dev, stru
 	log_info("%s: Writing label to sector %" PRIu64 " with stored offset %"
 		 PRIu32 ".", dev_name(dev), label->sector,
 		 xlate32(lh->offset_xl));
-	if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) {
+	if (!dev_write_backup(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE,
+			      buf, backupfd)) {
 		log_debug("Failed to write label to %s", dev_name(dev));
 		r = 0;
 	}
@@ -343,6 +350,11 @@ int label_write(struct device *dev, stru
 	return r;
 }
 
+int label_write(struct device *dev, struct label *label)
+{
+	return label_write_backup(dev, label, -1);
+}
+
 /* Unused */
 int label_verify(struct device *dev)
 {
diff -rN -u -p old-binbackup/lib/label/label.h new-binbackup/lib/label/label.h
--- old-binbackup/lib/label/label.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/label/label.h	2010-11-13 16:45:27.000000000 +0100
@@ -96,9 +96,14 @@ int label_register_handler(const char *n
 struct labeller *label_get_handler(const char *name);
 
 int label_remove(struct device *dev);
+int label_remove_backup(struct device *dev, int backupfd);
+
 int label_read(struct device *dev, struct label **result,
 		uint64_t scan_sector);
+
 int label_write(struct device *dev, struct label *label);
+int label_write_backup(struct device *dev, struct label *label, int backupfd);
+
 int label_verify(struct device *dev);
 struct label *label_create(struct labeller *labeller);
 void label_destroy(struct label *label);
diff -rN -u -p old-binbackup/lib/Makefile.in new-binbackup/lib/Makefile.in
--- old-binbackup/lib/Makefile.in	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/Makefile.in	2010-11-13 16:45:27.000000000 +0100
@@ -40,11 +40,13 @@ SOURCES =\
 	activate/activate.c \
 	cache/lvmcache.c \
 	commands/toolcontext.c \
+	commands/binary_backup.c \
 	config/config.c \
 	datastruct/btree.c \
 	datastruct/str_list.c \
 	device/dev-cache.c \
 	device/dev-io.c \
+	device/dev-backup.c \
 	device/dev-md.c \
 	device/dev-swap.c \
 	device/dev-luks.c \
@@ -85,6 +87,7 @@ SOURCES =\
 	metadata/segtype.c \
 	metadata/snapshot_manip.c \
 	metadata/vg.c \
+	misc/base64.c \
 	misc/crc.c \
 	misc/lvm-exec.c \
 	misc/lvm-file.c \
diff -rN -u -p old-binbackup/lib/metadata/lv_manip.c new-binbackup/lib/metadata/lv_manip.c
--- old-binbackup/lib/metadata/lv_manip.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/metadata/lv_manip.c	2010-11-13 16:45:27.000000000 +0100
@@ -2951,6 +2951,9 @@ int set_lv(struct cmd_context *cmd, stru
 		sectors = lv->size;
 
 	dev_set(dev, UINT64_C(0), (size_t) sectors << SECTOR_SHIFT, value);
+
+	dev_set_backup(dev, UINT64_C(0), sectors << SECTOR_SHIFT,
+		       value, cmd->device_backup_fd);
 	dev_flush(dev);
 	dev_close_immediate(dev);
 
diff -rN -u -p old-binbackup/lib/metadata/metadata.c new-binbackup/lib/metadata/metadata.c
--- old-binbackup/lib/metadata/metadata.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/metadata/metadata.c	2010-11-13 16:45:27.000000000 +0100
@@ -1484,6 +1484,9 @@ struct physical_volume * pvcreate_single
 		goto error;
 	}
 
+	if (!binary_backup_init(cmd, NULL, "pv"))
+		goto error;
+
 	dm_list_init(&mdas);
 	if (!(pv = pv_create(cmd, dev, pp->idp, pp->size,
 			     pp->data_alignment, pp->data_alignment_offset,
@@ -1498,7 +1501,7 @@ struct physical_volume * pvcreate_single
 		    " available sectors", pv_name, pv_size(pv));
 
 	/* Wipe existing label first */
-	if (!label_remove(pv_dev(pv))) {
+	if (!label_remove_backup(pv_dev(pv), cmd->device_backup_fd)) {
 		log_error("Failed to wipe existing label on %s", pv_name);
 		goto error;
 	}
@@ -1510,7 +1513,8 @@ struct physical_volume * pvcreate_single
 			goto error;
 		}
 
-		if (!dev_set(dev, UINT64_C(0), (size_t) 2048, 0)) {
+		if (!dev_set_backup(dev, UINT64_C(0), (size_t) 2048, 0,
+				    cmd->device_backup_fd)) {
 			log_error("%s not wiped: aborting", pv_name);
 			dev_close(dev);
 			goto error;
@@ -1526,6 +1530,15 @@ struct physical_volume * pvcreate_single
 		goto error;
 	}
 
+	cmd->device_backup_fd = -1;
+
+	struct id _id = pv_id(pv);
+	char _uuid[33];
+	strncpy(_uuid, (char*)_id.uuid, 32);
+	_uuid[32] = 0;
+	if (!binary_backup_fini(cmd, _uuid))
+		goto error;
+
 	log_print("Physical volume \"%s\" successfully created", pv_name);
 
 	return pv;
diff -rN -u -p old-binbackup/lib/metadata/metadata-exported.h new-binbackup/lib/metadata/metadata-exported.h
--- old-binbackup/lib/metadata/metadata-exported.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/metadata/metadata-exported.h	2010-11-13 16:45:27.000000000 +0100
@@ -743,4 +743,6 @@ int vgcreate_params_validate(struct cmd_
 int validate_vg_rename_params(struct cmd_context *cmd,
 			      const char *vg_name_old,
 			      const char *vg_name_new);
+
+struct id pv_id(const struct physical_volume *pv);
 #endif
diff -rN -u -p old-binbackup/lib/metadata/metadata.h new-binbackup/lib/metadata/metadata.h
--- old-binbackup/lib/metadata/metadata.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/lib/metadata/metadata.h	2010-11-13 16:45:27.000000000 +0100
@@ -401,7 +401,6 @@ int fixup_imported_mirrors(struct volume
 /*
  * Begin skeleton for external LVM library
  */
-struct id pv_id(const struct physical_volume *pv);
 const struct format_type *pv_format_type(const struct physical_volume *pv);
 struct id pv_vgid(const struct physical_volume *pv);
 
diff -rN -u -p old-binbackup/lib/misc/base64.c new-binbackup/lib/misc/base64.c
--- old-binbackup/lib/misc/base64.c	1970-01-01 01:00:00.000000000 +0100
+++ new-binbackup/lib/misc/base64.c	2010-11-13 16:45:27.000000000 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "lib.h"
+#include "base64.h"
+
+static void _encode_block(unsigned char *in, unsigned char *out, int length) {
+	static const char *c =
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	out[0] = c[ in[0] >> 2 ];
+	out[1] = c[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
+	out[2] = (length > 1 ?
+		  c[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
+	out[3] = (length > 2 ? c[ in[2] & 0x3f ] : '=');
+}
+
+void base64_encode(char *_in, char *_out, int length) {
+	unsigned char *in = (unsigned char *)_in,
+		     *out = (unsigned char *)_out;
+	int in_off = 0, out_off = 0;
+	while (in_off < length) {
+		_encode_block(in + in_off, out + out_off, min(3, length - in_off));
+		in_off += 3;
+		out_off += 4;
+	}
+	out[out_off] = 0;
+}
+
+static int _decode_block(unsigned char *in, unsigned char *out)
+{
+	static const char c[] = {
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+		-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+		52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
+		-1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+		15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+		-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+		41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+	};
+	if (c[in[0]] < 0 || c[in[1]] < 0 || c[in[2]] < 0 || c[in[3]] < 0)
+		return -1;
+
+	out[0] = (c[in[0]] << 2 | c[in[1]] >> 4);
+	out[1] = (c[in[1]] << 4 | c[in[2]] >> 2);
+	out[2] = (((c[in[2]] << 6) & 0xc0) | c[in[3]]);
+	if (in[2] == '=')
+		return 1;
+	if (in[3] == '=')
+		return 2;
+	return 3;
+}
+
+int base64_decode(char *_in, char *_out, int to_size)
+{
+	unsigned char *in = (unsigned char *)_in,
+		     *out = (unsigned char *)_out;
+	int in_off = 0, out_off = 0;
+	while (in_off < strlen(_in) && out_off < to_size) {
+		out_off += _decode_block(in + in_off, out + out_off);
+		in_off += 4;
+	}
+	return out_off;
+}
+
diff -rN -u -p old-binbackup/lib/misc/base64.h new-binbackup/lib/misc/base64.h
--- old-binbackup/lib/misc/base64.h	1970-01-01 01:00:00.000000000 +0100
+++ new-binbackup/lib/misc/base64.h	2010-11-13 16:45:27.000000000 +0100
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LVM_BASE64_H
+#define _LVM_BASE64_H
+
+
+void base64_encode(char *in, char *out, int length);
+int base64_decode(char *_in, char *_out, int to_size);
+
+#endif
diff -rN -u -p old-binbackup/man/lvm.conf.5.in new-binbackup/man/lvm.conf.5.in
--- old-binbackup/man/lvm.conf.5.in	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/man/lvm.conf.5.in	2010-11-13 16:45:27.000000000 +0100
@@ -232,6 +232,11 @@ Default is 1 (automatic backups enabled)
 Disabling this might make metadata recovery difficult or impossible 
 if something goes wrong.
 .IP
+\fBbinary\fP \(em Whether or not tools make an automatic binary backup
+of areas overwritten with metadata (only affects pvcreate as of
+now). Backups are written into \fBarchive_dir\fP.  Default is 1
+(automatic backups enabled).  Set to 0 to disable.
+.IP
 \fBretain_min\fP \(em Minimum number of archives to keep.
 Defaults to 10.
 .IP
diff -rN -u -p old-binbackup/test/test-utils.sh new-binbackup/test/test-utils.sh
--- old-binbackup/test/test-utils.sh	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/test/test-utils.sh	2010-11-13 16:45:27.000000000 +0100
@@ -10,8 +10,8 @@
 
 aux() {
         # use just "$@" for verbose operation
-	"$@" > /dev/null 2> /dev/null
-	#"$@"
+	#"$@" > /dev/null 2> /dev/null
+	"$@"
 }
 
 STACKTRACE() {
@@ -361,6 +361,7 @@ prepare_lvmconf() {
 		filter='[ "a/dev\/mirror/", "a/dev\/mapper\/.*pv[0-9_]*$/", "r/.*/" ]'
         locktype=
 	if test -n "$LVM_TEST_LOCKING"; then locktype="locking_type = $LVM_TEST_LOCKING"; fi
+	mkdir -p $TESTDIR/etc/archive/
 	cat > $TESTDIR/etc/lvm.conf.new <<-EOF
   $LVM_TEST_CONFIG
   devices {
@@ -383,6 +384,7 @@ prepare_lvmconf() {
   backup {
     backup = 0
     archive = 0
+    binary = 1
   }
   global {
     abort_on_internal_errors = 1
@@ -421,6 +423,8 @@ prepare() {
 	lv2=LV2
 	lv3=LV3
 	lv4=LV4
+
+	ulimit -c unlimited
 }
 
 LANG=C
diff -rN -u -p old-binbackup/test/t-pvcreate-undo.sh new-binbackup/test/t-pvcreate-undo.sh
--- old-binbackup/test/t-pvcreate-undo.sh	1970-01-01 01:00:00.000000000 +0100
+++ new-binbackup/test/t-pvcreate-undo.sh	2010-11-13 16:45:27.000000000 +0100
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Copyright (C) 2007 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+test_description="ensure that pvcreate's undo code works"
+privileges_required_=1
+
+. ./test-utils.sh
+aux prepare_devs 4
+
+test "`echo -n | md5sum | cut -d' ' -f1`" = d41d8cd98f00b204e9800998ecf8427e || exit 200
+
+md5sum="`md5sum $dev1 | cut -d\" \" -f1`"
+pvcreate $dev1
+test "`md5sum $dev1 | cut -d\" \" -f1`" != "$md5sum"
+pvcreate --undo $dev1
+test "`md5sum $dev1 | cut -d\" \" -f1`" = "$md5sum"
diff -rN -u -p old-binbackup/tools/args.h new-binbackup/tools/args.h
--- old-binbackup/tools/args.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/tools/args.h	2010-11-13 16:45:27.000000000 +0100
@@ -71,6 +71,7 @@ arg(noudevsync_ARG, '\0', "noudevsync",
 arg(poll_ARG, '\0', "poll", yes_no_arg, 0)
 arg(stripes_long_ARG, '\0', "stripes", int_arg, 0)
 arg(sysinit_ARG, '\0', "sysinit", NULL, 0)
+arg(undo_ARG, '\0', "undo", NULL, 0)
 
 /* Allow some variations */
 arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0)
diff -rN -u -p old-binbackup/tools/commands.h new-binbackup/tools/commands.h
--- old-binbackup/tools/commands.h	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/tools/commands.h	2010-11-13 16:45:27.000000000 +0100
@@ -515,6 +515,7 @@ xx(pvcreate,
    "\t[-v|--verbose] " "\n"
    "\t[-y|--yes]" "\n"
    "\t[-Z|--zero {y|n}]\n"
+   "\t[--undo]\n"
    "\t[--version] " "\n"
    "\tPhysicalVolume [PhysicalVolume...]\n",
 
@@ -522,7 +523,7 @@ xx(pvcreate,
    labelsector_ARG, metadatatype_ARG, metadatacopies_ARG,
    metadatasize_ARG, metadataignore_ARG, norestorefile_ARG,
    physicalvolumesize_ARG, pvmetadatacopies_ARG,
-   restorefile_ARG, uuidstr_ARG, yes_ARG, zero_ARG)
+   restorefile_ARG, uuidstr_ARG, yes_ARG, zero_ARG, undo_ARG)
 
 xx(pvdata,
    "Display the on-disk metadata for physical volume(s)",
diff -rN -u -p old-binbackup/tools/pvcreate.c new-binbackup/tools/pvcreate.c
--- old-binbackup/tools/pvcreate.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/tools/pvcreate.c	2010-11-13 16:45:27.000000000 +0100
@@ -15,6 +15,125 @@
 
 #include "tools.h"
 #include "metadata-exported.h"
+#include "archiver.h"
+#include "uuid.h"
+
+const char _really_undo[] =
+    "Really WIPE LABELS and restore backup of physical volume \"%s\" of volume group \"%s\" [y/n]? ";
+
+static int pvcreate_undo_check(struct cmd_context *cmd, const char *name,
+			       char **back)
+{
+	struct physical_volume *pv;
+
+	/* Is there a pv here already? */
+	/* If not, this is an error unless you used -f. */
+	if (!(pv = pv_read(cmd, name, NULL, NULL, 1, 0))) {
+		if (arg_count(cmd, force_ARG))
+			return 1;
+		log_error("Physical Volume %s not found", name);
+		return 0;
+	}
+
+	*back = dm_malloc(strlen(cmd->archive_params->dir)
+			  + 32 + 6 + 7);
+	strcpy(*back, cmd->archive_params->dir);
+	strcat(*back, "/pv-");
+	struct id id = pv_id(pv);
+	strncat(*back, (char *)id.uuid, 32);
+        // id_write_format(&id, *back + strlen(*back), 32 + 6 + 1);
+	strcat(*back, ".bin");
+	if (access(*back, R_OK) < 0) {
+		log_error("Can't access archive file %s", *back);
+		return 1;
+	}
+
+	if (is_orphan(pv))
+		return 1;
+
+	/* Allow partial & exported VGs to be destroyed. */
+	/* we must have -ff to overwrite a non orphan */
+	if (arg_count(cmd, force_ARG) < 2) {
+		log_error("Can't restore physical volume \"%s\" of "
+			  "volume group \"%s\" without -ff", name, pv->vg_name);
+		return 0;
+	}
+
+	/* prompt */
+	if (!arg_count(cmd, yes_ARG) &&
+	    yes_no_prompt(_really_undo, name, pv->vg_name) == 'n') {
+		log_print("%s: physical volume label not removed", name);
+		return 0;
+	}
+
+	if (arg_count(cmd, force_ARG)) {
+		log_print("Restoring backup of physical volume "
+			  "%s%s%s%s", name,
+			  pv->vg_name[0] ? " of volume group \"" : "",
+			  pv->vg_name[0] ? pv->vg_name : "",
+			  pv->vg_name[0] ? "\"" : "");
+	}
+
+	return 1;
+}
+
+static int pvcreate_undo(struct cmd_context *cmd, const char *name)
+{
+	struct device *dev;
+	char *back = NULL;
+	int err = 0;
+
+	/* if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
+		log_error("Can't get lock for orphan PVs");
+		return ECMD_FAILED;
+		} */
+
+	if (!pvcreate_undo_check(cmd, name, &back))
+		goto error;
+
+	if (!(dev = dev_cache_get(name, cmd->filter))) {
+		log_error("%s: Couldn't find device.  Check your filters?",
+			  name);
+		goto error;
+	}
+
+	if (!dev_test_excl(dev)) {
+		log_error("Can't open %s exclusively - not removing. "
+			  "Mounted filesystem?", dev_name(dev));
+		goto error;
+	}
+
+	if (!dev_open(dev)) {
+		log_error("Failed opening %s", dev_name(dev));
+		goto error;
+	}
+
+	/* Wipe existing label(s) */
+	if (!dev_restore(dev, back, 0)) {
+		log_error("Failed to restore backup of %s", name);
+		err = 1;
+	}
+
+	if (!dev_close(dev)) {
+		log_error("Failed to close device %s", name);
+		goto error;
+	}
+
+	if (err)
+		goto error;
+
+	log_print("Device \"%s\" successfully restored from backup",
+		  name);
+
+	dm_free(back);
+	// unlock_vg(cmd, VG_ORPHANS);
+	return ECMD_PROCESSED;
+
+      error:
+	dm_free(back);
+	// unlock_vg(cmd, VG_ORPHANS);
+	return ECMD_FAILED;
+}
 
 /*
  * Intial sanity checking of recovery-related command-line arguments.
@@ -92,6 +211,7 @@ int pvcreate(struct cmd_context *cmd, in
 {
 	int i;
 	int ret = ECMD_PROCESSED;
+	int undo = 0;
 	struct pvcreate_params pp;
 
 	pvcreate_params_set_defaults(&pp);
@@ -103,6 +223,9 @@ int pvcreate(struct cmd_context *cmd, in
 		return EINVALID_CMD_LINE;
 	}
 
+	if (arg_count(cmd, undo_ARG))
+		undo = 1;
+
 	for (i = 0; i < argc; i++) {
 		if (!lock_vol(cmd, VG_ORPHANS, LCK_VG_WRITE)) {
 			log_error("Can't get lock for orphan PVs");
@@ -111,7 +234,12 @@ int pvcreate(struct cmd_context *cmd, in
 
 		unescape_colons_and_at_signs(argv[i], NULL, NULL);
 
-		if (!pvcreate_single(cmd, argv[i], &pp)) {
+		if (undo) {
+			if (!pvcreate_undo(cmd, argv[i])) {
+				stack;
+				ret = ECMD_FAILED;
+			}
+		} else if (!pvcreate_single(cmd, argv[i], &pp)) {
 			stack;
 			ret = ECMD_FAILED;
 		}
@@ -120,6 +248,5 @@ int pvcreate(struct cmd_context *cmd, in
 		if (sigint_caught())
 			return ret;
 	}
-
 	return ret;
 }
diff -rN -u -p old-binbackup/tools/toollib.c new-binbackup/tools/toollib.c
--- old-binbackup/tools/toollib.c	2010-11-13 16:45:27.000000000 +0100
+++ new-binbackup/tools/toollib.c	2010-11-13 16:45:27.000000000 +0100
@@ -15,10 +15,6 @@
 
 #include "tools.h"
 #include "lv_alloc.h"
-#include "xlate.h"
-
-#include <sys/stat.h>
-#include <sys/wait.h>
 
 const char *command_name(struct cmd_context *cmd)
 {

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]