Index: LVM2.2.02.39/daemons/clvmd/lvm-functions.c
===================================================================
--- LVM2.2.02.39.orig/daemons/clvmd/lvm-functions.c
+++ LVM2.2.02.39/daemons/clvmd/lvm-functions.c
@@ -413,9 +413,6 @@ int do_lock_lv(unsigned char command, un
 		}
 	}
 
-	if (lock_flags & LCK_PARTIAL_MODE)
-		init_partial(1);
-
 	if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
 		init_mirror_in_sync(1);
 
@@ -454,9 +451,6 @@ int do_lock_lv(unsigned char command, un
 		break;
 	}
 
-	if (lock_flags & LCK_PARTIAL_MODE)
-		init_partial(0);
-
 	if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
 		init_mirror_in_sync(0);
 
Index: LVM2.2.02.39/doc/example.conf
===================================================================
--- LVM2.2.02.39.orig/doc/example.conf
+++ LVM2.2.02.39/doc/example.conf
@@ -266,11 +266,13 @@ global {
 }
 
 activation {
-    # Device used in place of missing stripes if activating incomplete volume.
-    # For now, you need to set this up yourself first (e.g. with 'dmsetup')
-    # For example, you could make it return I/O errors using the 'error' 
-    # target or make it return zeros.
-    missing_stripe_filler = "/dev/ioerror"
+    # How to fill in missing stripes if activating an incomplete volume.
+    # Using "error" will make inaccessible parts of the device return
+    # I/O errors on access.  You can instead use a device path, in which 
+    # case, that device will be used to in place of missing stripes.
+    # But note that using anything other than "error" with mirrored 
+    # or snapshotted volumes is likely to result in data corruption.
+    missing_stripe_filler = "error"
 
     # How much stack (in KB) to reserve for use while devices suspended
     reserved_stack = 256
Index: LVM2.2.02.39/lib/activate/activate.c
===================================================================
--- LVM2.2.02.39.orig/lib/activate/activate.c
+++ LVM2.2.02.39/lib/activate/activate.c
@@ -1057,6 +1057,12 @@ static int _lv_activate(struct cmd_conte
 		return 0;
 	}
 
+	if ((!lv->vg->cmd->partial_activate) && (lv->status & PARTIAL_LV)) {
+		log_error("Refusing activation of partial LV %s. Use --partial to override.",
+			  lv->name);
+		return_0;
+	}
+
 	if (test_mode()) {
 		_skip("Activating '%s'.", lv->name);
 		return 1;
Index: LVM2.2.02.39/lib/activate/dev_manager.c
===================================================================
--- LVM2.2.02.39.orig/lib/activate/dev_manager.c
+++ LVM2.2.02.39/lib/activate/dev_manager.c
@@ -47,7 +47,6 @@ struct dev_manager {
 
 	struct cmd_context *cmd;
 
-	const char *stripe_filler;
 	void *target_state;
 	uint32_t pvmove_mirror_count;
 	int flush_required;
@@ -60,8 +59,6 @@ struct lv_layer {
 	const char *old_name;
 };
 
-static const char *stripe_filler = NULL;
-
 static char *_build_dlid(struct dm_pool *mem, const char *lvid, const char *layer)
 {
 	char *dlid;
@@ -445,13 +442,6 @@ struct dev_manager *dev_manager_create(s
 	dm->cmd = cmd;
 	dm->mem = mem;
 
-	if (!stripe_filler) {
-		stripe_filler = find_config_tree_str(cmd,
-						"activation/missing_stripe_filler",
-						DEFAULT_STRIPE_FILLER);
-	}
-	dm->stripe_filler = stripe_filler;
-
 	if (!(dm->vg_name = dm_pool_strdup(dm->mem, vg_name)))
 		goto_bad;
 
@@ -701,6 +691,68 @@ bad:
 	return NULL;
 }
 
+static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
+			       struct lv_segment *seg, int s)
+{
+	char *id, *name;
+	char errid[32];
+	struct dm_tree_node *node;
+	struct lv_segment *seg_i;
+	int segno = -1, i = 0;;
+	uint64_t size = seg->len * seg->lv->vg->extent_size;
+
+	list_iterate_items(seg_i, &seg->lv->segments) {
+		if (seg == seg_i)
+			segno = i;
+		++i;
+	}
+
+	if (segno < 0) {
+		log_error("_add_error_device called with bad segment");
+		return_NULL;
+	}
+
+	sprintf(errid, "missing_%d_%d", segno, s);
+
+	if (!(id = build_dlid(dm, seg->lv->lvid.s, errid))) 
+		return_NULL;
+
+	if (!(name = build_dm_name(dm->mem, seg->lv->vg->name,
+				   seg->lv->name, errid)))
+		return_NULL;
+	if (!(node = dm_tree_add_new_dev(dtree, name, id, 0, 0, 0, 0, 0)))
+		return_NULL;
+	if (!dm_tree_node_add_error_target(node, size))
+		return_NULL;
+
+	return id;
+}
+
+static int _add_error_area(struct dev_manager *dm, struct dm_tree_node *node,
+			   struct lv_segment *seg, int s)
+{
+	char *dlid;
+	uint64_t extent_size = seg->lv->vg->extent_size;
+
+	if (!strcmp(dm->cmd->stripe_filler, "error")) {
+		/*
+		 * FIXME, the tree pointer is first field of dm_tree_node, but
+		 * we don't have the struct definition available.
+		 */
+		struct dm_tree **tree = (struct dm_tree **) node;
+		dlid = _add_error_device(dm, *tree, seg, s);
+		if (!dlid)
+			return_0;
+		dm_tree_node_add_target_area(node, NULL, dlid,
+					     extent_size * seg_le(seg, s));
+	} else {
+		dm_tree_node_add_target_area(node,
+					     dm->cmd->stripe_filler,
+					     NULL, UINT64_C(0));
+	}
+	return 1;
+}
+
 int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
 		   struct dm_tree_node *node, uint32_t start_area,
 		   uint32_t areas)
@@ -714,11 +766,10 @@ int add_areas_line(struct dev_manager *d
 		     (!seg_pvseg(seg, s) ||
 		      !seg_pv(seg, s) ||
 		      !seg_dev(seg, s))) ||
-		    (seg_type(seg, s) == AREA_LV && !seg_lv(seg, s)))
-			dm_tree_node_add_target_area(node,
-							dm->stripe_filler,
-							NULL, UINT64_C(0));
-		else if (seg_type(seg, s) == AREA_PV)
+		    (seg_type(seg, s) == AREA_LV && !seg_lv(seg, s))) {
+			if (!_add_error_area(dm, node, seg, s))
+				return_0;
+		} else if (seg_type(seg, s) == AREA_PV)
 			dm_tree_node_add_target_area(node,
 							dev_name(seg_dev(seg, s)),
 							NULL,
Index: LVM2.2.02.39/lib/commands/toolcontext.c
===================================================================
--- LVM2.2.02.39.orig/lib/commands/toolcontext.c
+++ LVM2.2.02.39/lib/commands/toolcontext.c
@@ -154,6 +154,7 @@ static int _process_config(struct cmd_co
 {
 	mode_t old_umask;
 	const char *read_ahead;
+	struct stat st;
 
 	/* umask */
 	cmd->default_settings.umask = find_config_tree_int(cmd,
@@ -218,6 +219,24 @@ static int _process_config(struct cmd_co
 		return 0;
 	}
 
+	cmd->stripe_filler = find_config_tree_str(cmd,
+						  "activation/missing_stripe_filler",
+						  DEFAULT_STRIPE_FILLER);
+	if (strcmp(cmd->stripe_filler, "error")) {
+		if (stat(cmd->stripe_filler, &st)) {
+			log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+				 "is invalid,", cmd->stripe_filler);
+			log_warn("         stat failed: %s", strerror(errno));
+			log_warn("Falling back to \"error\" missing_stripe_filler.");
+			cmd->stripe_filler = "error";
+		} else if (!S_ISBLK(st.st_mode)) {
+			log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+				 "is not a block device.", cmd->stripe_filler);
+			log_warn("Falling back to \"error\" missing_stripe_filler.");
+			cmd->stripe_filler = "error";
+		}
+	}
+
 	return 1;
 }
 
@@ -936,6 +955,7 @@ struct cmd_context *create_toolcontext(s
 	cmd->args = the_args;
 	cmd->is_static = is_static;
 	cmd->is_long_lived = is_long_lived;
+	cmd->handles_missing_pvs = 0;
 	cmd->hosttags = 0;
 	list_init(&cmd->formats);
 	list_init(&cmd->segtypes);
Index: LVM2.2.02.39/lib/commands/toolcontext.h
===================================================================
--- LVM2.2.02.39.orig/lib/commands/toolcontext.h
+++ LVM2.2.02.39/lib/commands/toolcontext.h
@@ -66,8 +66,10 @@ struct cmd_context {
 	struct command *command;
 	struct arg *args;
 	char **argv;
-	unsigned is_static;	/* Static binary? */
-	unsigned is_long_lived;	/* Optimises persistent_filter handling */
+	unsigned is_static:1;	/* Static binary? */
+	unsigned is_long_lived:1;	/* Optimises persistent_filter handling */
+	unsigned handles_missing_pvs:1;
+	unsigned partial_activate:1;
 
 	struct dev_filter *filter;
 	int dump_filter;	/* Dump filter when exiting? */
@@ -81,6 +83,7 @@ struct cmd_context {
 
 	struct archive_params *archive_params;
 	struct backup_params *backup_params;
+	const char *stripe_filler;
 
 	/* List of defined tags */
 	struct list tags;
Index: LVM2.2.02.39/lib/config/defaults.h
===================================================================
--- LVM2.2.02.39.orig/lib/config/defaults.h
+++ LVM2.2.02.39/lib/config/defaults.h
@@ -91,7 +91,7 @@
 #  define DEFAULT_ACTIVATION 0
 #endif
 
-#define DEFAULT_STRIPE_FILLER "/dev/ioerror"
+#define DEFAULT_STRIPE_FILLER "error"
 #define DEFAULT_MIRROR_REGION_SIZE 512	/* KB */
 #define DEFAULT_INTERVAL 15
 
Index: LVM2.2.02.39/lib/display/display.c
===================================================================
--- LVM2.2.02.39.orig/lib/display/display.c
+++ LVM2.2.02.39/lib/display/display.c
@@ -578,10 +578,7 @@ void vgdisplay_full(const struct volume_
 	struct lv_list *lvl;
 	char uuid[64] __attribute((aligned(8)));
 
-	if (vg->status & PARTIAL_VG)
-		active_pvs = list_size(&vg->pvs);
-	else
-		active_pvs = vg->pv_count;
+	active_pvs = vg->pv_count - vg_missing_pv_count(vg);
 
 	log_print("--- Volume group ---");
 	log_print("VG Name               %s", vg->name);
@@ -664,10 +661,7 @@ void vgdisplay_colons(const struct volum
 	const char *access;
 	char uuid[64] __attribute((aligned(8)));
 
-	if (vg->status & PARTIAL_VG)
-		active_pvs = list_size(&vg->pvs);
-	else
-		active_pvs = vg->pv_count;
+	active_pvs = vg->pv_count - vg_missing_pv_count(vg);
 
 	list_iterate_items(lvl, &vg->lvs)
 		if (lv_is_visible(lvl->lv) && !(lvl->lv->status & SNAPSHOT))
Index: LVM2.2.02.39/lib/format1/disk-rep.h
===================================================================
--- LVM2.2.02.39.orig/lib/format1/disk-rep.h
+++ LVM2.2.02.39/lib/format1/disk-rep.h
@@ -212,7 +212,7 @@ int export_pv(struct cmd_context *cmd, s
 	      struct pv_disk *pvd, struct physical_volume *pv);
 
 int import_vg(struct dm_pool *mem,
-	      struct volume_group *vg, struct disk_list *dl, int partial);
+	      struct volume_group *vg, struct disk_list *dl);
 int export_vg(struct vg_disk *vgd, struct volume_group *vg);
 
 int import_lv(struct dm_pool *mem, struct logical_volume *lv, struct lv_disk *lvd);
Index: LVM2.2.02.39/lib/format1/format1.c
===================================================================
--- LVM2.2.02.39.orig/lib/format1/format1.c
+++ LVM2.2.02.39/lib/format1/format1.c
@@ -23,7 +23,7 @@
 #include "segtype.h"
 
 /* VG consistency checks */
-static int _check_vgs(struct list *pvs, int *partial)
+static int _check_vgs(struct list *pvs)
 {
 	struct list *pvh, *t;
 	struct disk_list *dl = NULL;
@@ -33,8 +33,6 @@ static int _check_vgs(struct list *pvs,
 	uint32_t exported = 0;
 	int first_time = 1;
 
-	*partial = 0;
-
 	/*
 	 * If there are exported and unexported PVs, ignore exported ones.
 	 * This means an active VG won't be affected if disks are inserted
@@ -98,10 +96,6 @@ static int _check_vgs(struct list *pvs,
 				  dl->vgd.pe_total, dl->vgd.pe_allocated,
 				  dl->vgd.pvg_total);
 			list_del(pvh);
-			if (partial_mode()) {
-				*partial = 1;
-				continue;
-			}
 			return 0;
 		}
 		pv_count++;
@@ -111,21 +105,17 @@ static int _check_vgs(struct list *pvs,
 	if (pv_count != first->vgd.pv_cur) {
 		log_error("%d PV(s) found for VG %s: expected %d",
 			  pv_count, first->pvd.vg_name, first->vgd.pv_cur);
-		if (!partial_mode())
-			return 0;
-		*partial = 1;
 	}
 
 	return 1;
 }
 
 static struct volume_group *_build_vg(struct format_instance *fid,
-				      struct list *pvs)
+				      struct list *pvs,
+					  struct dm_pool *mem)
 {
-	struct dm_pool *mem = fid->fmt->cmd->mem;
 	struct volume_group *vg = dm_pool_alloc(mem, sizeof(*vg));
 	struct disk_list *dl;
-	int partial;
 
 	if (!vg)
 		goto_bad;
@@ -136,18 +126,19 @@ static struct volume_group *_build_vg(st
 	memset(vg, 0, sizeof(*vg));
 
 	vg->cmd = fid->fmt->cmd;
+	vg->vgmem = mem;
 	vg->fid = fid;
 	vg->seqno = 0;
 	list_init(&vg->pvs);
 	list_init(&vg->lvs);
 	list_init(&vg->tags);
 
-	if (!_check_vgs(pvs, &partial))
+	if (!_check_vgs(pvs))
 		goto_bad;
 
 	dl = list_item(pvs->n, struct disk_list);
 
-	if (!import_vg(mem, vg, dl, partial))
+	if (!import_vg(mem, vg, dl))
 		goto_bad;
 
 	if (!import_pvs(fid->fmt, mem, vg, pvs, &vg->pvs, &vg->pv_count))
@@ -173,7 +164,7 @@ static struct volume_group *_format1_vg_
 				     const char *vg_name,
 				     struct metadata_area *mda __attribute((unused)))
 {
-	struct dm_pool *mem = dm_pool_create("lvm1 vg_read", 1024 * 10);
+	struct dm_pool *mem = dm_pool_create("lvm1 vg_read", VG_MEMPOOL_CHUNK);
 	struct list pvs;
 	struct volume_group *vg = NULL;
 	list_init(&pvs);
@@ -188,12 +179,13 @@ static struct volume_group *_format1_vg_
 	    (fid->fmt, vg_name, fid->fmt->cmd->filter, mem, &pvs))
 		goto_bad;
 
-	if (!(vg = _build_vg(fid, &pvs)))
+	if (!(vg = _build_vg(fid, &pvs, mem)))
 		goto_bad;
 
-      bad:
-	dm_pool_destroy(mem);
 	return vg;
+bad:
+	dm_pool_destroy(mem);
+	return NULL;
 }
 
 static struct disk_list *_flatten_pv(struct format_instance *fid,
@@ -250,7 +242,7 @@ static int _flatten_vg(struct format_ins
 static int _format1_vg_write(struct format_instance *fid, struct volume_group *vg,
 		     struct metadata_area *mda __attribute((unused)))
 {
-	struct dm_pool *mem = dm_pool_create("lvm1 vg_write", 1024 * 10);
+	struct dm_pool *mem = dm_pool_create("lvm1 vg_write", VG_MEMPOOL_CHUNK);
 	struct list pvds;
 	int r = 0;
 
Index: LVM2.2.02.39/lib/format1/import-export.c
===================================================================
--- LVM2.2.02.39.orig/lib/format1/import-export.c
+++ LVM2.2.02.39/lib/format1/import-export.c
@@ -213,7 +213,7 @@ int export_pv(struct cmd_context *cmd, s
 }
 
 int import_vg(struct dm_pool *mem,
-	      struct volume_group *vg, struct disk_list *dl, int partial)
+	      struct volume_group *vg, struct disk_list *dl)
 {
 	struct vg_disk *vgd = &dl->vgd;
 	memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
@@ -235,10 +235,10 @@ int import_vg(struct dm_pool *mem,
 	if (vgd->vg_status & VG_EXTENDABLE)
 		vg->status |= RESIZEABLE_VG;
 
-	if (partial || (vgd->vg_access & VG_READ))
+	if (vgd->vg_access & VG_READ)
 		vg->status |= LVM_READ;
 
-	if (!partial && (vgd->vg_access & VG_WRITE))
+	if (vgd->vg_access & VG_WRITE)
 		vg->status |= LVM_WRITE;
 
 	if (vgd->vg_access & VG_CLUSTERED)
@@ -254,9 +254,6 @@ int import_vg(struct dm_pool *mem,
 	vg->max_pv = vgd->pv_max;
 	vg->alloc = ALLOC_NORMAL;
 
-	if (partial)
-		vg->status |= PARTIAL_VG;
-
 	return 1;
 }
 
Index: LVM2.2.02.39/lib/format_pool/format_pool.c
===================================================================
--- LVM2.2.02.39.orig/lib/format_pool/format_pool.c
+++ LVM2.2.02.39/lib/format_pool/format_pool.c
@@ -113,6 +113,7 @@ static struct volume_group *_build_vg_fr
 	}
 
 	vg->cmd = fid->fmt->cmd;
+	vg->vgmem = mem;
 	vg->fid = fid;
 	vg->name = NULL;
 	vg->status = 0;
@@ -160,7 +161,7 @@ static struct volume_group *_pool_vg_rea
 				     const char *vg_name,
 				     struct metadata_area *mda __attribute((unused)))
 {
-	struct dm_pool *mem = dm_pool_create("pool vg_read", 1024);
+	struct dm_pool *mem = dm_pool_create("pool vg_read", VG_MEMPOOL_CHUNK);
 	struct list pds;
 	struct volume_group *vg = NULL;
 
@@ -182,9 +183,10 @@ static struct volume_group *_pool_vg_rea
 	if (!(vg = _build_vg_from_pds(fid, mem, &pds)))
 		goto_out;
 
-      out:
-	dm_pool_destroy(mem);
 	return vg;
+out:
+	dm_pool_destroy(mem);
+	return NULL;
 }
 
 static int _pool_pv_setup(const struct format_type *fmt __attribute((unused)),
Index: LVM2.2.02.39/lib/format_text/archiver.c
===================================================================
--- LVM2.2.02.39.orig/lib/format_text/archiver.c
+++ LVM2.2.02.39/lib/format_text/archiver.c
@@ -134,10 +134,8 @@ int archive_display(struct cmd_context *
 {
 	int r1, r2;
 
-	init_partial(1);
 	r1 = archive_list(cmd, cmd->archive_params->dir, vg_name);
 	r2 = backup_list(cmd, cmd->backup_params->dir, vg_name);
-	init_partial(0);
 
 	return r1 && r2;
 }
@@ -146,9 +144,7 @@ int archive_display_file(struct cmd_cont
 {
 	int r;
 
-	init_partial(1);
 	r = archive_list_file(cmd, file);
-	init_partial(0);
 
 	return r;
 }
@@ -393,7 +389,7 @@ void check_current_backup(struct volume_
 	char path[PATH_MAX];
 	struct volume_group *vg_backup;
 
-	if ((vg->status & PARTIAL_VG) || (vg->status & EXPORTED_VG))
+	if (vg->status & EXPORTED_VG)
 		return;
 
 	if (dm_snprintf(path, sizeof(path), "%s/%s",
Index: LVM2.2.02.39/lib/format_text/flags.c
===================================================================
--- LVM2.2.02.39.orig/lib/format_text/flags.c
+++ LVM2.2.02.39/lib/format_text/flags.c
@@ -30,12 +30,12 @@ struct flag {
 static struct flag _vg_flags[] = {
 	{EXPORTED_VG, "EXPORTED"},
 	{RESIZEABLE_VG, "RESIZEABLE"},
-	{PARTIAL_VG, "PARTIAL"},
 	{PVMOVE, "PVMOVE"},
 	{LVM_READ, "READ"},
 	{LVM_WRITE, "WRITE"},
 	{CLUSTERED, "CLUSTERED"},
 	{SHARED, "SHARED"},
+	{PARTIAL_VG, NULL},
 	{PRECOMMITTED, NULL},
 	{0, NULL}
 };
@@ -43,6 +43,7 @@ static struct flag _vg_flags[] = {
 static struct flag _pv_flags[] = {
 	{ALLOCATABLE_PV, "ALLOCATABLE"},
 	{EXPORTED_VG, "EXPORTED"},
+	{MISSING_PV, "MISSING"},
 	{0, NULL}
 };
 
@@ -61,6 +62,8 @@ static struct flag _lv_flags[] = {
 	{SNAPSHOT, NULL},
 	{ACTIVATE_EXCL, NULL},
 	{CONVERTING, NULL},
+	{PARTIAL_LV, NULL},
+	{POSTORDER_FLAG, NULL},
 	{0, NULL}
 };
 
@@ -151,7 +154,16 @@ int read_flags(uint32_t *status, int typ
 				break;
 			}
 
-		if (!flags[f].description) {
+		if (type == VG_FLAGS && !strcmp(cv->v.str, "PARTIAL")) {
+			/*
+			 * Exception: We no longer write this flag out, but it
+			 * might be encountered in old backup files, so restore
+			 * it in that case. It is never part of live metadata
+			 * though, so only vgcfgrestore needs to be concerned
+			 * by this case.
+			 */
+			s |= PARTIAL_VG;
+		} else if (!flags[f].description) {
 			log_err("Unknown status flag '%s'.", cv->v.str);
 			return 0;
 		}
Index: LVM2.2.02.39/lib/format_text/import_vsn1.c
===================================================================
--- LVM2.2.02.39.orig/lib/format_text/import_vsn1.c
+++ LVM2.2.02.39/lib/format_text/import_vsn1.c
@@ -169,11 +169,6 @@ static int _read_pv(struct format_instan
 		else
 			log_error("Couldn't find device with uuid '%s'.",
 				  buffer);
-
-		if (partial_mode())
-			vg->status |= PARTIAL_VG;
-		else
-			return 0;
 	}
 
 	if (!(pv->vg_name = dm_pool_strdup(mem, vg->name)))
@@ -191,6 +186,9 @@ static int _read_pv(struct format_instan
 		return 0;
 	}
 
+	if (!pv->dev)
+		pv->status |= MISSING_PV;
+
 	/* Late addition */
 	_read_int64(pvn, "dev_size", &pv->size);
 
@@ -647,18 +645,23 @@ static struct volume_group *_read_vg(str
 	struct config_node *vgn, *cn;
 	struct volume_group *vg;
 	struct dm_hash_table *pv_hash = NULL;
-	struct dm_pool *mem = fid->fmt->cmd->mem;
+	struct dm_pool *mem = dm_pool_create("lvm2 vg_read", VG_MEMPOOL_CHUNK);
+
+	if (!mem)
+		return_NULL;
 
 	/* skip any top-level values */
 	for (vgn = cft->root; (vgn && vgn->v); vgn = vgn->sib) ;
 
 	if (!vgn) {
 		log_error("Couldn't find volume group in file.");
-		return NULL;
+		goto bad;
 	}
 
 	if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
-		return_NULL;
+		goto_bad;
+
+	vg->vgmem = mem;
 	vg->cmd = fid->fmt->cmd;
 
 	/* FIXME Determine format type from file contents */
@@ -789,11 +792,6 @@ static struct volume_group *_read_vg(str
 
 	dm_hash_destroy(pv_hash);
 
-	if (vg->status & PARTIAL_VG) {
-		vg->status &= ~LVM_WRITE;
-		vg->status |= LVM_READ;
-	}
-
 	/*
 	 * Finished.
 	 */
@@ -803,7 +801,7 @@ static struct volume_group *_read_vg(str
 	if (pv_hash)
 		dm_hash_destroy(pv_hash);
 
-	dm_pool_free(mem, vg);
+	dm_pool_destroy(mem);
 	return NULL;
 }
 
Index: LVM2.2.02.39/lib/locking/cluster_locking.c
===================================================================
--- LVM2.2.02.39.orig/lib/locking/cluster_locking.c
+++ LVM2.2.02.39/lib/locking/cluster_locking.c
@@ -315,9 +315,6 @@ static int _lock_for_cluster(unsigned ch
 	args[0] = flags & 0x7F; /* Maskoff lock flags */
 	args[1] = flags & 0xC0; /* Bitmap flags */
 
-	if (partial_mode())
-		args[1] |= LCK_PARTIAL_MODE;
-
 	if (mirror_in_sync())
 		args[1] |= LCK_MIRROR_NOSYNC_MODE;
 
Index: LVM2.2.02.39/lib/locking/locking.h
===================================================================
--- LVM2.2.02.39.orig/lib/locking/locking.h
+++ LVM2.2.02.39/lib/locking/locking.h
@@ -83,7 +83,6 @@ int check_lvm1_vg_inactive(struct cmd_co
 /*
  * Additional lock bits for cluster communication
  */
-#define LCK_PARTIAL_MODE	0x00000001U	/* Running in partial mode */
 #define LCK_MIRROR_NOSYNC_MODE	0x00000002U	/* Mirrors don't require sync */
 #define LCK_DMEVENTD_MONITOR_MODE	0x00000004U	/* Register with dmeventd */
 
@@ -116,6 +115,9 @@ int check_lvm1_vg_inactive(struct cmd_co
 	lock_vol(cmd, (lv)->lvid.s, flags | LCK_LV_CLUSTERED(lv))
 
 #define unlock_vg(cmd, vol)	lock_vol(cmd, vol, LCK_VG_UNLOCK)
+#define unlock_release_vg(cmd, vg, vol) do { unlock_vg(cmd, vol); \
+					     vg_release(vg); \
+					} while (0)
 
 #define resume_lv(cmd, lv)	lock_lv_vol(cmd, lv, LCK_LV_RESUME)
 #define suspend_lv(cmd, lv)	lock_lv_vol(cmd, lv, LCK_LV_SUSPEND | LCK_HOLD)
Index: LVM2.2.02.39/lib/log/log.c
===================================================================
--- LVM2.2.02.39.orig/lib/log/log.c
+++ LVM2.2.02.39/lib/log/log.c
@@ -29,7 +29,6 @@ static struct str_list _log_dev_alias;
 
 static int _verbose_level = VERBOSE_BASE_LEVEL;
 static int _test = 0;
-static int _partial = 0;
 static int _md_filtering = 0;
 static int _pvmove = 0;
 static int _full_scan_done = 0;	/* Restrict to one full scan during each cmd */
@@ -154,11 +153,6 @@ void init_test(int level)
 	_test = level;
 }
 
-void init_partial(int level)
-{
-	_partial = level;
-}
-
 void init_md_filtering(int level)
 {
 	_md_filtering = level;
@@ -254,11 +248,6 @@ int test_mode()
 	return _test;
 }
 
-int partial_mode()
-{
-	return _partial;
-}
-
 int md_filtering()
 {
 	return _md_filtering;
Index: LVM2.2.02.39/lib/log/log.h
===================================================================
--- LVM2.2.02.39.orig/lib/log/log.h
+++ LVM2.2.02.39/lib/log/log.h
@@ -64,7 +64,6 @@ void fin_syslog(void);
 
 void init_verbose(int level);
 void init_test(int level);
-void init_partial(int level);
 void init_md_filtering(int level);
 void init_pvmove(int level);
 void init_full_scan_done(int level);
@@ -84,7 +83,6 @@ void init_error_message_produced(int err
 void set_cmd_name(const char *cmd_name);
 
 int test_mode(void);
-int partial_mode(void);
 int md_filtering(void);
 int pvmove_mode(void);
 int full_scan_done(void);
Index: LVM2.2.02.39/lib/metadata/metadata.c
===================================================================
--- LVM2.2.02.39.orig/lib/metadata/metadata.c
+++ LVM2.2.02.39/lib/metadata/metadata.c
@@ -314,9 +314,9 @@ int vg_remove_single(struct cmd_context
 	struct pv_list *pvl;
 	int ret = 1;
 
-	if (!vg || !consistent || (vg_status(vg) & PARTIAL_VG)) {
-		log_error("Volume group \"%s\" not found or inconsistent.",
-			  vg_name);
+	if (!vg || !consistent || vg_missing_pv_count(vg)) {
+		log_error("Volume group \"%s\" not found, is inconsistent "
+			  "or has PVs missing.", vg_name);
 		log_error("Consider vgreduce --removemissing if metadata "
 			  "is inconsistent.");
 		return 0;
@@ -466,21 +466,22 @@ struct volume_group *vg_create(struct cm
 			       int pv_count, char **pv_names)
 {
 	struct volume_group *vg;
-	struct dm_pool *mem = cmd->mem;
 	int consistent = 0;
-	int old_partial;
 
-	if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
-		return_NULL;
+	struct dm_pool *mem;
 
 	/* is this vg name already in use ? */
-	old_partial = partial_mode();
-	init_partial(1);
-	if (vg_read(cmd, vg_name, NULL, &consistent)) {
+	if ((vg = vg_read(cmd, vg_name, NULL, &consistent))) {
 		log_err("A volume group called '%s' already exists.", vg_name);
-		goto bad;
+		vg_release(vg);
+		return NULL;
 	}
-	init_partial(old_partial);
+
+	if (!(mem = dm_pool_create("lvm2 vg_create", VG_MEMPOOL_CHUNK)))
+		return_NULL;
+
+	if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
+		goto_bad;
 
 	if (!id_create(&vg->id)) {
 		log_err("Couldn't create uuid for volume group '%s'.", vg_name);
@@ -490,6 +491,7 @@ struct volume_group *vg_create(struct cm
 	/* Strip dev_dir if present */
 	vg_name = strip_dir(vg_name, cmd->dev_dir);
 
+	vg->vgmem = mem;
 	vg->cmd = cmd;
 
 	if (!(vg->name = dm_pool_strdup(mem, vg_name)))
@@ -542,7 +544,7 @@ struct volume_group *vg_create(struct cm
 	return vg;
 
       bad:
-	dm_pool_free(mem, vg);
+	dm_pool_destroy(mem);
 	return NULL;
 }
 
@@ -1171,6 +1173,157 @@ int vgs_are_compatible(struct cmd_contex
 	return 1;
 }
 
+struct _lv_postorder_baton {
+	int (*fn)(struct logical_volume *lv, void *data);
+	void *data;
+};
+
+static int _lv_postorder_visit(struct logical_volume *,
+			       int (*fn)(struct logical_volume *lv, void *data),
+			       void *data);
+
+static int _lv_postorder_level(struct logical_volume *lv, void *data)
+{
+	struct _lv_postorder_baton *baton = data;
+	int r =_lv_postorder_visit(lv, baton->fn, baton->data);
+	lv->status |= POSTORDER_FLAG;
+	return r;
+};
+
+static int _lv_each_dependency(struct logical_volume *lv,
+			       int (*fn)(struct logical_volume *lv, void *data),
+			       void *data)
+{
+	int i, s;
+	struct lv_segment *lvseg;
+
+	struct logical_volume *deps[] = {
+		lv->snapshot ? lv->snapshot->origin : 0,
+		lv->snapshot ? lv->snapshot->cow : 0 };
+	for (i = 0; i < sizeof(deps) / sizeof(*deps); ++i) {
+		if (deps[i] && !fn(deps[i], data))
+			return_0;
+	}
+
+	list_iterate_items(lvseg, &lv->segments) {
+		if (lvseg->log_lv && !fn(lvseg->log_lv, data))
+			return_0;
+		for (s = 0; s < lvseg->area_count; ++s) {
+			if (seg_type(lvseg, s) == AREA_LV && !fn(seg_lv(lvseg,s), data))
+				return_0;
+		}
+	}
+	return 1;
+}
+
+static int _lv_postorder_cleanup(struct logical_volume *lv, void *data)
+{
+	if (!(lv->status & POSTORDER_FLAG))
+		return 1;
+	lv->status &= ~POSTORDER_FLAG;
+
+	if (!_lv_each_dependency(lv, _lv_postorder_cleanup, data))
+		return_0;
+	return 1;
+}
+
+static int _lv_postorder_visit(struct logical_volume *lv,
+			       int (*fn)(struct logical_volume *lv, void *data),
+			       void *data)
+{
+	struct _lv_postorder_baton baton;
+	int r;
+
+	if (lv->status & POSTORDER_FLAG)
+		return 1;
+
+	baton.fn = fn;
+	baton.data = data;
+	r = _lv_each_dependency(lv, _lv_postorder_level, &baton);
+	if (r) {
+		r = fn(lv, data);
+		log_verbose("visited %s", lv->name);
+	}
+	return r;
+}
+
+/*
+ * This will walk the LV dependency graph in depth-first order and in the
+ * postorder, call a callback function "fn". The void *data is passed along all
+ * the calls. The callback may return zero to indicate an error and terminate
+ * the depth-first walk. The error is propagated to return value of
+ * _lv_postorder.
+ */
+static int _lv_postorder(struct logical_volume *lv,
+			       int (*fn)(struct logical_volume *lv, void *data),
+			       void *data)
+{
+	int r;
+	r = _lv_postorder_visit(lv, fn, data);
+	_lv_postorder_cleanup(lv, 0);
+	return r;
+}
+
+struct _lv_mark_if_partial_baton {
+	int partial;
+};
+
+static int _lv_mark_if_partial_collect(struct logical_volume *lv, void *data)
+{
+	struct _lv_mark_if_partial_baton *baton = data;
+	if (lv->status & PARTIAL_LV)
+		baton->partial = 1;
+
+	return 1;
+}
+
+static int _lv_mark_if_partial_single(struct logical_volume *lv, void *data)
+{
+	int s;
+	struct _lv_mark_if_partial_baton baton;
+	struct lv_segment *lvseg;
+
+	list_iterate_items(lvseg, &lv->segments) {
+		for (s = 0; s < lvseg->area_count; ++s) {
+			if (seg_type(lvseg, s) == AREA_PV) {
+				if (seg_pv(lvseg, s)->status & MISSING_PV)
+					lv->status |= PARTIAL_LV;
+			}
+		}
+	}
+
+	baton.partial = 0;
+	_lv_each_dependency(lv, _lv_mark_if_partial_collect, &baton);
+
+	if (baton.partial)
+		lv->status |= PARTIAL_LV;
+
+	return 1;
+}
+
+static int _lv_mark_if_partial(struct logical_volume *lv)
+{
+	return _lv_postorder(lv, _lv_mark_if_partial_single, NULL);
+}
+
+/*
+ * Mark LVs with missing PVs using PARTIAL_LV status flag. The flag is
+ * propagated transitively, so LVs referencing other LVs are marked
+ * partial as well, if any of their referenced LVs are marked partial.
+ */
+static int _vg_mark_partial_lvs(struct volume_group *vg)
+{
+	struct logical_volume *lv;
+	struct lv_list *lvl;
+
+	list_iterate_items(lvl, &vg->lvs) {
+		lv = lvl->lv;
+		if (!_lv_mark_if_partial(lv))
+			return_0;
+	}
+	return 1;
+}
+
 int vg_validate(struct volume_group *vg)
 {
 	struct pv_list *pvl, *pvl2;
@@ -1268,8 +1421,13 @@ int vg_write(struct volume_group *vg)
 		return_0;
 
 	if (vg->status & PARTIAL_VG) {
-		log_error("Cannot change metadata for partial volume group %s",
-			  vg->name);
+		log_error("Cannot update partial volume group %s.", vg->name);
+		return 0;
+	}
+
+	if (vg_missing_pv_count(vg) && !vg->cmd->handles_missing_pvs) {
+		log_error("Cannot update volume group %s while physical "
+			  "volumes are missing.", vg->name);
 		return 0;
 	}
 
@@ -1400,23 +1558,28 @@ static struct volume_group *_vg_read_orp
 	struct pv_list *pvl;
 	struct volume_group *vg;
 	struct physical_volume *pv;
+	struct dm_pool *mem;
 
 	lvmcache_label_scan(cmd, 0);
 
 	if (!(vginfo = vginfo_from_vgname(orphan_vgname, NULL)))
 		return_NULL;
 
-	if (!(vg = dm_pool_zalloc(cmd->mem, sizeof(*vg)))) {
+	if (!(mem = dm_pool_create("vg_read orphan", VG_MEMPOOL_CHUNK)))
+		return_NULL;
+
+	if (!(vg = dm_pool_zalloc(mem, sizeof(*vg)))) {
 		log_error("vg allocation failed");
 		return NULL;
 	}
 	list_init(&vg->pvs);
 	list_init(&vg->lvs);
 	list_init(&vg->tags);
+	vg->vgmem = mem;
 	vg->cmd = cmd;
-	if (!(vg->name = dm_pool_strdup(cmd->mem, orphan_vgname))) {
+	if (!(vg->name = dm_pool_strdup(mem, orphan_vgname))) {
 		log_error("vg name allocation failed");
-		return NULL;
+		goto bad;
 	}
 
 	/* create format instance with appropriate metadata area */
@@ -1424,17 +1587,16 @@ static struct volume_group *_vg_read_orp
 							  orphan_vgname, NULL,
 							  NULL))) {
 		log_error("Failed to create format instance");
-		dm_pool_free(cmd->mem, vg);
-		return NULL;
+		goto bad;
 	}
 
 	list_iterate_items(info, &vginfo->infos) {
 		if (!(pv = _pv_read(cmd, dev_name(info->dev), NULL, NULL, 1))) {
 			continue;
 		}
-		if (!(pvl = dm_pool_zalloc(cmd->mem, sizeof(*pvl)))) {
+		if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl)))) {
 			log_error("pv_list allocation failed");
-			return NULL;
+			goto bad;
 		}
 		pvl->pv = pv;
 		list_add(&vg->pvs, &pvl->list);
@@ -1442,6 +1604,9 @@ static struct volume_group *_vg_read_orp
 	}
 
 	return vg;
+bad:
+	dm_pool_destroy(mem);
+	return NULL;
 }
 
 static int _update_pv_list(struct list *all_pvs, struct volume_group *vg)
@@ -1468,9 +1633,20 @@ static int _update_pv_list(struct list *
 	return 1;
 }
 
+int vg_missing_pv_count(const vg_t *vg)
+{
+	int ret = 0;
+	struct pv_list *pvl;
+	list_iterate_items(pvl, &vg->pvs) {
+		if (pvl->pv->status & MISSING_PV)
+			++ ret;
+	}
+	return ret;
+}
+
 /* Caller sets consistent to 1 if it's safe for vg_read to correct
  * inconsistent metadata on disk (i.e. the VG write lock is held).
- * This guarantees only consistent metadata is returned unless PARTIAL_VG.
+ * This guarantees only consistent metadata is returned.
  * If consistent is 0, caller must check whether consistent == 1 on return
  * and take appropriate action if it isn't (e.g. abort; get write lock
  * and call vg_read again).
@@ -1509,6 +1685,11 @@ static struct volume_group *_vg_read(str
 	}
 
 	if ((correct_vg = lvmcache_get_vg(vgid, precommitted))) {
+		if (vg_missing_pv_count(correct_vg)) {
+			log_verbose("There are %d physical volumes missing.",
+				    vg_missing_pv_count(correct_vg));
+			_vg_mark_partial_lvs(correct_vg);
+		}
 		*consistent = 1;
 		return correct_vg;
 	}
@@ -1604,7 +1785,8 @@ static struct volume_group *_vg_read(str
 			}
 		}
 
-		if (list_size(&correct_vg->pvs) != list_size(pvids)) {
+		if (list_size(&correct_vg->pvs) != list_size(pvids)
+		    + vg_missing_pv_count(correct_vg)) {
 			log_debug("Cached VG %s had incorrect PV list",
 				  vgname);
 
@@ -1613,6 +1795,8 @@ static struct volume_group *_vg_read(str
 			else
 				correct_vg = NULL;
 		} else list_iterate_items(pvl, &correct_vg->pvs) {
+			if (pvl->pv->status & MISSING_PV)
+				continue;
 			if (!str_list_match_item(pvids, pvl->pv->dev->pvid)) {
 				log_debug("Cached VG %s had incorrect PV list",
 					  vgname);
@@ -1695,15 +1879,6 @@ static struct volume_group *_vg_read(str
 		if (!*consistent)
 			return correct_vg;
 
-		/* Don't touch partial volume group metadata */
-		/* Should be fixed manually with vgcfgbackup/restore etc. */
-		if ((correct_vg->status & PARTIAL_VG)) {
-			log_error("Inconsistent metadata copies found for "
-				  "partial volume group %s", vgname);
-			*consistent = 0;
-			return correct_vg;
-		}
-
 		/* Don't touch if vgids didn't match */
 		if (inconsistent_vgid) {
 			log_error("Inconsistent metadata UUIDs found for "
@@ -1742,6 +1917,12 @@ static struct volume_group *_vg_read(str
 		}
 	}
 
+	if (vg_missing_pv_count(correct_vg)) {
+		log_verbose("There are %d physical volumes missing.",
+			    vg_missing_pv_count(correct_vg));
+		_vg_mark_partial_lvs(correct_vg);
+	}
+
 	if ((correct_vg->status & PVMOVE) && !pvmove_mode()) {
 		log_error("WARNING: Interrupted pvmove detected in "
 			  "volume group %s", correct_vg->name);
@@ -1780,6 +1961,18 @@ struct volume_group *vg_read(struct cmd_
 	return vg;
 }
 
+void vg_release(struct volume_group *vg)
+{
+	if (!vg || !vg->vgmem)
+		return;
+
+	if (vg->vgmem == vg->cmd->mem)
+		log_error("Internal error: global memory pool used for VG %s",
+			  vg->name);
+
+	dm_pool_destroy(vg->vgmem);
+}
+
 /* This is only called by lv_from_lvid, which is only called from
  * activate.c so we know the appropriate VG lock is already held and
  * the vg_read is therefore safe.
@@ -1801,11 +1994,10 @@ static struct volume_group *_vg_read_by_
 		if ((vg = _vg_read(cmd, NULL, vgid,
 				   &consistent, precommitted)) &&
 		    !strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+
 			if (!consistent) {
 				log_error("Volume group %s metadata is "
 					  "inconsistent", vg->name);
-				if (!partial_mode())
-					return NULL;
 			}
 			return vg;
 		}
@@ -1833,6 +2025,7 @@ static struct volume_group *_vg_read_by_
 		if ((vg = _vg_read(cmd, vgname, vgid, &consistent,
 				   precommitted)) &&
 		    !strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+
 			if (!consistent) {
 				log_error("Volume group %s metadata is "
 					  "inconsistent", vgname);
@@ -1966,7 +2159,6 @@ static int _get_pvs(struct cmd_context *
 	struct list *vgids;
 	struct volume_group *vg;
 	int consistent = 0;
-	int old_partial;
 	int old_pvmove;
 
 	lvmcache_label_scan(cmd, 0);
@@ -1988,9 +2180,7 @@ static int _get_pvs(struct cmd_context *
 
 	/* Read every VG to ensure cache consistency */
 	/* Orphan VG is last on list */
-	old_partial = partial_mode();
 	old_pvmove = pvmove_mode();
-	init_partial(1);
 	init_pvmove(1);
 	list_iterate_items(strl, vgids) {
 		vgid = strl->str;
@@ -2015,7 +2205,6 @@ static int _get_pvs(struct cmd_context *
 				list_add(results, pvh);
 	}
 	init_pvmove(old_pvmove);
-	init_partial(old_partial);
 
 	if (pvslist)
 		*pvslist = results;
Index: LVM2.2.02.39/lib/metadata/metadata-exported.h
===================================================================
--- LVM2.2.02.39.orig/lib/metadata/metadata-exported.h
+++ LVM2.2.02.39/lib/metadata/metadata-exported.h
@@ -71,6 +71,13 @@ struct pv_segment;
 //#define PRECOMMITTED		0x00200000U	/* VG - internal use only */
 #define CONVERTING		0x00400000U	/* LV */
 
+#define MISSING_PV              0x00800000U	/* PV */
+#define PARTIAL_LV              0x01000000U	/* LV - derived flag, not
+						   written out in metadata*/
+
+//#define POSTORDER_FLAG	0x02000000U /* Not a real flag, reserved for
+//					       temporary use inside vg_read. */
+
 #define LVM_READ              	0x00000100U	/* LV VG */
 #define LVM_WRITE             	0x00000200U	/* LV VG */
 #define CLUSTERED         	0x00000400U	/* VG */
@@ -181,6 +188,7 @@ struct format_instance {
 
 struct volume_group {
 	struct cmd_context *cmd;
+	struct dm_pool *vgmem;
 	struct format_instance *fid;
 	uint32_t seqno;		/* Metadata sequence number */
 
@@ -380,6 +388,7 @@ int vg_change_pesize(struct cmd_context
 int vg_split_mdas(struct cmd_context *cmd, struct volume_group *vg_from,
 		  struct volume_group *vg_to);
 
+void vg_release(struct volume_group *vg);
 /* Manipulate LVs */
 struct logical_volume *lv_create_empty(const char *name,
 				       union lvid *lvid,
@@ -559,6 +568,7 @@ uint64_t pv_pe_start(const pv_t *pv);
 uint32_t pv_pe_count(const pv_t *pv);
 uint32_t pv_pe_alloc_count(const pv_t *pv);
 
+int vg_missing_pv_count(const vg_t *vg);
 uint32_t vg_status(const vg_t *vg);
 #define vg_is_clustered(vg) (vg_status((vg)) & CLUSTERED)
 
Index: LVM2.2.02.39/lib/metadata/metadata.h
===================================================================
--- LVM2.2.02.39.orig/lib/metadata/metadata.h
+++ LVM2.2.02.39/lib/metadata/metadata.h
@@ -35,6 +35,7 @@
 //#define PV_MIN_SIZE ( 512L * 1024L >> SECTOR_SHIFT)	/* 512 KB in sectors */
 //#define MAX_RESTRICTED_LVS 255	/* Used by FMT_RESTRICTED_LVIDS */
 #define MIRROR_LOG_SIZE 1	/* Extents */
+#define VG_MEMPOOL_CHUNK   10240   /* in bytes, hint only */
 
 /* Various flags */
 /* Note that the bits no longer necessarily correspond to LVM1 disk format */
@@ -61,6 +62,14 @@
 //#define MIRROR_NOTSYNCED	0x00080000U	/* LV */
 #define ACTIVATE_EXCL		0x00100000U	/* LV - internal use only */
 #define PRECOMMITTED		0x00200000U	/* VG - internal use only */
+//#define CONVERTING		0x00400000U	/* LV */
+
+//#define MISSING_PV		0x00800000U	/* PV */
+//#define PARTIAL_LV		0x01000000U	/* LV - derived flag, not
+//						   written out in metadata*/
+
+#define POSTORDER_FLAG		0x02000000U /* Not a real flag, reserved for
+					       temporary use inside vg_read. */
 
 //#define LVM_READ              	0x00000100U	/* LV VG */
 //#define LVM_WRITE             	0x00000200U	/* LV VG */
Index: LVM2.2.02.39/lib/report/report.c
===================================================================
--- LVM2.2.02.39.orig/lib/report/report.c
+++ LVM2.2.02.39/lib/report/report.c
@@ -439,7 +439,7 @@ static int _vgstatus_disp(struct dm_repo
 	else
 		repstr[2] = '-';
 
-	if (vg->status & PARTIAL_VG)
+	if (vg_missing_pv_count(vg))
 		repstr[3] = 'p';
 	else
 		repstr[3] = '-';
Index: LVM2.2.02.39/man/lvconvert.8
===================================================================
--- LVM2.2.02.39.orig/man/lvconvert.8
+++ LVM2.2.02.39/man/lvconvert.8
@@ -29,7 +29,7 @@ It is also used to add and remove disk l
 .SH OPTIONS
 See \fBlvm\fP for common options.
 .br
-Exactly one of \-\-mirrors or \-\-snapshot arguments required.
+Exactly one of \-\-mirrors, \-\-repair or \-\-snapshot arguments required.
 .br
 .TP
 .I \-m, \-\-mirrors Mirrors
@@ -61,6 +61,12 @@ Run the daemon in the background.
 Report progress as a percentage at regular intervals.
 .br
 .TP
+.I \-\-repair
+Repair a mirror that has suffered a disk failure. The mirror will be brought
+back into a consistent state, and if possible, the original number of
+mirrors will be restored.
+.br
+.TP
 .I \-s, \-\-snapshot
 Create a snapshot from existing logical volume using another
 existing logical volume as its origin.
Index: LVM2.2.02.39/man/lvm.conf.5
===================================================================
--- LVM2.2.02.39.orig/man/lvm.conf.5
+++ LVM2.2.02.39/man/lvm.conf.5
@@ -300,12 +300,16 @@ in \fBlvm\fP (8).
 .TP
 \fBactivation\fP \(em Settings affecting device-mapper activation
 .IP
-\fBmissing_stripe_filler\fP \(em When activating an incomplete
-logical volume in partial mode, this missing data is replaced
-with this device.  It could perhaps be a block device that always
-returns an error when it is accessed, or one that always
-returns zeros.  See \fBlvcreate\fP (8) for how to create
-such devices.
+\fBmissing_stripe_filler\fP \(em When activating an incomplete logical
+volume in partial mode, this option dictates how the missing data is
+replaced.  A value of "error" will cause activation to create error
+mappings for the missing data, meaning that read access to missing
+portions of the volume will result in I/O errors. You can instead also
+use a device path, and in that case this device will be used in place of
+missing stripes. However, note that using anything other than
+"error" with mirrored or snapshotted volumes is likely to result in data
+corruption.  For instructions on how to create a device that always
+returns zeros, see \fBlvcreate\fP (8).
 .IP
 \fBmirror_region_size\fP \(em Unit size in KB for copy operations
 when mirroring.
Index: LVM2.2.02.39/man/vgreduce.8
===================================================================
--- LVM2.2.02.39.orig/man/vgreduce.8
+++ LVM2.2.02.39/man/vgreduce.8
@@ -18,11 +18,13 @@ See \fBlvm\fP for common options.
 Removes all empty physical volumes if none are given on command line.
 .TP
 .I \-\-removemissing
-Removes all missing physical volumes from the volume group and makes 
-the volume group consistent again.  
+Removes all missing physical volumes from the volume group, if there are no
+logical volumes allocated on those. This resumes normal operation of the volume
+group (new logical volumes may again be created, changed and so on).
 
-It's a good idea to run this option with --test first to find out what it 
-would remove before running it for real.  
+If this is not possible (there are logical volumes referencing the missing
+physical volumes) and you cannot or do not want to remove them manually, you
+can run this option with --force to have vgreduce remove any partial LVs.
 
 Any logical volumes and dependent snapshots that were partly on the 
 missing disks get removed completely. This includes those parts 
Index: LVM2.2.02.39/test/t-lvconvert-repair.sh
===================================================================
--- /dev/null
+++ LVM2.2.02.39/test/t-lvconvert-repair.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# Copyright (C) 2008 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-utils.sh
+
+prepare_vg 4
+
+vgreduce $vg $dev4
+lvcreate -m 1 -L 1 -n mirror $vg
+
+lvchange -a n $vg/mirror
+vgextend $vg $dev4
+disable_dev $dev1
+lvchange --partial -a y $vg/mirror
+
+not vgreduce -v --removemissing $vg
+lvconvert -i 1 --repair $vg/mirror
+vgreduce --removemissing $vg
+
+enable_dev $dev1
+vgextend $vg $dev1
+disable_dev $dev2
+lvconvert -i 1 --repair $vg/mirror
+vgreduce --removemissing $vg
+
+enable_dev $dev2
+vgextend $vg $dev2
+disable_dev $dev3
+lvconvert -i 1 --repair $vg/mirror
+vgreduce --removemissing $vg
+
+enable_dev $dev3
+vgextend $vg $dev3
+lvcreate -m 2 -l 1 -n mirror2 $vg $dev1 $dev2 $dev3 $dev4
+vgchange -a n $vg
+pvremove -ff -y $dev4
+echo 'y' | not lvconvert -i 1 --repair $vg/mirror2
+vgs
Index: LVM2.2.02.39/tools/args.h
===================================================================
--- LVM2.2.02.39.orig/tools/args.h
+++ LVM2.2.02.39/tools/args.h
@@ -49,6 +49,7 @@ arg(nosync_ARG, '\0', "nosync", NULL, 0)
 arg(resync_ARG, '\0', "resync", NULL, 0)
 arg(corelog_ARG, '\0', "corelog", NULL, 0)
 arg(mirrorlog_ARG, '\0', "mirrorlog", string_arg, 0)
+arg(repair_ARG, '\0', "repair", NULL, 0)
 arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0)
 arg(config_ARG, '\0', "config", string_arg, 0)
 arg(trustcache_ARG, '\0', "trustcache", NULL, 0)
Index: LVM2.2.02.39/tools/commands.h
===================================================================
--- LVM2.2.02.39.orig/tools/commands.h
+++ LVM2.2.02.39/tools/commands.h
@@ -94,6 +94,7 @@ xx(lvconvert,
    0,
    "lvconvert "
    "[-m|--mirrors Mirrors [{--mirrorlog {disk|core}|--corelog}]]\n"
+   "\t[--repair]\n"
    "\t[-R|--regionsize MirrorLogRegionSize]\n"
    "\t[--alloc AllocationPolicy]\n"
    "\t[-b|--background]\n"
@@ -115,7 +116,8 @@ xx(lvconvert,
    "\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n",
 
    alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG,
-   mirrorlog_ARG, mirrors_ARG, regionsize_ARG, snapshot_ARG, test_ARG, zero_ARG)
+   mirrorlog_ARG, mirrors_ARG, regionsize_ARG, repair_ARG, snapshot_ARG,
+   test_ARG, zero_ARG)
 
 xx(lvcreate,
    "Create a logical volume",
@@ -850,13 +852,15 @@ xx(vgreduce,
    "\t[-h|--help]\n"
    "\t[--mirrorsonly]\n"
    "\t[--removemissing]\n"
+   "\t[-f|--force]\n"
    "\t[-t|--test]\n"
    "\t[-v|--verbose]\n"
    "\t[--version]" "\n"
    "\tVolumeGroupName\n"
    "\t[PhysicalVolumePath...]\n",
 
-   all_ARG, autobackup_ARG, mirrorsonly_ARG, removemissing_ARG, test_ARG)
+   all_ARG, autobackup_ARG, mirrorsonly_ARG, removemissing_ARG,
+   force_ARG, test_ARG)
 
 xx(vgremove,
    "Remove volume group(s)",
Index: LVM2.2.02.39/tools/lvconvert.c
===================================================================
--- LVM2.2.02.39.orig/tools/lvconvert.c
+++ LVM2.2.02.39/tools/lvconvert.c
@@ -364,6 +364,60 @@ static int _insert_lvconvert_layer(struc
 	return 1;
 }
 
+static int _area_missing(struct lv_segment *lvseg, int s)
+{
+	if (seg_type(lvseg, s) == AREA_LV) {
+		if (seg_lv(lvseg, s)->status & PARTIAL_LV)
+			return 1;
+	} else if (seg_type(lvseg, s) == AREA_PV) {
+		if (seg_pv(lvseg, s)->status & MISSING_PV)
+			return 1;
+	}
+	return 0;
+}
+
+/* FIXME we want to handle mirror stacks here... */
+static int _count_failed_mirrors(struct logical_volume *lv)
+{
+	struct lv_segment *lvseg;
+	int ret = 0;
+	int s;
+	list_iterate_items(lvseg, &lv->segments) {
+		if (!seg_is_mirrored(lvseg))
+			return -1;
+		for(s = 0; s < lvseg->area_count; ++s) {
+			if (_area_missing(lvseg, s))
+				++ ret;
+		}
+	}
+	return ret;
+}
+
+static struct list *_failed_pv_list(struct volume_group *vg)
+{
+	struct list *r;
+	struct pv_list *pvl, *new_pvl;
+
+	if (!(r = dm_pool_alloc(vg->vgmem, sizeof(*r)))) {
+		log_error("Allocation of list failed");
+		return_0;
+	}
+
+	list_init(r);
+	list_iterate_items(pvl, &vg->pvs) {
+		if (!(pvl->pv->status & MISSING_PV))
+			continue;
+
+		if (!(new_pvl = dm_pool_alloc(vg->vgmem, sizeof(*new_pvl)))) {
+			log_error("Unable to allocate physical volume list.");
+			return_0;
+		}
+		new_pvl->pv = pvl->pv;
+		list_add(r, &new_pvl->list);
+	}
+	return r;
+}
+
 /* walk down the stacked mirror LV to the original mirror LV */
 static struct logical_volume *_original_lv(struct logical_volume *lv)
 {
@@ -383,17 +437,26 @@ static int lvconvert_mirrors(struct cmd_
 	const char *mirrorlog;
 	unsigned corelog = 0;
 	struct logical_volume *original_lv;
+	struct logical_volume *log_lv;
+	int failed_mirrors = 0, failed_log = 0;
+	struct list* old_pvh, *remove_pvs = NULL;
 
 	seg = first_seg(lv);
 	existing_mirrors = lv_mirror_count(lv);
 
 	/* If called with no argument, try collapsing the resync layers */
 	if (!arg_count(cmd, mirrors_ARG) && !arg_count(cmd, mirrorlog_ARG) &&
-	    !arg_count(cmd, corelog_ARG) && !arg_count(cmd, regionsize_ARG)) {
+	    !arg_count(cmd, corelog_ARG) && !arg_count(cmd, regionsize_ARG) &&
+	    !arg_count(cmd, repair_ARG)) {
 		lp->need_polling = 1;
 		return 1;
 	}
 
+	if (arg_count(cmd, mirrors_ARG) && arg_count(cmd, repair_ARG)) {
+		log_error("You can only use one of -m, --repair.");
+		return 0;
+	}
+
 	/*
 	 * Adjust required number of mirrors
 	 *
@@ -411,38 +474,59 @@ static int lvconvert_mirrors(struct cmd_
 	else
 		lp->mirrors += 1;
 
-	/*
-	 * Did the user try to subtract more legs than available?
-	 */
-	if (lp->mirrors < 1) {
-		log_error("Logical volume %s only has %" PRIu32 " mirrors.",
-			  lv->name, existing_mirrors);
-		return 0;
-	}
-
-	/*
-	 * Adjust log type
-	 */
-	if (arg_count(cmd, corelog_ARG))
-		corelog = 1;
-
-	mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
-				  corelog ? "core" : DEFAULT_MIRRORLOG);
-	if (!strcmp("disk", mirrorlog)) {
-		if (corelog) {
-			log_error("--mirrorlog disk and --corelog "
-				  "are incompatible");
+	if (arg_count(cmd,repair_ARG)) {
+		cmd->handles_missing_pvs = 1;
+		cmd->partial_activate = 1;
+		lp->need_polling = 0;
+		if (!(lv->status & PARTIAL_LV)) {
+			log_error("The mirror is consistent, nothing to repair.");
+			return 0;
+		}
+		if ((failed_mirrors = _count_failed_mirrors(lv)) < 0)
+			return_0;
+		lp->mirrors -= failed_mirrors;
+		log_error("Mirror status: %d/%d legs failed.",
+			  failed_mirrors, existing_mirrors);
+		old_pvh = lp->pvh;
+		if (!(lp->pvh = _failed_pv_list(lv->vg)))
+			return_0;
+		log_lv=first_seg(lv)->log_lv;
+		if (!log_lv || log_lv->status & PARTIAL_LV)
+			failed_log = corelog = 1;
+	} else {
+		/*
+		 * Did the user try to subtract more legs than available?
+		 */
+		if (lp->mirrors < 1) {
+			log_error("Logical volume %s only has %" PRIu32 " mirrors.",
+				  lv->name, existing_mirrors);
+			return 0;
+		}
+		
+		/*
+		 * Adjust log type
+		 */
+		if (arg_count(cmd, corelog_ARG))
+			corelog = 1;
+		
+		mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
+					  corelog ? "core" : DEFAULT_MIRRORLOG);
+		if (!strcmp("disk", mirrorlog)) {
+			if (corelog) {
+				log_error("--mirrorlog disk and --corelog "
+					  "are incompatible");
+				return 0;
+			}
+			corelog = 0;
+		} else if (!strcmp("core", mirrorlog))
+			corelog = 1;
+		else {
+			log_error("Unknown mirrorlog type: %s", mirrorlog);
 			return 0;
 		}
-		corelog = 0;
-	} else if (!strcmp("core", mirrorlog))
-		corelog = 1;
-	else {
-		log_error("Unknown mirrorlog type: %s", mirrorlog);
-		return 0;
-	}
 
-	log_verbose("Setting logging type to %s", mirrorlog);
+		log_verbose("Setting logging type to %s", mirrorlog);
+	}
 
 	/*
 	 * Region size must not change on existing mirrors
@@ -455,6 +539,18 @@ static int lvconvert_mirrors(struct cmd_
 	}
 
 	/*
+	 * FIXME This check used to precede mirror->mirror conversion
+	 * but didn't affect mirror->linear or linear->mirror. I do
+	 * not understand what is its intention, in fact.
+	 */
+	if (list_size(&lv->segments) != 1) {
+		log_error("Logical volume %s has multiple "
+			  "mirror segments.", lv->name);
+		return 0;
+	}
+	
+ restart:
+	/*
 	 * Converting from mirror to linear
 	 */
 	if ((lp->mirrors == 1)) {
@@ -463,17 +559,24 @@ static int lvconvert_mirrors(struct cmd_
 				  lv->name);
 			return 1;
 		}
-
-		if (!lv_remove_mirrors(cmd, lv, existing_mirrors - 1, 1,
-				       lp->pv_count ? lp->pvh : NULL, 0))
-			return_0;
-		goto commit_changes;
 	}
 
 	/*
-	 * Converting from linear to mirror
+	 * Downconversion.
 	 */
-	if (!(lv->status & MIRRORED)) {
+	if (lp->mirrors < existing_mirrors) {
+		/* Reduce number of mirrors */
+		if (arg_count(cmd, repair_ARG) || lp->pv_count)
+			remove_pvs = lp->pvh;
+		if (!lv_remove_mirrors(cmd, lv, existing_mirrors - lp->mirrors,
+				       (corelog || lp->mirrors == 1) ? 1U : 0U,
+				       remove_pvs, 0))
+			return_0;
+	} else if (!(lv->status & MIRRORED)) {
+		/*
+		 * Converting from linear to mirror
+		 */
+	
 		/* FIXME Share code with lvcreate */
 
 		/* FIXME Why is this restriction here?  Fix it! */
@@ -484,6 +587,11 @@ static int lvconvert_mirrors(struct cmd_
 			}
 		}
 
+		/*
+		 * FIXME should we give not only lp->pvh, but also all PVs
+		 * currently taken by the mirror? Would make more sense from
+		 * user perspective.
+		 */
 		if (!lv_add_mirrors(cmd, lv, lp->mirrors - 1, 1,
 				    adjusted_mirror_region_size(
 						lv->vg->extent_size,
@@ -494,46 +602,7 @@ static int lvconvert_mirrors(struct cmd_
 			return_0;
 		if (lp->wait_completion)
 			lp->need_polling = 1;
-		goto commit_changes;
-	}
-
-	/*
-	 * Converting from mirror to mirror with different leg count,
-	 * or different log type.
-	 */
-	if (list_size(&lv->segments) != 1) {
-		log_error("Logical volume %s has multiple "
-			  "mirror segments.", lv->name);
-		return 0;
-	}
-
-	if (lp->mirrors == existing_mirrors) {
-		/*
-		 * Convert Mirror log type
-		 */
-		original_lv = _original_lv(lv);
-		if (!first_seg(original_lv)->log_lv && !corelog) {
-			if (!add_mirror_log(cmd, original_lv, 1,
-					    adjusted_mirror_region_size(
-							lv->vg->extent_size,
-							lv->le_count,
-							lp->region_size),
-					    lp->pvh, lp->alloc))
-				return_0;
-		} else if (first_seg(original_lv)->log_lv && corelog) {
-			if (!remove_mirror_log(cmd, original_lv,
-					       lp->pv_count ? lp->pvh : NULL))
-				return_0;
-		} else {
-			/* No change */
-			log_error("Logical volume %s already has %"
-				  PRIu32 " mirror(s).", lv->name,
-				  lp->mirrors - 1);
-			if (lv->status & CONVERTING)
-				lp->need_polling = 1;
-			return 1;
-		}
-	} else if (lp->mirrors > existing_mirrors) {
+	} else if (lp->mirrors > existing_mirrors || failed_mirrors) {
 		if (lv->status & MIRROR_NOTSYNCED) {
 			log_error("Not adding mirror to mirrored LV "
 				  "without initial resync");
@@ -575,15 +644,36 @@ static int lvconvert_mirrors(struct cmd_
 			return_0;
 		lv->status |= CONVERTING;
 		lp->need_polling = 1;
-	} else {
-		/* Reduce number of mirrors */
-		if (!lv_remove_mirrors(cmd, lv, existing_mirrors - lp->mirrors,
-				       corelog ? 1U : 0U,
-				       lp->pv_count ? lp->pvh : NULL, 0))
-			return_0;
 	}
 
-commit_changes:
+	if (lp->mirrors == existing_mirrors) {
+		/*
+		 * Convert Mirror log type
+		 */
+		original_lv = _original_lv(lv);
+		if (!first_seg(original_lv)->log_lv && !corelog) {
+			if (!add_mirror_log(cmd, original_lv, 1,
+					    adjusted_mirror_region_size(
+							lv->vg->extent_size,
+							lv->le_count,
+							lp->region_size),
+					    lp->pvh, lp->alloc))
+				return_0;
+		} else if (first_seg(original_lv)->log_lv && corelog) {
+			if (!remove_mirror_log(cmd, original_lv,
+					       lp->pv_count ? lp->pvh : NULL))
+				return_0;
+		} else {
+			/* No change */
+			log_error("Logical volume %s already has %"
+				  PRIu32 " mirror(s).", lv->name,
+				  lp->mirrors - 1);
+			if (lv->status & CONVERTING)
+				lp->need_polling = 1;
+			return 1;
+		}
+	}
+
 	log_very_verbose("Updating logical volume \"%s\" on disk(s)", lv->name);
 
 	if (!vg_write(lv->vg))
@@ -609,6 +699,17 @@ commit_changes:
 		return 0;
 	}
 
+	if (failed_log || failed_mirrors) {
+		lp->pvh = old_pvh;
+		if (failed_log)
+			failed_log = corelog = 0;
+		lp->mirrors += failed_mirrors;
+		failed_mirrors = 0;
+		existing_mirrors = lv_mirror_count(lv);
+		/* Now replace missing devices. */
+		goto restart;
+	}
+
 	if (!lp->need_polling)
 		log_print("Logical volume %s converted.", lv->name);
 
Index: LVM2.2.02.39/tools/lvmcmdline.c
===================================================================
--- LVM2.2.02.39.orig/tools/lvmcmdline.c
+++ LVM2.2.02.39/tools/lvmcmdline.c
@@ -710,13 +710,13 @@ static int _get_settings(struct cmd_cont
 	cmd->current_settings.archive = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.archive);
 	cmd->current_settings.backup = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.backup);
 	cmd->current_settings.cache_vgmetadata = cmd->command->flags & CACHE_VGMETADATA ? 1 : 0;
+	cmd->partial_activate = 0;
 
 	if (arg_count(cmd, partial_ARG)) {
-		init_partial(1);
+		cmd->partial_activate = 1;
 		log_print("Partial mode. Incomplete volume groups will "
 			  "be activated read-only.");
-	} else
-		init_partial(0);
+	}
 
 	if (arg_count(cmd, ignorelockingfailure_ARG))
 		init_ignorelockingfailure(1);
@@ -826,6 +826,7 @@ static void _apply_settings(struct cmd_c
 
 	cmd->fmt = arg_ptr_value(cmd, metadatatype_ARG,
 				 cmd->current_settings.fmt);
+	cmd->handles_missing_pvs = 0;
 }
 
 static char *_copy_command_line(struct cmd_context *cmd, int argc, char **argv)
Index: LVM2.2.02.39/tools/lvremove.c
===================================================================
--- LVM2.2.02.39.orig/tools/lvremove.c
+++ LVM2.2.02.39/tools/lvremove.c
@@ -31,6 +31,8 @@ int lvremove(struct cmd_context *cmd, in
 		return EINVALID_CMD_LINE;
 	}
 
+	cmd->handles_missing_pvs = 1;
+
 	return process_each_lv(cmd, argc, argv, LCK_VG_WRITE, NULL,
 			       &lvremove_single);
 }
Index: LVM2.2.02.39/tools/pvchange.c
===================================================================
--- LVM2.2.02.39.orig/tools/pvchange.c
+++ LVM2.2.02.39/tools/pvchange.c
@@ -57,20 +57,14 @@ static const char* pv_symlink_handle(str
 	struct physical_volume *pv;
 	char *pvuuid;
 	char *pvuuid_link;
-	int old_partial;
 
 	pvuuid_link = malloc(70);
 	if (pvuuid_link == NULL) return NULL;
 
-	old_partial = partial_mode();
-
-	init_partial(1);
 	if (!(pv = pv_read(cmd, name, NULL, NULL, 1))) {
 		free(pvuuid_link);
-		init_partial(old_partial);
 		return NULL;
 	}
-	init_partial(old_partial);
 
 	pvuuid = malloc(sizeof(char)*40);
 	if (pvuuid == NULL) {
Index: LVM2.2.02.39/tools/pvcreate.c
===================================================================
--- LVM2.2.02.39.orig/tools/pvcreate.c
+++ LVM2.2.02.39/tools/pvcreate.c
@@ -66,20 +66,14 @@ static const char* pv_symlink_handle(str
     struct physical_volume *pv;
     char *pvuuid;
     char *pvuuid_link;
-	int old_partial;
 
 	pvuuid_link = malloc(70);
 	if (pvuuid_link == NULL) return NULL;
 
-	old_partial = partial_mode();
-
-    init_partial(1);
     if (!(pv = pv_read(cmd, name, NULL, NULL, 1))) {
 		free(pvuuid_link);
-		init_partial(old_partial);
 		return NULL;
 	}
-    init_partial(old_partial);
 
     pvuuid = malloc(sizeof(char)*40);
     if (pvuuid == NULL) {
@@ -130,8 +124,6 @@ static int pvcreate_check(struct cmd_con
 	}
 
 	/* Is there a pv here already? */
-	/* FIXME Use partial mode here? */
-	init_partial(1);
 	pv = pv_read(cmd, name, NULL, NULL, 0);
 
 	/*
@@ -145,7 +137,6 @@ static int pvcreate_check(struct cmd_con
 			return_0;
 		pv = pv_read(cmd, name, NULL, NULL, 0);
 	}
-	init_partial(0);
 
 	/* Allow partial & exported VGs to be destroyed. */
 	/* We must have -ff to overwrite a non orphan */
@@ -260,13 +251,11 @@ static int pvcreate_single(struct cmd_co
 	if (arg_count(cmd, restorefile_ARG)) {
 		restorefile = arg_str_value(cmd, restorefile_ARG, "");
 		/* The uuid won't already exist */
-		init_partial(1);
 		if (!(vg = backup_read_vg(cmd, NULL, restorefile))) {
 			log_error("Unable to read volume group from %s",
 				  restorefile);
 			return ECMD_FAILED;
 		}
-		init_partial(0);
 		if (!(existing_pv = find_pv_in_vg_by_uuid(vg, idp))) {
 			log_error("Can't find uuid %s in backup file %s",
 				  uuid, restorefile);
Index: LVM2.2.02.39/tools/pvremove.c
===================================================================
--- LVM2.2.02.39.orig/tools/pvremove.c
+++ LVM2.2.02.39/tools/pvremove.c
@@ -24,11 +24,9 @@ static const char* pv_remove_symlink(str
 	char *pvuuid;
 	char pvuuid_link[70];
 
-	init_partial(1);
 	if (!(pv = pv_read(cmd, name, NULL, NULL, 1))) {
 		return NULL;
 	}
-	init_partial(0);
 
 	pvuuid = malloc(sizeof(char)*40);
 	if (pvuuid == NULL) {
Index: LVM2.2.02.39/tools/vgcfgbackup.c
===================================================================
--- LVM2.2.02.39.orig/tools/vgcfgbackup.c
+++ LVM2.2.02.39/tools/vgcfgbackup.c
@@ -96,8 +96,7 @@ int vgcfgbackup(struct cmd_context *cmd,
 	int ret;
 	char *last_filename = NULL;
 
-	if (partial_mode())
-		init_pvmove(1);
+	init_pvmove(1);
 
 	ret = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, &last_filename,
 			      &vg_backup_single);
Index: LVM2.2.02.39/tools/vgreduce.c
===================================================================
--- LVM2.2.02.39.orig/tools/vgreduce.c
+++ LVM2.2.02.39/tools/vgreduce.c
@@ -16,7 +16,7 @@
 #include "tools.h"
 #include "lv_alloc.h"
 
-static int _remove_pv(struct volume_group *vg, struct pv_list *pvl)
+static int _remove_pv(struct volume_group *vg, struct pv_list *pvl, int silent)
 {
 	char uuid[64] __attribute((aligned(8)));
 
@@ -31,8 +31,9 @@ static int _remove_pv(struct volume_grou
 	log_verbose("Removing PV with UUID %s from VG %s", uuid, vg->name);
 
 	if (pvl->pv->pe_alloc_count) {
-		log_error("LVs still present on PV with UUID %s: Can't remove "
-			  "from VG %s", uuid, vg->name);
+		if (!silent)
+			log_error("LVs still present on PV with UUID %s: "
+				  "Can't remove from VG %s", uuid, vg->name);
 		return 0;
 	}
 
@@ -130,11 +131,39 @@ static int _remove_lv(struct cmd_context
 	return 1;
 }
 
+static int _consolidate_vg(struct cmd_context *cmd, struct volume_group *vg)
+{
+	struct pv_list *pvl;
+	struct lv_list *lvl;
+	int r = 1;
+
+	list_iterate_items(lvl, &vg->lvs)
+		if (lvl->lv->status & PARTIAL_LV) {
+			log_warn("WARNING: Partial LV %s needs to be repaired "
+				 "or removed. ", lvl->lv->name);
+			r = 0;
+		}
+
+	if (!r) {
+		cmd->handles_missing_pvs = 1;
+		log_warn("WARNING: There are still partial LVs in VG %s.", vg->name);
+		log_warn("To remove them unconditionally use: vgreduce --removemissing --force.");
+		log_warn("Proceeding to remove empty missing PVs.");
+	}
+
+	list_iterate_items(pvl, &vg->pvs) {
+		if (pvl->pv->dev && !(pvl->pv->status & MISSING_PV))
+			continue;
+		if (r && !_remove_pv(vg, pvl, 0))
+			return_0;
+	}
+
+	return r;
+}
+
 static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
 {
-	struct list *pvh, *pvht;
 	struct list *lvh, *lvht;
-	struct pv_list *pvl;
 	struct lv_list *lvl, *lvl2, *lvlt;
 	struct logical_volume *lv;
 	struct physical_volume *pv;
@@ -183,20 +212,8 @@ static int _make_vg_consistent(struct cm
 		return 0;
 	}
 
-	/* Remove missing PVs */
-	list_iterate_safe(pvh, pvht, &vg->pvs) {
-		pvl = list_item(pvh, struct pv_list);
-		if (pvl->pv->dev)
-			continue;
-		if (!_remove_pv(vg, pvl))
-			return_0;
-	}
-
-	/* VG is now consistent */
-	vg->status &= ~PARTIAL_VG;
-	vg->status |= LVM_WRITE;
-
-	init_partial(0);
+	if (!_consolidate_vg(cmd, vg))
+		return_0;
 
 	/* FIXME Recovery.  For now people must clean up by hand. */
 
@@ -209,14 +226,11 @@ static int _make_vg_consistent(struct cm
 
 		if (!test_mode()) {
 			/* Suspend lvs_changed */
-			init_partial(1);
 			if (!suspend_lvs(cmd, &lvs_changed)) {
 				stack;
-				init_partial(0);
 				vg_revert(vg);
 				return 0;
 			}
-			init_partial(0);
 		}
 
 		if (!vg_commit(vg)) {
@@ -441,26 +455,26 @@ int vgreduce(struct cmd_context *cmd, in
 	char *vg_name;
 	int ret = 1;
 	int consistent = 1;
+	int fixed = 1;
+	int repairing = arg_count(cmd, removemissing_ARG);
 
-	if (!argc && !arg_count(cmd, removemissing_ARG)) {
+	if (!argc && !repairing) {
 		log_error("Please give volume group name and "
 			  "physical volume paths");
 		return EINVALID_CMD_LINE;
 	}
-
-	if (!argc && arg_count(cmd, removemissing_ARG)) {
+	
+	if (!argc && repairing) {
 		log_error("Please give volume group name");
 		return EINVALID_CMD_LINE;
 	}
 
-	if (arg_count(cmd, mirrorsonly_ARG) &&
-	    !arg_count(cmd, removemissing_ARG)) {
+	if (arg_count(cmd, mirrorsonly_ARG) && !repairing) {
 		log_error("--mirrorsonly requires --removemissing");
 		return EINVALID_CMD_LINE;
 	}
 
-	if (argc == 1 && !arg_count(cmd, all_ARG)
-	    && !arg_count(cmd, removemissing_ARG)) {
+	if (argc == 1 && !arg_count(cmd, all_ARG) && !repairing) {
 		log_error("Please enter physical volume paths or option -a");
 		return EINVALID_CMD_LINE;
 	}
@@ -471,7 +485,7 @@ int vgreduce(struct cmd_context *cmd, in
 		return EINVALID_CMD_LINE;
 	}
 
-	if (argc > 1 && arg_count(cmd, removemissing_ARG)) {
+	if (argc > 1 && repairing) {
 		log_error("Please only specify the volume group");
 		return EINVALID_CMD_LINE;
 	}
@@ -492,8 +506,8 @@ int vgreduce(struct cmd_context *cmd, in
 		return ECMD_FAILED;
 	}
 
-	if ((!(vg = vg_read(cmd, vg_name, NULL, &consistent)) || !consistent) &&
-	    !arg_count(cmd, removemissing_ARG)) {
+	if ((!(vg = vg_read(cmd, vg_name, NULL, &consistent)) || !consistent)
+	    && !repairing) {
 		log_error("Volume group \"%s\" doesn't exist", vg_name);
 		unlock_vg(cmd, vg_name);
 		return ECMD_FAILED;
@@ -504,16 +518,15 @@ int vgreduce(struct cmd_context *cmd, in
 		return ECMD_FAILED;
 	}
 
-	if (arg_count(cmd, removemissing_ARG)) {
-		if (vg && consistent) {
+	if (repairing) {
+		if (vg && consistent && !vg_missing_pv_count(vg)) {
 			log_error("Volume group \"%s\" is already consistent",
 				  vg_name);
 			unlock_vg(cmd, vg_name);
 			return ECMD_PROCESSED;
 		}
 
-		init_partial(1);
-		consistent = 0;
+		consistent = !arg_count(cmd, force_ARG);
 		if (!(vg = vg_read(cmd, vg_name, NULL, &consistent))) {
 			log_error("Volume group \"%s\" not found", vg_name);
 			unlock_vg(cmd, vg_name);
@@ -524,16 +537,17 @@ int vgreduce(struct cmd_context *cmd, in
 			return ECMD_FAILED;
 		}
 		if (!archive(vg)) {
-			init_partial(0);
 			unlock_vg(cmd, vg_name);
 			return ECMD_FAILED;
 		}
 
-		if (!_make_vg_consistent(cmd, vg)) {
-			init_partial(0);
-			unlock_vg(cmd, vg_name);
-			return ECMD_FAILED;
-		}
+		if (arg_count(cmd, force_ARG)) {
+			if (!_make_vg_consistent(cmd, vg)) {
+				unlock_vg(cmd, vg_name);
+				return ECMD_FAILED;
+			}
+		} else
+			fixed = _consolidate_vg(cmd, vg);
 
 		if (!vg_write(vg) || !vg_commit(vg)) {
 			log_error("Failed to write out a consistent VG for %s",
@@ -544,7 +558,9 @@ int vgreduce(struct cmd_context *cmd, in
 
 		backup(vg);
 
-		log_print("Wrote out consistent volume group %s", vg_name);
+		if (fixed)
+			log_print("Wrote out consistent volume group %s",
+				  vg_name);
 
 	} else {
 		if (!vg_check_status(vg, EXPORTED_VG | LVM_WRITE | RESIZEABLE_VG)) {
