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

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



Hi,

I am attaching another rebase of the binary backup code. It passes the
testsuite (the attached patch enables binary backups for all of the
testsuite, although that might be gratuitous) and should apply cleanly
on top of current CVS. It also comes with a testcase for pvcreate --undo
itself, test/t-pvcreate-undo.sh.

The functionality is limited to pvcreate, but lvcreate --zero shouldn't
be too hard to add. I would however prefer to have this merged first
(since it's been around for a long time) before adding any more features
to the bundle.

Yours,
   Petr.

diff -rN -u -p old-binbackup/doc/example.conf.in new-binbackup/doc/example.conf.in
--- old-binbackup/doc/example.conf.in	2010-10-04 23:56:44.000000000 +0200
+++ new-binbackup/doc/example.conf.in	2010-10-04 23:56:44.000000000 +0200
@@ -211,6 +211,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/include/.symlinks.in	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/commands/toolcontext.c	2010-10-04 23:56:44.000000000 +0200
@@ -1020,11 +1020,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");
@@ -1044,21 +1060,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 */
@@ -1066,21 +1076,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;
 }
 
@@ -1129,6 +1140,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);
@@ -1308,6 +1321,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/commands/toolcontext.h	2010-10-04 23:56:44.000000000 +0200
@@ -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;
@@ -96,6 +97,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;
 };
 
 /*
@@ -112,4 +116,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/config/config.c	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/config/config.h	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/device/device.h	2010-10-04 23:56:44.000000000 +0200
@@ -82,9 +82,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/device/dev-io.c	2010-10-04 23:56:44.000000000 +0200
@@ -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
@@ -685,6 +696,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)));
 
@@ -702,7 +719,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/format_text/archiver.c	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/format_text/archiver.h	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/format_text/format-text.c	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/label/label.c	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/label/label.h	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/Makefile.in	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/metadata/lv_manip.c	2010-10-04 23:56:44.000000000 +0200
@@ -2942,6 +2942,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/metadata/metadata.c	2010-10-04 23:56:44.000000000 +0200
@@ -1461,6 +1461,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,
@@ -1475,7 +1478,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;
 	}
@@ -1487,7 +1490,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;
@@ -1503,6 +1507,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/metadata/metadata-exported.h	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/lib/metadata/metadata.h	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/man/lvm.conf.5.in	2010-10-04 23:56:44.000000000 +0200
@@ -226,6 +226,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/test/test-utils.sh	2010-10-04 23:56:44.000000000 +0200
@@ -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 {
@@ -382,6 +383,7 @@ prepare_lvmconf() {
   backup {
     backup = 0
     archive = 0
+    binary = 1
   }
   global {
     abort_on_internal_errors = 1
@@ -418,6 +420,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-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/tools/args.h	2010-10-04 23:56:44.000000000 +0200
@@ -70,6 +70,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/tools/commands.h	2010-10-04 23:56:44.000000000 +0200
@@ -514,6 +514,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",
 
@@ -521,7 +522,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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/tools/pvcreate.c	2010-10-04 23:56:44.000000000 +0200
@@ -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-10-04 23:56:44.000000000 +0200
+++ new-binbackup/tools/toollib.c	2010-10-04 23:56:44.000000000 +0200
@@ -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]