Module Name:    src
Committed By:   martin
Date:           Tue Nov 12 16:33:14 UTC 2019

Modified Files:
        src/usr.sbin/sysinst: bsddisklabel.c defs.h disklabel.c disks.c gpt.c
            install.c label.c main.c mbr.c mbr.h msg.mi.de msg.mi.en msg.mi.es
            msg.mi.fr msg.mi.pl part_edit.c partitions.c partitions.h util.c
        src/usr.sbin/sysinst/arch/i386: md.c

Log Message:
Add options to the various partitioning stages that allow cloning of
alien partitions (optionally including data).


To generate a diff of this commit:
cvs rdiff -u -r1.29 -r1.30 src/usr.sbin/sysinst/bsddisklabel.c
cvs rdiff -u -r1.45 -r1.46 src/usr.sbin/sysinst/defs.h
cvs rdiff -u -r1.14 -r1.15 src/usr.sbin/sysinst/disklabel.c
cvs rdiff -u -r1.54 -r1.55 src/usr.sbin/sysinst/disks.c
cvs rdiff -u -r1.11 -r1.12 src/usr.sbin/sysinst/gpt.c \
    src/usr.sbin/sysinst/install.c
cvs rdiff -u -r1.12 -r1.13 src/usr.sbin/sysinst/label.c
cvs rdiff -u -r1.17 -r1.18 src/usr.sbin/sysinst/main.c \
    src/usr.sbin/sysinst/msg.mi.es
cvs rdiff -u -r1.21 -r1.22 src/usr.sbin/sysinst/mbr.c \
    src/usr.sbin/sysinst/msg.mi.fr
cvs rdiff -u -r1.3 -r1.4 src/usr.sbin/sysinst/mbr.h
cvs rdiff -u -r1.16 -r1.17 src/usr.sbin/sysinst/msg.mi.de
cvs rdiff -u -r1.23 -r1.24 src/usr.sbin/sysinst/msg.mi.en
cvs rdiff -u -r1.24 -r1.25 src/usr.sbin/sysinst/msg.mi.pl
cvs rdiff -u -r1.10 -r1.11 src/usr.sbin/sysinst/part_edit.c
cvs rdiff -u -r1.4 -r1.5 src/usr.sbin/sysinst/partitions.c
cvs rdiff -u -r1.7 -r1.8 src/usr.sbin/sysinst/partitions.h
cvs rdiff -u -r1.34 -r1.35 src/usr.sbin/sysinst/util.c
cvs rdiff -u -r1.21 -r1.22 src/usr.sbin/sysinst/arch/i386/md.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.sbin/sysinst/bsddisklabel.c
diff -u src/usr.sbin/sysinst/bsddisklabel.c:1.29 src/usr.sbin/sysinst/bsddisklabel.c:1.30
--- src/usr.sbin/sysinst/bsddisklabel.c:1.29	Fri Oct 25 12:24:34 2019
+++ src/usr.sbin/sysinst/bsddisklabel.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: bsddisklabel.c,v 1.29 2019/10/25 12:24:34 martin Exp $	*/
+/*	$NetBSD: bsddisklabel.c,v 1.30 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -251,6 +251,11 @@ draw_size_menu_line(menudesc *m, int opt
 		mount = swap;
 	} else if (pset->infos[opt].mount[0]) {
 		mount = pset->infos[opt].mount;
+	} else if (pset->infos[opt].flags & PUIFLG_CLONE_PARTS) {
+		snprintf(swap, sizeof swap, "%zu %s",
+		    pset->infos[opt].clone_src->num_sel,
+		    msg_string(MSG_clone_target_disp));
+		mount = swap;
 	} else {
 		mount = NULL;
 		if (pset->infos[opt].parts->pscheme->other_partition_identifier
@@ -319,7 +324,7 @@ add_other_ptn_size(menudesc *menu, void 
 			break;
 	}
 
-	m = realloc(pset->menu_opts, (pset->num+4)*sizeof(*pset->menu_opts));
+	m = realloc(pset->menu_opts, (pset->num+5)*sizeof(*pset->menu_opts));
 	if (m == NULL)
 		return 0;
 	p = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos));
@@ -348,6 +353,84 @@ add_other_ptn_size(menudesc *menu, void 
 	return -1;
 }
 
+static int
+inst_ext_clone(menudesc *menu, void *arg)
+{
+	struct selected_partitions selected;
+	struct clone_target_menu_data data;
+	struct partition_usage_set *pset = arg;
+	struct part_usage_info *p;
+	menu_ent *men;
+	int num_men, i;
+ 
+	if (!select_partitions(&selected, pm->parts))
+		return 0;
+
+	num_men = pset->num+1;
+	men = calloc(num_men, sizeof *men);
+	if (men == NULL)
+		return 0;
+	for (i = 0; i < num_men; i++)
+		men[i].opt_action = clone_target_select;
+	men[num_men-1].opt_name = MSG_clone_target_end;
+
+	memset(&data, 0, sizeof data);
+	data.usage = *pset;
+	data.res = -1;
+
+	data.usage.menu = new_menu(MSG_clone_target_hdr,
+	    men, num_men, 3, 2, 0, 65, MC_SCROLL,
+	    NULL, draw_size_menu_line, NULL, NULL, MSG_cancel);
+	process_menu(data.usage.menu, &data);
+	free_menu(data.usage.menu);
+	free(men);
+
+	if (data.res < 0)
+		goto err;
+
+	/* insert clone record */
+	men = realloc(pset->menu_opts, (pset->num+5)*sizeof(*pset->menu_opts));
+	if (men == NULL)
+		goto err;
+	pset->menu_opts = men;
+	menu->opts = men;
+	menu->numopts = pset->num+4;
+
+	p = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos));
+	if (p == NULL)
+		goto err;
+	pset->infos = p;
+
+	men += data.res;
+	p += data.res;
+	memmove(men+1, men, sizeof(*men)*((pset->num+4)-data.res));
+	memmove(p+1, p, sizeof(*p)*((pset->num)-data.res));
+	memset(men, 0, sizeof(*men));
+	memset(p, 0, sizeof(*p));
+	p->flags = PUIFLG_CLONE_PARTS;
+	p->cur_part_id = NO_PART;
+	p->clone_src = malloc(sizeof(selected));
+	if (p->clone_src != NULL) {
+		*p->clone_src = selected;
+		p->clone_ndx = ~0U;
+		p->size = selected_parts_size(&selected);
+		p->parts = pset->parts;
+	} else {
+		p->clone_ndx = 0;
+		free_selected_partitions(&selected);
+	}
+
+	menu->cursel = data.res == 0 ? 1 : 0;
+	pset->num++;
+	fill_ptn_menu(pset);
+
+	return -1;
+
+err:
+	free_selected_partitions(&selected);
+	return 0;
+}
+
 static size_t
 fill_ptn_menu(struct partition_usage_set *pset)
 {
@@ -357,9 +440,12 @@ fill_ptn_menu(struct partition_usage_set
 	size_t i;
 	daddr_t free_space;
 
-	memset(pset->menu_opts, 0, (pset->num+3)*sizeof(*pset->menu_opts));
+	memset(pset->menu_opts, 0, (pset->num+4)*sizeof(*pset->menu_opts));
 	for (m = pset->menu_opts, p = pset->infos, i = 0; i < pset->num;
 	    m++, p++, i++) {
+		if (p->flags & PUIFLG_CLONE_PARTS)
+			m->opt_flags = OPT_IGNORE|OPT_NOSHORT;
+		else
 		m->opt_action = set_ptn_size;
 	}
 
@@ -371,6 +457,10 @@ fill_ptn_menu(struct partition_usage_set
 	m->opt_action = add_other_ptn_size;
 	m++;
 
+	m->opt_name = MSG_clone_from_elsewhere;
+	m->opt_action = inst_ext_clone;
+	m++;
+
 	m->opt_name = MSG_askunits;
 	m->opt_menu = MENU_sizechoice;
 	m->opt_flags = OPT_SUB;
@@ -552,7 +642,7 @@ get_ptn_sizes(struct partition_usage_set
 	wrefresh(stdscr);
 
 	if (pset->menu_opts == NULL)
-		pset->menu_opts = calloc(pset->num+3, sizeof(*pset->menu_opts));
+		pset->menu_opts = calloc(pset->num+4, sizeof(*pset->menu_opts));
 
 	pset->menu = -1;
 	num = fill_ptn_menu(pset);
@@ -1000,7 +1090,7 @@ sort_and_sync_parts(struct partition_usa
 			continue;
 		if (pset->infos[i].flags & PUIFLG_JUST_MOUNTPOINT)
 			continue;
-		if ((pset->infos[i].flags & (PUIFLG_IS_OUTER|PUIFLAG_ADD_INNER))
+		if ((pset->infos[i].flags & (PUIFLG_IS_OUTER|PUIFLG_ADD_INNER))
 		    == PUIFLG_IS_OUTER)
 			continue;
 		if (pno >= pset->parts->num_part)
@@ -1046,17 +1136,73 @@ sort_and_sync_parts(struct partition_usa
 	pset->infos = infos;
 }
 
+/*
+ * Convert clone entries with more than one source into
+ * several entries with a single source each.
+ */
+static void
+normalize_clones(struct part_usage_info **infos, size_t *num)
+{
+	size_t i, j, add_clones;
+	struct part_usage_info *ui, *src, *target;
+	struct disk_part_info info;
+	struct selected_partition *clone;
+
+	for (add_clones = 0, i = 0; i < *num; i++) {
+		if ((*infos)[i].clone_src != NULL &&
+		    (*infos)[i].flags & PUIFLG_CLONE_PARTS &&
+		    (*infos)[i].cur_part_id == NO_PART)
+			add_clones += (*infos)[i].clone_src->num_sel-1;
+	}
+	if (add_clones == 0)
+		return;
+
+	ui = calloc(*num+add_clones, sizeof(**infos));
+	if (ui == NULL)
+		return;	/* can not handle this well here, drop some clones */
+
+	/* walk the list and dedup clones */
+	for (src = *infos, target = ui, i = 0; i < *num; i++) {
+		if (src != target)
+			*target = *src;
+		if (target->clone_src != NULL &&
+		    (target->flags & PUIFLG_CLONE_PARTS) &&
+		    target->cur_part_id == NO_PART) {
+			for (j = 0; j < src->clone_src->num_sel; j++) {
+				if (j > 0) {
+					target++;
+					*target = *src;
+				}
+				target->clone_ndx = j;
+				clone = &target->clone_src->selection[j];
+				clone->parts->pscheme->get_part_info(
+				    clone->parts, clone->id, &info);
+				target->size = info.size;
+			}
+		}
+		target++;
+		src++;
+	}
+	*num += add_clones;
+	assert((target-ui) >= 0 && (size_t)(target-ui) == *num);
+	free(*infos);
+	*infos = ui;
+}
+
 static void
 apply_settings_to_partitions(struct pm_devs *p, struct disk_partitions *parts,
     struct partition_usage_set *wanted, daddr_t start, daddr_t size)
 {
 	size_t i, exp_ndx = ~0U;
 	daddr_t planned_space = 0, nsp, from, align;
-	struct disk_part_info *infos;
+	struct disk_part_info *infos, cinfo, srcinfo;
 	struct disk_part_free_space space;
 	struct disk_partitions *ps = NULL;
+	struct selected_partition *sp;
 	part_id pno, new_part_id;
 
+	normalize_clones(&wanted->infos, &wanted->num);
+
 	infos = calloc(wanted->num, sizeof(*infos));
 	if (infos == NULL) {
 		err_msg_win(err_outofmem);
@@ -1167,14 +1313,14 @@ apply_settings_to_partitions(struct pm_d
 			    new_part_id, &infos[i]);
 			want->cur_part_id = new_part_id;
 
-			want->flags |= PUIFLAG_ADD_INNER|PUIFLG_IS_OUTER;
+			want->flags |= PUIFLG_ADD_INNER|PUIFLG_IS_OUTER;
 			from = rounddown(infos[i].start + 
 			    infos[i].size+outer_align, outer_align);
 		}
 	}
 
 	/*
-	 * Now add new inner partitions
+	 * Now add new inner partitions (and cloned partitions)
 	 */
 	for (i = 0; i < wanted->num && from < wanted->parts->disk_size; i++) {
 		struct part_usage_info *want = &wanted->infos[i];
@@ -1183,35 +1329,66 @@ apply_settings_to_partitions(struct pm_d
 			continue;
 		if (want->flags & (PUIFLG_JUST_MOUNTPOINT|PUIFLG_IS_OUTER))
 			continue;
-		if (want->size <= 0)
-			continue;
+		if ((want->flags & PUIFLG_CLONE_PARTS) &&
+		    want->clone_src != NULL &&
+		    want->clone_ndx < want->clone_src->num_sel) {
+			sp = &want->clone_src->selection[want->clone_ndx];
+			if (!sp->parts->pscheme->get_part_info(
+			    sp->parts, sp->id, &srcinfo))
+				continue;
+			if (!wanted->parts->pscheme->
+			    adapt_foreign_part_info(wanted->parts,
+			    &cinfo, sp->parts->pscheme, &srcinfo))
+				continue;
 
-		size_t cnt = wanted->parts->pscheme->get_free_spaces(
-		    wanted->parts, &space, 1, want->size-align, align, from,
-		    -1);
-		if (cnt == 0)
-			cnt = wanted->parts->pscheme->get_free_spaces(
-			    wanted->parts, &space, 1,
-			    want->size-5*align, align, from, -1);
-
-		if (cnt == 0)
-			continue;	/* no free space for this partition */
-
-		infos[i].start = space.start;
-		infos[i].size = min(want->size, space.size);
-		infos[i].nat_type =
-		    wanted->parts->pscheme->get_fs_part_type(want->fs_type,
-		    want->fs_version);
-		infos[i].last_mounted = want->mount;
-		infos[i].fs_type = want->fs_type;
-		infos[i].fs_sub_type = want->fs_version;
-		if (want->fs_type != FS_UNUSED && want->type != PT_swap) {
-			want->instflags |= PUIINST_NEWFS;
-			if (want->mount[0] != 0)
-				want->instflags |= PUIINST_MOUNT;
+			/* find space for cinfo and add a partition */
+			size_t cnt = wanted->parts->pscheme->get_free_spaces(
+			    wanted->parts, &space, 1, want->size-align, align,
+			    from, -1);
+			if (cnt == 0)
+				cnt = wanted->parts->pscheme->get_free_spaces(
+				    wanted->parts, &space, 1,
+				    want->size-5*align, align, from, -1);
+
+			if (cnt == 0)
+				continue; /* no free space for this clone */
+
+			infos[i] = cinfo;
+			infos[i].start = space.start;
+			new_part_id = wanted->parts->pscheme->add_partition(
+			    wanted->parts, &infos[i], NULL);
+		} else {
+			if (want->size <= 0)
+				continue;
+			size_t cnt = wanted->parts->pscheme->get_free_spaces(
+			    wanted->parts, &space, 1, want->size-align, align,
+			    from, -1);
+			if (cnt == 0)
+				cnt = wanted->parts->pscheme->get_free_spaces(
+				    wanted->parts, &space, 1,
+				    want->size-5*align, align, from, -1);
+
+			if (cnt == 0)
+				continue; /* no free space for this partition */
+
+			infos[i].start = space.start;
+			infos[i].size = min(want->size, space.size);
+			infos[i].nat_type =
+			    wanted->parts->pscheme->get_fs_part_type(
+			    want->fs_type, want->fs_version);
+			infos[i].last_mounted = want->mount;
+			infos[i].fs_type = want->fs_type;
+			infos[i].fs_sub_type = want->fs_version;
+			if (want->fs_type != FS_UNUSED &&
+			    want->type != PT_swap) {
+				want->instflags |= PUIINST_NEWFS;
+				if (want->mount[0] != 0)
+					want->instflags |= PUIINST_MOUNT;
+			}
+			new_part_id = wanted->parts->pscheme->add_partition(
+			    wanted->parts, &infos[i], NULL);
 		}
-		new_part_id = wanted->parts->pscheme->add_partition(
-		    wanted->parts, &infos[i], NULL);
+
 		if (new_part_id == NO_PART)
 			continue;	/* failed to add, skip */
 
@@ -1235,8 +1412,8 @@ apply_settings_to_partitions(struct pm_d
 		if (want->size <= 0)
 			continue;
 
-		if ((want->flags & (PUIFLAG_ADD_INNER|PUIFLG_IS_OUTER)) !=
-		    (PUIFLAG_ADD_INNER|PUIFLG_IS_OUTER))
+		if ((want->flags & (PUIFLG_ADD_INNER|PUIFLG_IS_OUTER)) !=
+		    (PUIFLG_ADD_INNER|PUIFLG_IS_OUTER))
 			continue;
 
 		infos[i].start = want->cur_start;

Index: src/usr.sbin/sysinst/defs.h
diff -u src/usr.sbin/sysinst/defs.h:1.45 src/usr.sbin/sysinst/defs.h:1.46
--- src/usr.sbin/sysinst/defs.h:1.45	Wed Oct  2 11:16:04 2019
+++ src/usr.sbin/sysinst/defs.h	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: defs.h,v 1.45 2019/10/02 11:16:04 maya Exp $	*/
+/*	$NetBSD: defs.h,v 1.46 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -252,8 +252,9 @@ struct part_usage_info {
 #define	PUIFLAG_ADD_OUTER	2	/* Add this partition to the outer
 					 * partitions (if available) */
 #define	PUIFLG_IS_OUTER		4	/* this is an existing outer one */
-#define	PUIFLAG_ADD_INNER	8	/* add outer also to inner */
+#define	PUIFLG_ADD_INNER	8	/* add outer also to inner */
 #define	PUIFLG_JUST_MOUNTPOINT	16	/* tmpfs of mfs mountpoints */
+#define	PUIFLG_CLONE_PARTS	32	/* clone external partitions */
 	uint flags;
 	struct disk_partitions *parts;	/* Where does this partition live?
 					 * We currently only support
@@ -282,6 +283,17 @@ struct part_usage_info {
 	unsigned int instflags;		/* installer handling flags */
 	uint fs_type, fs_version;	/* e.g. FS_LFS, or FS_BSDFS,
 					 * version = 2 for FFSv2 */
+	/*
+	 * Only != NULL when PUIFLG_CLONE_PARTS is set, describes the
+	 * source partitions to clone here.
+	 */
+	struct selected_partitions *clone_src;
+	/*
+	 * If clone_src != NULL, this record corresponds to a single
+	 * selected source partition, if clone_ndx is a valid index in clone_src
+	 * (>= 0 && <= clone_src->num_sel, or all of them if clone_ndx = ~0U.
+	 */
+	size_t clone_ndx;
 };
 
 /*
@@ -607,6 +619,30 @@ bool is_cdrom_device(const char *dev, bo
 bool is_bootable_device(const char *dev);
 bool is_partitionable_device(const char *dev);
 bool convert_scheme(struct pm_devs *p, bool is_boot_drive, const char **err_msg);
+/* a single partition selected for cloning (etc) */
+struct selected_partition {
+	struct disk_partitions *parts;
+	part_id id;
+};
+struct selected_partitions {
+	struct selected_partition *selection;
+	size_t num_sel;
+	bool with_data;		/* partitions and their data selected */
+	bool free_parts;	/* caller should free parts */
+};
+bool select_partitions(struct selected_partitions *res,
+    const struct disk_partitions *ignore);
+daddr_t	selected_parts_size(struct selected_partitions *);
+void	free_selected_partitions(struct selected_partitions *);
+
+struct clone_target_menu_data {
+	struct partition_usage_set usage;
+	int res;
+};
+
+int	clone_target_select(menudesc *m, void *arg);
+bool	clone_partition_data(struct disk_partitions *dest_parts, part_id did,
+	struct disk_partitions *src_parts, part_id sid);
 
 struct menudesc;
 void	disp_cur_fspart(int, int);
@@ -716,6 +752,7 @@ bool	md_parts_use_wholedisk(struct disk_
 /* from util.c */
 bool	root_is_read_only(void);
 void	get_ptn_alignment(const struct disk_partitions *parts, daddr_t *align, daddr_t *p0off);
+struct disk_partitions *get_inner_parts(struct disk_partitions *parts);
 char*	str_arg_subst(const char *, size_t, const char **);
 void	msg_display_subst(const char *, size_t, ...);
 void	msg_display_add_subst(const char *, size_t, ...);

Index: src/usr.sbin/sysinst/disklabel.c
diff -u src/usr.sbin/sysinst/disklabel.c:1.14 src/usr.sbin/sysinst/disklabel.c:1.15
--- src/usr.sbin/sysinst/disklabel.c:1.14	Mon Oct 21 16:09:59 2019
+++ src/usr.sbin/sysinst/disklabel.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: disklabel.c,v 1.14 2019/10/21 16:09:59 martin Exp $	*/
+/*	$NetBSD: disklabel.c,v 1.15 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 2018 The NetBSD Foundation, Inc.
@@ -140,7 +140,7 @@ disklabel_parts_new(const char *dev, dad
 	parts->l.d_secpercyl = geo.dg_nsectors * geo.dg_ntracks;
 
 	parts->dp.pscheme = &disklabel_parts;
-	parts->dp.disk = dev;
+	parts->dp.disk = strdup(dev);
 	parts->dp.disk_start = start;
 	parts->dp.disk_size = parts->dp.free_space = len;
 	disklabel_init_default_alignment(parts, parts->l.d_secpercyl);
@@ -216,7 +216,7 @@ disklabel_parts_read(const char *disk, d
 	if (len > disklabel_parts.size_limit)
 		len = disklabel_parts.size_limit;
 	parts->dp.pscheme = scheme;
-	parts->dp.disk = disk;
+	parts->dp.disk = strdup(disk);
 	parts->dp.disk_start = start;
 	parts->dp.disk_size = parts->dp.free_space = len;
 	disklabel_init_default_alignment(parts, 0);
@@ -582,6 +582,12 @@ disklabel_get_fs_part_type(unsigned fsty
 }
 
 static const struct part_type_desc *
+disklabel_create_unknown_part_type(void)
+{
+	return disklabel_find_type(FS_OTHER, false);
+}
+
+static const struct part_type_desc *
 disklabel_get_generic_type(enum part_type pt)
 {
 	size_t nt;
@@ -1076,6 +1082,7 @@ disklabel_free(struct disk_partitions *a
 {
 
 	assert(arg != NULL);
+	free(__UNCONST(arg->disk));
 	free(arg);
 }
 
@@ -1100,7 +1107,9 @@ disklabel_parts = {
 	.get_generic_part_type = disklabel_get_generic_type,
 	.get_fs_part_type = disklabel_get_fs_part_type,
 	.create_custom_part_type = disklabel_create_custom_part_type,
+	.create_unknown_part_type = disklabel_create_unknown_part_type,
 	.get_part_alignment = disklabel_get_alignment,
+	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
 	.get_part_info = disklabel_get_part_info,
 	.can_add_partition = disklabel_can_add_partition,
 	.set_part_info = disklabel_set_part_info,

Index: src/usr.sbin/sysinst/disks.c
diff -u src/usr.sbin/sysinst/disks.c:1.54 src/usr.sbin/sysinst/disks.c:1.55
--- src/usr.sbin/sysinst/disks.c:1.54	Fri Oct 25 12:49:58 2019
+++ src/usr.sbin/sysinst/disks.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: disks.c,v 1.54 2019/10/25 12:49:58 martin Exp $ */
+/*	$NetBSD: disks.c,v 1.55 2019/11/12 16:33:14 martin Exp $ */
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -734,7 +734,7 @@ convert_copy(struct disk_partitions *old
 		}
 
 		if (!new_parts->pscheme->adapt_foreign_part_info(new_parts,
-			    &oinfo, &ninfo))
+			    &ninfo, old_parts->pscheme, &oinfo))
 			continue;
 		new_parts->pscheme->add_partition(new_parts, &ninfo, NULL);
 	}
@@ -2045,3 +2045,472 @@ get_dkwedges(struct dkwedge_info **dkw, 
 
 	return dkwl.dkwl_nwedges;
 }
+
+/*
+ * Helper structures used in the partition select menu
+ */
+struct single_partition {
+	struct disk_partitions *parts;
+	part_id id;
+};
+
+struct sel_menu_data {
+	struct single_partition *partitions;
+	struct selected_partition result;
+};
+
+static int
+select_single_part(menudesc *m, void *arg)
+{
+	struct sel_menu_data *data = arg;
+
+	data->result.parts = data->partitions[m->cursel].parts;
+	data->result.id = data->partitions[m->cursel].id;
+
+	return 1;
+}
+
+static void
+display_single_part(menudesc *m, int opt, void *arg)
+{
+	const struct sel_menu_data *data = arg;
+	struct disk_part_info info;
+	struct disk_partitions *parts = data->partitions[opt].parts;
+	part_id id = data->partitions[opt].id;
+	int l;
+	const char *desc = NULL;
+	char line[MENUSTRSIZE*2];
+
+	if (!parts->pscheme->get_part_info(parts, id, &info))
+		return;
+
+	if (parts->pscheme->other_partition_identifier != NULL)
+		desc = parts->pscheme->other_partition_identifier(
+		    parts, id);
+
+	daddr_t start = info.start / sizemult;
+	daddr_t size = info.size / sizemult;
+	snprintf(line, sizeof line, "%s [%" PRIu64 " @ %" PRIu64 "]",
+	    parts->disk, size, start);
+
+	if (info.nat_type != NULL) {
+		strlcat(line, " ", sizeof line);
+		strlcat(line, info.nat_type->description, sizeof line);
+	}
+
+	if (desc != NULL) {
+		strlcat(line, ": ", sizeof line);
+		strlcat(line, desc, sizeof line);
+	}
+
+	l = strlen(line);
+	if (l >= (m->w))
+		strcpy(line + (m->w-3), "...");
+	wprintw(m->mw, "%s", line);
+}
+
+/*
+ * is the given "test" partitions set used in the selected set?
+ */
+static bool
+selection_has_parts(struct selected_partitions *sel,
+    const struct disk_partitions *test)
+{
+	size_t i;
+
+	for (i = 0; i < sel->num_sel; i++) {
+		if (sel->selection[i].parts == test)
+			return true;
+	}
+	return false;
+}
+
+/*
+ * is the given "test" partition in the selected set?
+ */
+static bool
+selection_has_partition(struct selected_partitions *sel,
+    const struct disk_partitions *test, part_id test_id)
+{
+	size_t i;
+
+	for (i = 0; i < sel->num_sel; i++) {
+		if (sel->selection[i].parts == test &&
+		    sel->selection[i].id == test_id)
+			return true;
+	}
+	return false;
+}
+
+/*
+ * let the user select a partition, optionally skipping all partitions
+ * on the "ignore" device
+ */
+static bool
+add_select_partition(struct selected_partitions *res,
+    struct disk_partitions **all_parts, size_t all_cnt)
+{
+	struct disk_partitions *ps;
+	struct disk_part_info info;
+	part_id id;
+	struct single_partition *partitions, *pp;
+	struct menu_ent *part_menu_opts, *menup;
+	size_t n, part_cnt;
+	int sel_menu;
+
+	/*
+	 * count how many items our menu will have
+	 */
+	part_cnt = 0;
+	for (n = 0; n < all_cnt; n++) {
+		ps = all_parts[n];
+		for (id = 0; id < ps->num_part; id++) {
+			if (selection_has_partition(res, ps, id))
+				continue;
+			if (!ps->pscheme->get_part_info(ps, id, &info))
+				continue;
+			if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK|
+			    PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
+				continue;
+			part_cnt++;
+		}
+	}
+
+	/*
+	 * create a menu from this and let the user
+	 * select one partition
+	 */
+	part_menu_opts = NULL;
+	partitions = calloc(part_cnt, sizeof *partitions);
+	if (partitions == NULL)
+		goto done;
+	part_menu_opts = calloc(part_cnt, sizeof *part_menu_opts);
+	if (part_menu_opts == NULL)
+		goto done;
+	pp = partitions;
+	menup = part_menu_opts;
+	for (n = 0; n < all_cnt; n++) {
+		ps = all_parts[n];
+		for (id = 0; id < ps->num_part; id++) {
+			if (selection_has_partition(res, ps, id))
+				continue;
+			if (!ps->pscheme->get_part_info(ps, id, &info))
+				continue;
+			if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK|
+			    PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
+				continue;
+			pp->parts = ps;
+			pp->id = id;
+			pp++;
+			menup->opt_action = select_single_part;
+			menup++;
+		}
+	}
+	sel_menu = new_menu(MSG_select_foreign_part, part_menu_opts, part_cnt,
+	    3, 3, 0, 60,
+	    MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
+	    NULL, display_single_part, NULL,
+	    NULL, NULL);
+	if (sel_menu != -1) {
+		struct selected_partition *newsels;
+		struct sel_menu_data data;
+
+		memset(&data, 0, sizeof data);
+		data.partitions = partitions;
+		process_menu(sel_menu, &data);
+		free_menu(sel_menu);
+
+		if (data.result.parts != NULL) {
+			newsels = realloc(res->selection,
+			    sizeof(*res->selection)*(res->num_sel+1));
+			if (newsels != NULL) {
+				res->selection = newsels;
+				newsels += res->num_sel++;
+				newsels->parts = data.result.parts;
+				newsels->id = data.result.id;
+			}
+		}
+	}
+
+	/*
+	 * Final cleanup
+	 */
+done:
+	free(part_menu_opts);
+	free(partitions);
+
+	return res->num_sel > 0;
+}
+
+struct part_selection_and_all_parts {
+	struct selected_partitions *selection;
+	struct disk_partitions **all_parts;
+	size_t all_cnt;
+	char *title;
+	bool cancelled;
+};
+
+static int
+toggle_clone_data(struct menudesc *m, void *arg)
+{
+	struct part_selection_and_all_parts *sel = arg;
+
+	sel->selection->with_data = !sel->selection->with_data;
+	return 0;
+}
+
+static int
+add_another(struct menudesc *m, void *arg)
+{
+	struct part_selection_and_all_parts *sel = arg;
+
+	add_select_partition(sel->selection, sel->all_parts, sel->all_cnt);
+	return 0;
+}
+
+static int
+cancel_clone(struct menudesc *m, void *arg)
+{	
+	struct part_selection_and_all_parts *sel = arg;
+
+	sel->cancelled = true;
+	return 1;
+}
+
+static void
+update_sel_part_title(struct part_selection_and_all_parts *sel)
+{
+	struct disk_part_info info;
+	char *buf, line[MENUSTRSIZE];
+	size_t buf_len, i;
+
+	buf_len = MENUSTRSIZE * (1+sel->selection->num_sel);
+	buf = malloc(buf_len);
+	if (buf == NULL)
+		return;
+
+	strcpy(buf, msg_string(MSG_select_source_hdr));
+	for (i = 0; i < sel->selection->num_sel; i++) {
+		struct selected_partition *s =
+		    &sel->selection->selection[i];
+		if (!s->parts->pscheme->get_part_info(s->parts, s->id, &info))
+			continue;
+		daddr_t start = info.start / sizemult;
+		daddr_t size = info.size / sizemult;
+		sprintf(line, "\n  %s [%" PRIu64 " @ %" PRIu64 "] ",
+		    s->parts->disk, size, start);
+		if (info.nat_type != NULL)
+			strlcat(line, info.nat_type->description, sizeof(line));
+		strlcat(buf, line, buf_len);
+	}
+	free(sel->title);
+	sel->title = buf;
+}
+
+static void
+post_sel_part(struct menudesc *m, void *arg)
+{
+	struct part_selection_and_all_parts *sel = arg;
+
+	if (m->mw == NULL)
+		return;
+	update_sel_part_title(sel);
+	m->title = sel->title;
+	m->h = 0;
+	resize_menu_height(m);
+}
+
+static void
+fmt_sel_part_line(struct menudesc *m, int i, void *arg)
+{
+	struct part_selection_and_all_parts *sel = arg;
+
+	wprintw(m->mw, "%s: %s", msg_string(MSG_clone_with_data),
+	    sel->selection->with_data ?
+		msg_string(MSG_Yes) :
+		 msg_string(MSG_No));
+}
+
+bool
+select_partitions(struct selected_partitions *res,
+    const struct disk_partitions *ignore)
+{
+	struct disk_desc disks[MAX_DISKS];
+	struct disk_partitions *ps;
+	struct part_selection_and_all_parts data;
+	struct pm_devs *i;
+	size_t j;
+	int cnt, n, m;
+	static menu_ent men[] = {
+		{ .opt_name = MSG_select_source_add,
+		  .opt_action = add_another },
+		{ .opt_action = toggle_clone_data },
+		{ .opt_name = MSG_cancel, .opt_action = cancel_clone },
+	};
+
+	memset(res, 0, sizeof *res);
+	memset(&data, 0, sizeof data);
+	data.selection = res;
+
+	/*
+	 * collect all available partition sets
+	 */
+	data.all_cnt = 0;
+	if (SLIST_EMPTY(&pm_head)) {
+		cnt = get_disks(disks, false);
+		if (cnt <= 0)
+			return false;
+
+		/*
+		 * allocate two slots for each disk (primary/secondary)
+		 */
+		data.all_parts = calloc(2*cnt, sizeof *data.all_parts);
+		if (data.all_parts == NULL)
+			return false;
+
+		for (n = 0; n < cnt; n++) {
+			if (ignore != NULL &&
+			    strcmp(disks[n].dd_name, ignore->disk) == 0)
+				continue;
+
+			ps = partitions_read_disk(disks[n].dd_name,
+			    disks[n].dd_totsec, disks[n].dd_no_mbr);
+			if (ps == NULL)
+				continue;
+			data.all_parts[data.all_cnt++] = ps;
+			ps = get_inner_parts(ps);
+			if (ps == NULL)
+				continue;
+			data.all_parts[data.all_cnt++] = ps;
+		}
+		if (data.all_cnt > 0)
+			res->free_parts = true;
+	} else {
+		cnt = 0;
+		SLIST_FOREACH(i, &pm_head, l)
+			cnt++;
+
+		data.all_parts = calloc(cnt, sizeof *data.all_parts);
+		if (data.all_parts == NULL)
+			return false;
+
+		SLIST_FOREACH(i, &pm_head, l) {
+			if (i->parts == NULL)
+				continue;
+			if (i->parts == ignore)
+				continue;
+			data.all_parts[data.all_cnt++] = i->parts;
+		}
+	}
+
+	if (!add_select_partition(res, data.all_parts, data.all_cnt))
+		goto fail;
+
+	/* loop with menu */
+	update_sel_part_title(&data);
+	m = new_menu(data.title, men, __arraycount(men), 3, 2, 0, 65, MC_SCROLL,
+	    post_sel_part, fmt_sel_part_line, NULL, NULL,
+	    "Source selection OK, proceed to target selection");
+	process_menu(m, &data);
+	free(data.title);
+	if (res->num_sel == 0)
+		goto fail;
+
+	/* cleanup */
+	if (res->free_parts) {
+		for (j = 0; j < data.all_cnt; j++) {
+			if (selection_has_parts(res, data.all_parts[j]))
+				continue;
+			if (data.all_parts[j]->parent != NULL)
+				continue;
+			data.all_parts[j]->pscheme->free(data.all_parts[j]);
+		}
+	}
+	free(data.all_parts);
+	return true;
+
+fail:
+	if (res->free_parts) {
+		for (j = 0; j < data.all_cnt; j++) {
+			if (data.all_parts[j]->parent != NULL)
+				continue;
+			data.all_parts[j]->pscheme->free(data.all_parts[j]);
+		}
+	}
+	free(data.all_parts);
+	return false;
+}
+
+void
+free_selected_partitions(struct selected_partitions *selected)
+{
+	size_t i;
+	struct disk_partitions *parts;
+
+	if (!selected->free_parts)
+		return;
+
+	for (i = 0; i < selected->num_sel; i++) {
+		parts = selected->selection[i].parts;
+
+		/* remove from list before testing for other instances */
+		selected->selection[i].parts = NULL;
+
+		/* if this is the secondary partion set, the parent owns it */
+		if (parts->parent != NULL)
+			continue;
+
+		/* only free once (we use the last one) */
+		if (selection_has_parts(selected, parts))
+			continue;
+		parts->pscheme->free(parts);
+	}
+	free(selected->selection);
+}
+
+daddr_t
+selected_parts_size(struct selected_partitions *selected)
+{
+	struct disk_part_info info;
+	size_t i;
+	daddr_t s = 0;
+
+	for (i = 0; i < selected->num_sel; i++) {
+		if (!selected->selection[i].parts->pscheme->get_part_info(
+		    selected->selection[i].parts,
+		    selected->selection[i].id, &info))
+			continue;
+		s += info.size;
+	}
+
+	return s;
+}
+
+int
+clone_target_select(menudesc *m, void *arg)
+{
+	struct clone_target_menu_data *data = arg;
+
+	data->res = m->cursel;
+	return 1;
+}
+
+bool
+clone_partition_data(struct disk_partitions *dest_parts, part_id did,
+    struct disk_partitions *src_parts, part_id sid)
+{
+	char src_dev[MAXPATHLEN], target_dev[MAXPATHLEN];
+
+	if (!src_parts->pscheme->get_part_device(
+	    src_parts, sid, src_dev, sizeof src_dev, NULL,
+	    raw_dev_name, true))
+		return false;
+	if (!dest_parts->pscheme->get_part_device(
+	    dest_parts, did, target_dev, sizeof target_dev, NULL,
+	    raw_dev_name, true))
+		return false;
+
+	return run_program(RUN_DISPLAY | RUN_PROGRESS, 
+	    "progress -f %s -b 1m dd bs=1m of=%s",
+	    src_dev, target_dev) == 0;
+}

Index: src/usr.sbin/sysinst/gpt.c
diff -u src/usr.sbin/sysinst/gpt.c:1.11 src/usr.sbin/sysinst/gpt.c:1.12
--- src/usr.sbin/sysinst/gpt.c:1.11	Mon Aug 26 12:14:06 2019
+++ src/usr.sbin/sysinst/gpt.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: gpt.c,v 1.11 2019/08/26 12:14:06 martin Exp $	*/
+/*	$NetBSD: gpt.c,v 1.12 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 2018 The NetBSD Foundation, Inc.
@@ -32,10 +32,12 @@
 #include "md.h"
 #include "gpt_uuid.h"
 #include <assert.h>
+#include <err.h>
 #include <paths.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <util.h>
+#include <uuid.h>
 
 bool	gpt_parts_check(void);	/* check for needed binaries */
 
@@ -44,7 +46,8 @@ bool	gpt_parts_check(void);	/* check for
 /* a GPT based disk_partitions interface */
 
 #define GUID_STR_LEN	40
-#define	GPT_PTYPE_MAX	32	/* should be >  gpt type -l | wc -l */
+#define	GPT_PTYPE_ALLOC	32	/* initial type array allocation, should be >
+				 * gpt type -l | wc -l */
 #define	GPT_DEV_LEN	16	/* dkNN */
 
 #define	GPT_PARTS_PER_SEC	4	/* a 512 byte sector hols 4 entries */
@@ -112,8 +115,11 @@ struct {
 	{ .name = "vmresered",	.fstype = FS_VMWRESV,	.ptype = PT_unknown }
 };
 
-static size_t gpt_ptype_cnt;
-static struct gpt_ptype_desc gpt_ptype_descs[GPT_PTYPE_MAX];
+static size_t gpt_ptype_cnt = 0, gpt_ptype_alloc = 0;
+static struct gpt_ptype_desc *gpt_ptype_descs = NULL;
+
+/* "well" known types with special handling */
+static const struct part_type_desc *gpt_native_root;
 
 /* similar to struct gpt_ent, but matching our needs */
 struct gpt_part_entry {
@@ -364,7 +370,7 @@ gpt_read_from_disk(const char *dev, dadd
 	}
 
 	parts->dp.pscheme = scheme;
-	parts->dp.disk = dev;
+	parts->dp.disk = strdup(dev);
 	parts->dp.disk_start = start;
 	parts->dp.disk_size = disk_size;
 	parts->dp.free_space = avail_size;
@@ -449,7 +455,7 @@ gpt_create_new(const char *disk, daddr_t
 		return NULL;
 
 	parts->dp.pscheme = &gpt_parts;
-	parts->dp.disk = disk;
+	parts->dp.disk = strdup(disk);
 
 	gpt_md_init(is_boot_drive, &parts->max_num_parts, &parts->prologue,
 	    &parts->epilogue);
@@ -709,26 +715,6 @@ gpt_get_free_spaces(const struct disk_pa
 	    max_num_result, min_space_size, align, start, ignore);
 }
 
-
-static bool
-gpt_adapt(const struct disk_partitions *arg,
-    const struct disk_part_info *src, struct disk_part_info *dest)
-{
-	/* slightly simplistic, enhance when needed */
-	memcpy(dest, src, sizeof(*dest));
-
-	if (src->nat_type == NULL)
-		return false;
-
-	dest->nat_type = arg->pscheme->get_generic_part_type(
-	    src->nat_type->generic_ptype);
-	if (dest->nat_type == NULL)
-		dest->nat_type = arg->pscheme->get_generic_part_type(
-		    PT_unknown);
-
-	return true;
-}
-
 static void
 gpt_match_ptype(const char *name, struct gpt_ptype_desc *t)
 {
@@ -739,6 +725,11 @@ gpt_match_ptype(const char *name, struct
 			t->gent.generic_ptype = gpt_fs_types[i].ptype;
 			t->fsflags = gpt_fs_types[i].fsflags;
 			t->default_fs_type = gpt_fs_types[i].fstype;
+
+			/* recongnize special entries */
+			if (gpt_native_root == NULL && i == 0)
+				gpt_native_root = &t->gent;
+
 			return;
 		}
 	}
@@ -751,10 +742,20 @@ gpt_match_ptype(const char *name, struct
 static void
 gpt_internal_add_ptype(const char *uid, const char *name, const char *desc)
 {
+	if (gpt_ptype_cnt >= gpt_ptype_alloc) {
+		gpt_ptype_alloc = gpt_ptype_alloc ? 2*gpt_ptype_alloc
+		    : GPT_PTYPE_ALLOC;
+		struct gpt_ptype_desc *nptypes = realloc(gpt_ptype_descs,
+		    gpt_ptype_alloc*sizeof(*gpt_ptype_descs));
+		if (nptypes == 0)
+			errx(EXIT_FAILURE, "out of memory");
+		gpt_ptype_descs = nptypes;
+	}
+
 	strlcpy(gpt_ptype_descs[gpt_ptype_cnt].tid, uid,
 	    sizeof(gpt_ptype_descs[gpt_ptype_cnt].tid));
-	gpt_ptype_descs[gpt_ptype_cnt].gent.short_desc = name;
-	gpt_ptype_descs[gpt_ptype_cnt].gent.description = desc;
+	gpt_ptype_descs[gpt_ptype_cnt].gent.short_desc = strdup(name);
+	gpt_ptype_descs[gpt_ptype_cnt].gent.description = strdup(desc);
 	gpt_match_ptype(name, &gpt_ptype_descs[gpt_ptype_cnt]);
 	gpt_ptype_cnt++;
 }
@@ -766,6 +767,19 @@ gpt_init_ptypes(void)
 		gpt_uuid_query(gpt_internal_add_ptype);
 }
 
+static void
+gpt_cleanup(void)
+{
+	/* free all of gpt_ptype_descs */
+	for (size_t i = 0; i < gpt_ptype_cnt; i++) {
+		free(__UNCONST(gpt_ptype_descs[i].gent.short_desc));
+		free(__UNCONST(gpt_ptype_descs[i].gent.description));
+	}
+	free(gpt_ptype_descs);
+	gpt_ptype_descs = NULL;
+	gpt_ptype_cnt = gpt_ptype_alloc = 0;
+}
+
 static size_t
 gpt_type_count(void)
 {
@@ -793,6 +807,11 @@ gpt_get_generic_type(enum part_type gent
 	if (gpt_ptype_cnt == 0)
 		gpt_init_ptypes();
 
+	if (gent == PT_root)
+		return gpt_native_root;
+	if (gent == PT_unknown)
+		return NULL;
+
 	for (size_t i = 0; i < gpt_ptype_cnt; i++)
 		if (gpt_ptype_descs[i].gent.generic_ptype == gent)
 			return &gpt_ptype_descs[i].gent;
@@ -863,7 +882,61 @@ gpt_get_fs_part_type(unsigned fstype, un
 		if (fstype == gpt_fs_types[i].fstype)
 			return gpt_find_type(gpt_fs_types[i].name);
 
-	return gpt_get_generic_type(PT_root);
+	return NULL;
+}
+
+static const struct part_type_desc *
+gpt_get_uuid_part_type(const uuid_t *id)
+{
+	char str[GUID_STR_LEN], desc[GUID_STR_LEN + MENUSTRSIZE];
+	const struct gpt_ptype_desc *t;
+	char *guid = NULL;
+	uint32_t err;
+
+	uuid_to_string(id, &guid, &err);
+	strlcpy(str, err == uuid_s_ok ? guid : "-", sizeof str);
+	free(guid);
+
+	t = gpt_find_guid_type(str);
+	if (t == NULL) {
+		snprintf(desc, sizeof desc, "%s (%s)",
+		    msg_string(MSG_custom_type), str);
+		gpt_internal_add_ptype(str, str, desc);
+		t = gpt_find_guid_type(str);
+		assert(t != NULL);
+	}
+	return &t->gent;
+}
+
+static const struct part_type_desc *
+gpt_create_custom_part_type(const char *custom, const char **err_msg)
+{
+	uuid_t id;
+	uint32_t err;
+
+	uuid_from_string(custom, &id, &err);
+	if (err_msg != NULL &&
+	   (err == uuid_s_invalid_string_uuid || err == uuid_s_bad_version)) {
+		*err_msg = MSG_invalid_guid;
+		return NULL;
+	}
+	if (err != uuid_s_ok)
+		return NULL;
+
+	return gpt_get_uuid_part_type(&id);
+}
+
+static const struct part_type_desc *
+gpt_create_unknown_part_type(void)
+{
+	uuid_t id;
+	uint32_t err;
+
+	uuid_create(&id, &err);
+	if (err != uuid_s_ok)
+		return NULL;
+
+	return gpt_get_uuid_part_type(&id);
 }
 
 static daddr_t
@@ -1312,9 +1385,11 @@ gpt_write_to_disk(struct disk_partitions
 	close(fd);
 
 	/*
-	 * Collect first root and efi partition (if available)
+	 * Collect first root and efi partition (if available), clear
+	 * "have wedge" flags.
 	 */
 	for (pno = 0, p = parts->partitions; p != NULL; p = p->gp_next, pno++) {
+		p->gp_flags &= ~GPEF_WEDGE;
 		if (root_id == NO_PART && p->gp_type != NULL) {
 			if (p->gp_type->gent.generic_ptype == PT_root &&
 			    p->gp_start == pm->ptstart) {
@@ -1455,6 +1530,7 @@ gpt_free(struct disk_partitions *arg)
 		n = p->gp_next;
 		free(p);
 	}
+	free(__UNCONST(parts->dp.disk));
 	free(parts);
 }
 
@@ -1657,6 +1733,8 @@ gpt_parts = {
 	.get_part_type = gpt_get_ptype,
 	.get_generic_part_type = gpt_get_generic_type,
 	.get_fs_part_type = gpt_get_fs_part_type,
+	.create_custom_part_type = gpt_create_custom_part_type,
+	.create_unknown_part_type = gpt_create_unknown_part_type,
 	.get_part_alignment = gpt_get_part_alignment,
 	.read_from_disk = gpt_read_from_disk,
 	.create_new_for_disk = gpt_create_new,
@@ -1671,7 +1749,7 @@ gpt_parts = {
 	.get_part_device = gpt_get_part_device,
 	.max_free_space_at = gpt_max_free_space_at,
 	.get_free_spaces = gpt_get_free_spaces,
-	.adapt_foreign_part_info = gpt_adapt,
+	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
 	.get_part_info = gpt_get_part_info,
 	.get_part_attr_str = gpt_get_part_attr_str,
 	.set_part_info = gpt_set_part_info,
@@ -1680,4 +1758,5 @@ gpt_parts = {
 	.delete_partition = gpt_delete_partition,
 	.write_to_disk = gpt_write_to_disk,
 	.free = gpt_free,
+	.cleanup = gpt_cleanup,
 };
Index: src/usr.sbin/sysinst/install.c
diff -u src/usr.sbin/sysinst/install.c:1.11 src/usr.sbin/sysinst/install.c:1.12
--- src/usr.sbin/sysinst/install.c:1.11	Sat Aug 17 18:08:06 2019
+++ src/usr.sbin/sysinst/install.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: install.c,v 1.11 2019/08/17 18:08:06 martin Exp $	*/
+/*	$NetBSD: install.c,v 1.12 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -34,6 +34,7 @@
 
 /* install.c -- system installation. */
 
+#include <sys/param.h>
 #include <stdio.h>
 #include <curses.h>
 #include "defs.h"
@@ -49,6 +50,7 @@ static bool
 write_all_parts(struct install_partition_desc *install)
 {
 	struct disk_partitions **allparts, *parts;
+	struct selected_partition *src;
 	size_t num_parts, i, j;
 	bool found, res;
 
@@ -75,7 +77,7 @@ write_all_parts(struct install_partition
 		allparts[num_parts++] = parts;
 	}
 
-	/* do four phases, abort anytime and go out, returning res */
+	/* do multiple phases, abort anytime and go out, returning res */
 
 	res = true;
 
@@ -98,7 +100,20 @@ write_all_parts(struct install_partition
 	/* phase 3: now we may have a first chance to enable swap space */
 	set_swap_if_low_ram(install);
 
-	/* phase 4: post disklabel (used for updating boot loaders) */
+	/* phase 4: copy any cloned partitions data (if requested) */
+	for (i = 0; i < install->num; i++) {
+		if ((install->infos[i].flags & PUIFLG_CLONE_PARTS) == 0
+		    || install->infos[i].clone_src == NULL
+		    || !install->infos[i].clone_src->with_data)
+			continue;
+		src = &install->infos[i].clone_src
+		    ->selection[install->infos[i].clone_ndx];
+		clone_partition_data(install->infos[i].parts,
+		    install->infos[i].cur_part_id,
+		    src->parts, src->id);
+	}
+
+	/* phase 5: post disklabel (used for updating boot loaders) */
 	for (i = 0; i < num_parts; i++) {
 		if (!md_post_disklabel(install, allparts[i])) {
 			res = false;
@@ -217,5 +232,5 @@ do_install(void)
 	hit_enter_to_continue(MSG_instcomplete, NULL);
 
 error:
-	free(install.infos);
+	free_install_desc(&install);
 }

Index: src/usr.sbin/sysinst/label.c
diff -u src/usr.sbin/sysinst/label.c:1.12 src/usr.sbin/sysinst/label.c:1.13
--- src/usr.sbin/sysinst/label.c:1.12	Sun Aug  4 10:29:41 2019
+++ src/usr.sbin/sysinst/label.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: label.c,v 1.12 2019/08/04 10:29:41 martin Exp $	*/
+/*	$NetBSD: label.c,v 1.13 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Jonathan Stone
@@ -36,7 +36,7 @@
 
 #include <sys/cdefs.h>
 #if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: label.c,v 1.12 2019/08/04 10:29:41 martin Exp $");
+__RCSID("$NetBSD: label.c,v 1.13 2019/11/12 16:33:14 martin Exp $");
 #endif
 
 #include <sys/types.h>
@@ -1113,11 +1113,16 @@ fmt_fspart_header(menudesc *menu, void *
 {
 	struct partition_usage_set *pset = arg;
 	char total[6], free_space[6], scol[13], ecol[13], szcol[13],
-	    sepline[MENUSTRSIZE], *p;
+	    sepline[MENUSTRSIZE], *p, desc[MENUSTRSIZE];
 	const char *fstype, *flags;
 	int i;
-	bool with_inst_flag = pset->parts->parent == NULL;
+	size_t ptn;
+	bool with_clone, with_inst_flag = pset->parts->parent == NULL;
 
+	with_clone = false;
+	for (ptn = 0; ptn < pset->num && !with_clone; ptn++)
+		if (pset->infos[ptn].flags & PUIFLG_CLONE_PARTS)
+			with_clone = true;
 	humanize_number(total, sizeof total,
 	    pset->parts->disk_size * 512,
 	    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
@@ -1125,14 +1130,19 @@ fmt_fspart_header(menudesc *menu, void *
 	    pset->cur_free_space * 512,
 	    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
 
+	if (with_clone)
+		strlcpy(desc, msg_string(MSG_clone_flag_desc), sizeof desc);
+	else
+		desc[0] = 0;
+	if (pset->parts->pscheme->part_flag_desc)
+		strlcat(desc, msg_string(pset->parts->pscheme->part_flag_desc),
+		sizeof desc);
+
 	msg_display_subst(MSG_fspart, 7, pset->parts->disk,
 	    msg_string(pset->parts->pscheme->name),
 	    msg_string(pset->parts->pscheme->short_name),
 	    with_inst_flag ? msg_string(MSG_ptn_instflag_desc) : "",
-	    pset->parts->pscheme->part_flag_desc ?
-	        msg_string(pset->parts->pscheme->part_flag_desc)
-		: "",
-	    total, free_space);
+	    desc, total, free_space);
 
 	snprintf(scol, sizeof scol, "%s (%s)",
 	    msg_string(MSG_ptnheaders_start), multname);
@@ -1177,11 +1187,40 @@ fmt_fspart_row(menudesc *m, int ptn, voi
 	static const char *Yes;
 	char flag_str[MENUSTRSIZE], *fp;
 	unsigned inst_flags;
+	size_t clone_cnt;
 	bool with_inst_flag = pset->parts->parent == NULL;
 
 	if (Yes == NULL)
 		Yes = msg_string(MSG_Yes);
 
+	if ((pset->infos[ptn].flags & PUIFLG_CLONE_PARTS) &&
+	   pset->infos[ptn].cur_part_id == NO_PART) {
+		psize = pset->infos[ptn].size / sizemult;
+		if (pset->infos[ptn].clone_ndx <
+		    pset->infos[ptn].clone_src->num_sel)
+			clone_cnt = 1;
+		else
+			clone_cnt = pset->infos[ptn].clone_src->num_sel;
+		if (pset->infos[ptn].cur_part_id == NO_PART)
+			wprintw(m->mw, "                          %12" PRIu64
+			    " [%zu %s]", psize, clone_cnt,
+			    msg_string(MSG_clone_target_disp));
+		else {
+			poffset = pset->infos[ptn].cur_start / sizemult;
+			pend = (pset->infos[ptn].cur_start +
+			    pset->infos[ptn].size) / sizemult - 1;
+			wprintw(m->mw, "%12" PRIu64 " %12" PRIu64 " %12" PRIu64
+			    " [%zu %s]",
+			    poffset, pend, psize, clone_cnt,
+			    msg_string(MSG_clone_target_disp));
+		}
+		if (m->title == fspart_title)
+			m->opts[ptn].opt_flags |= OPT_IGNORE;
+		else
+			m->opts[ptn].opt_flags &= ~OPT_IGNORE;
+		return;
+	}
+
 	if (!real_partition(pset, ptn))
 		return;
 
@@ -1189,11 +1228,22 @@ fmt_fspart_row(menudesc *m, int ptn, voi
 	    pset->infos[ptn].cur_part_id, &info))
 		return;
 
-	/* enable / disable this line if it is something like RAW_PART */
-	if (info.flags & (PTI_WHOLE_DISK|PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
-		m->opts[ptn].opt_flags |= OPT_IGNORE;
-	else
-		m->opts[ptn].opt_flags &= ~OPT_IGNORE;
+	/*
+	 * We use this function in multiple menus, but only want it
+	 * to play with enable/disable in a single one:
+	 */
+	if (m->title == fspart_title) {
+		/*
+		 * Enable / disable this line if it is something
+		 * like RAW_PART
+		 */
+		if ((info.flags &
+		    (PTI_WHOLE_DISK|PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
+		    || (pset->infos[ptn].flags & PUIFLG_CLONE_PARTS))
+			m->opts[ptn].opt_flags |= OPT_IGNORE;
+		else
+			m->opts[ptn].opt_flags &= ~OPT_IGNORE;
+	}
 
 	poffset = info.start / sizemult;
 	psize = info.size / sizemult;
@@ -1223,6 +1273,8 @@ fmt_fspart_row(menudesc *m, int ptn, voi
 	}
 	if (inst_flags & PUIINST_NEWFS)
 		*fp++ = msg_string(MSG_newfs_flag)[0];
+	if (pset->infos[ptn].flags & PUIFLG_CLONE_PARTS)
+		*fp++ = msg_string(MSG_clone_flag)[0];
 	*fp = 0;
 	if (pset->parts->pscheme->get_part_attr_str != NULL)
 		pset->parts->pscheme->get_part_attr_str(pset->parts,
@@ -1245,6 +1297,153 @@ fmt_fspart_row(menudesc *m, int ptn, voi
 }
 
 static int
+part_ext_clone(menudesc *m, void *arg)
+{
+	struct selected_partitions selected, *clone_src;
+	struct clone_target_menu_data data;
+	struct partition_usage_set *pset = arg;
+	struct part_usage_info *p;
+	struct disk_part_info sinfo, cinfo;
+	struct disk_partitions *csrc;
+	struct disk_part_free_space space;
+	menu_ent *men;
+	daddr_t clone_size, free_size, offset, align;
+	int num_men, i;
+	size_t s, clone_cnt;
+	part_id cid;
+	struct clone_data {
+		struct disk_part_info info;
+		part_id new_id;
+		size_t ndx;
+	};
+	struct clone_data *clones = NULL;
+
+	if (!select_partitions(&selected, pm->parts))
+		return 0;
+
+	clone_size = selected_parts_size(&selected);
+	num_men = pset->num+1;
+	men = calloc(num_men, sizeof *men);
+	if (men == NULL)
+		return 0;
+	for (i = 0; i < num_men; i++) {
+		men[i].opt_action = clone_target_select;
+		if (i == 0)
+			free_size = pset->infos[i].cur_start;
+		else if (i > 0 && (size_t)i < pset->num)
+			free_size = pset->infos[i].cur_start -
+			    pset->infos[i-1].cur_start - pset->infos[i-1].size;
+		else
+			free_size = pset->parts->free_space;
+		if (free_size < clone_size)
+			men[i].opt_flags = OPT_IGNORE;
+	}
+	men[num_men-1].opt_name = MSG_clone_target_end;
+
+	memset(&data, 0, sizeof data);
+	data.usage = *pset;
+	data.res = -1;
+
+	data.usage.menu = new_menu(MSG_clone_target_hdr,
+	    men, num_men, 3, 2, 0, 65, MC_SCROLL,
+	    NULL, fmt_fspart_row, NULL, NULL, MSG_cancel);
+	process_menu(data.usage.menu, &data);
+	free_menu(data.usage.menu);
+	free(men);
+
+	if (data.res < 0)
+		goto err;
+
+	/* create temporary infos for all clones that work out */
+	clone_cnt = 0;
+	clones = calloc(selected.num_sel, sizeof(*clones));
+	if (clones == NULL)
+		goto err;
+
+	clone_src = malloc(sizeof(selected));
+	if (clone_src == NULL)
+		goto err;
+	*clone_src = selected; 
+
+	/* find selected offset from data.res and insert clones there */
+	align = pset->parts->pscheme->get_part_alignment(pset->parts);
+	offset = -1;
+	if (data.res > 0)
+		offset = pset->infos[data.res-1].cur_start
+		    + pset->infos[data.res-1].size;
+	else
+		offset = 0;
+	for (s = 0; s < selected.num_sel; s++) {
+		csrc = selected.selection[s].parts;
+		cid = selected.selection[s].id;
+		csrc->pscheme->get_part_info(csrc, cid, &sinfo);
+		if (!pset->parts->pscheme->adapt_foreign_part_info(
+		    pset->parts, &cinfo, csrc->pscheme, &sinfo))
+			continue;
+		size_t cnt = pset->parts->pscheme->get_free_spaces(
+		    pset->parts, &space, 1, cinfo.size-align, align,
+		    offset, -1);
+		if (cnt == 0)
+			continue;
+		cinfo.start = space.start;
+		cid = pset->parts->pscheme->add_partition(
+		    pset->parts, &cinfo, NULL);
+		if (cid == NO_PART)
+			continue;
+		pset->parts->pscheme->get_part_info(pset->parts, cid, &cinfo);
+		clones[clone_cnt].info = cinfo;
+		clones[clone_cnt].new_id = cid;
+		clones[clone_cnt].ndx = s;
+		clone_cnt++;
+		offset = rounddown(cinfo.start+cinfo.size+align, align);
+	}
+
+	/* insert new clone records at offset data.res */
+	men = realloc(m->opts, (m->numopts+clone_cnt)*sizeof(*m->opts));
+	if (men == NULL)
+		goto err;
+	pset->menu_opts = men;
+	m->opts = men;
+	m->numopts += clone_cnt;
+
+	p = realloc(pset->infos, (pset->num+clone_cnt)*sizeof(*pset->infos));
+	if (p == NULL)
+		goto err;
+	pset->infos = p;
+
+	men += data.res;
+	p += data.res;
+	memmove(men+clone_cnt, men,
+	    sizeof(*men)*(m->numopts-data.res-clone_cnt));
+	if (pset->num > (size_t)data.res)
+		memmove(p+clone_cnt, p, sizeof(*p)*(pset->num-data.res));
+	memset(men, 0, sizeof(*men)*clone_cnt);
+	memset(p, 0, sizeof(*p)*clone_cnt);
+	for (s = 0; s < clone_cnt; s++) {
+		p[s].cur_part_id = clones[s].new_id;
+		p[s].cur_start = clones[s].info.start;
+		p[s].size = clones[s].info.size;
+		p[s].cur_flags = clones[s].info.flags;
+		p[s].flags = PUIFLG_CLONE_PARTS;
+		p[s].parts = pset->parts;
+		p[s].clone_src = clone_src;
+		p[s].clone_ndx = s;
+	}
+	free(clones);
+	m->cursel = ((size_t)data.res >= pset->num) ? 0 : data.res+clone_cnt;
+	pset->num += clone_cnt;
+	m->h = 0;
+	resize_menu_height(m);
+
+	return -1;
+
+err:
+	free(clones);
+	free_selected_partitions(&selected);
+	return 0;
+}
+
+static int
 edit_fspart_pack(menudesc *m, void *arg)
 {
 	struct partition_usage_set *pset = arg;
@@ -1396,7 +1595,7 @@ edit_and_check_label(struct pm_devs *p, 
 	    pset->parts->pscheme->set_disk_pack_name != NULL;
 
 	pset->menu_opts = calloc(pset->parts->num_part
-	     +3+may_add+may_edit_pack,
+	     +4+may_add+may_edit_pack,
 	     sizeof *pset->menu_opts);
 	if (pset->menu_opts == NULL)
 		return 0;
@@ -1432,6 +1631,11 @@ edit_and_check_label(struct pm_devs *p, 
 		op->opt_action = edit_fspart_pack;
 		op++;
 	}
+
+	/* add a clone-from-elsewhere option */
+	op->opt_name = MSG_clone_from_elsewhere;
+	op->opt_action = part_ext_clone;
+	op++;
 	        
 	/* and abort option */
 	op->opt_name = MSG_cancel;
@@ -1439,7 +1643,7 @@ edit_and_check_label(struct pm_devs *p, 
 	op->opt_action = edit_fspart_abort;
 	op++;
 	cnt = op - pset->menu_opts;
-	assert(cnt == pset->parts->num_part+3+may_add+may_edit_pack);
+	assert(cnt == pset->parts->num_part+4+may_add+may_edit_pack);
 
 	pset->menu = new_menu(fspart_title, pset->menu_opts, cnt,
 			0, -1, 0, 74,

Index: src/usr.sbin/sysinst/main.c
diff -u src/usr.sbin/sysinst/main.c:1.17 src/usr.sbin/sysinst/main.c:1.18
--- src/usr.sbin/sysinst/main.c:1.17	Sat Jun 22 20:46:07 2019
+++ src/usr.sbin/sysinst/main.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: main.c,v 1.17 2019/06/22 20:46:07 christos Exp $	*/
+/*	$NetBSD: main.c,v 1.18 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -281,6 +281,8 @@ main(int argc, char **argv)
 	pm_destroy_all();
 #endif
 
+	partitions_cleanup();
+
 	exit_cleanly = 1;
 	return 0;
 }
Index: src/usr.sbin/sysinst/msg.mi.es
diff -u src/usr.sbin/sysinst/msg.mi.es:1.17 src/usr.sbin/sysinst/msg.mi.es:1.18
--- src/usr.sbin/sysinst/msg.mi.es:1.17	Wed Oct  2 11:16:04 2019
+++ src/usr.sbin/sysinst/msg.mi.es	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg.mi.es,v 1.17 2019/10/02 11:16:04 maya Exp $	*/
+/*	$NetBSD: msg.mi.es,v 1.18 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -1284,6 +1284,7 @@ message dl_type_invalid	{Invalid file sy
 message	cancel		{Cancel}
 
 message	out_of_range	{Invalid value}
+message	invalid_guid	{Invalid GUID}
 
 message	reedit_partitions	{Re-edit}
 message abort_installation	{Abort installation}
@@ -1446,6 +1447,9 @@ message newfs_flag	{N}
 message ptn_install	{instalar}
 message ptn_instflag_desc	{(I)nstalar, }
 
+message clone_flag	{C}
+message clone_flag_desc	{, (C)lone}
+
 message parttype_gpt {Guid Partition Table (GPT)}
 message parttype_gpt_short {GPT}
 
@@ -1471,3 +1475,15 @@ message size_ptn_not_mounted		{(Other: $
 
 message running_system			{current system}
 
+message clone_from_elsewhere		{Clone external partition(s)}
+message select_foreign_part
+{Please select an external source partition:}
+message select_source_hdr
+{Your currently selected source partitions are:}
+message clone_with_data			{Clone with data}
+message	select_source_add		{Add another partition}
+message clone_target_end		{Add at end}
+message clone_target_hdr
+{Insert cloned partitions before:}
+message clone_target_disp		{cloned partition(s)}
+

Index: src/usr.sbin/sysinst/mbr.c
diff -u src/usr.sbin/sysinst/mbr.c:1.21 src/usr.sbin/sysinst/mbr.c:1.22
--- src/usr.sbin/sysinst/mbr.c:1.21	Tue Aug 27 17:23:24 2019
+++ src/usr.sbin/sysinst/mbr.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: mbr.c,v 1.21 2019/08/27 17:23:24 martin Exp $ */
+/*	$NetBSD: mbr.c,v 1.22 2019/11/12 16:33:14 martin Exp $ */
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -74,6 +74,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <util.h>
+#include <paths.h>
+#include <sys/ioctl.h>
 #include "defs.h"
 #include "mbr.h"
 #include "md.h"
@@ -89,6 +91,9 @@
 #define MAXSECTOR	63
 
 
+#define	MBR_UNKNOWN_PTYPE	94	/* arbitrary not widely used value */
+
+
 /* A list of predefined partition types */
 const struct {
 	unsigned int ptype;
@@ -588,7 +593,7 @@ static int
 write_mbr(const char *disk, mbr_info_t *mbri, int bsec, int bhead, int bcyl)
 {
 	char diskpath[MAXPATHLEN];
-	int fd, i, ret = 0;
+	int fd, i, ret = 0, bits = 0;
 	struct mbr_partition *mbrp;
 	u_int32_t pstart, psize;
 #ifdef BOOTSEL
@@ -605,6 +610,10 @@ write_mbr(const char *disk, mbr_info_t *
 	if (fd < 0)
 		return -1;
 
+	/* Remove all wedges */
+	if (ioctl(fd, DIOCRMWEDGES, &bits) == -1)
+		return -1;
+
 #ifdef BOOTSEL
 	/*
 	 * If the main boot code (appears to) contain the netbsd bootcode,
@@ -646,6 +655,7 @@ write_mbr(const char *disk, mbr_info_t *
 #endif
 
 	for (ext = mbri; ext != NULL; ext = ext->extended) {
+		memset(mbri->wedge, 0, sizeof mbri->wedge);
 		sector = ext->sector;
 		mbrsec = ext->mbr;	/* copy sector */
 		mbrp = &mbrsec.mbr_parts[0];
@@ -830,7 +840,7 @@ mbr_create_new(const char *disk, daddr_t
 		return NULL;
 
 	parts->dp.pscheme = &mbr_parts;
-	parts->dp.disk = disk;
+	parts->dp.disk = strdup(disk);
 	if (len > mbr_parts.size_limit)
 		len = mbr_parts.size_limit;
 	parts->dp.disk_start = start;
@@ -894,7 +904,7 @@ mbr_read_from_disk(const char *disk, dad
 		return NULL;
 
 	parts->dp.pscheme = scheme;
-	parts->dp.disk = disk;
+	parts->dp.disk = strdup(disk);
 	if (len >= mbr_parts.size_limit)
 		len = mbr_parts.size_limit;
 	parts->dp.disk_start = start;
@@ -1127,6 +1137,16 @@ mbr_custom_part_type(const char *custom,
 }
 
 static const struct part_type_desc *
+mbr_create_unknown_part_type(void)
+{
+
+	if (mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen.short_desc != NULL)
+		return &mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen;
+
+	return mbr_new_custom_part_type(MBR_UNKNOWN_PTYPE);
+}
+
+static const struct part_type_desc *
 mbr_get_gen_type_desc(unsigned int pt)
 {
 
@@ -1231,12 +1251,80 @@ mbr_do_get_part_info(const struct disk_p
 	mbr_partition_to_info(mp, mb->sector, info);
 	if (mb->last_mounted[i] != NULL && mb->last_mounted[i][0] != 0)
 		info->last_mounted = mb->last_mounted[i];
-	info->fs_type = mb->fs_type[i];
-	info->fs_sub_type = mb->fs_sub_type[i];
+	if (mb->fs_type[i] != FS_UNUSED) {
+		info->fs_type = mb->fs_type[i];
+		info->fs_sub_type = mb->fs_sub_type[i];
+	} else {
+		info->fs_sub_type = 0;
+		switch (mp->mbrp_type) {
+		case MBR_PTYPE_FAT12:
+		case MBR_PTYPE_FAT16S:
+		case MBR_PTYPE_FAT16B:
+		case MBR_PTYPE_FAT32:
+		case MBR_PTYPE_FAT32L:
+		case MBR_PTYPE_FAT16L:
+		case MBR_PTYPE_OS2_DOS12:
+		case MBR_PTYPE_OS2_DOS16S:
+		case MBR_PTYPE_OS2_DOS16B:
+		case MBR_PTYPE_HID_FAT32:
+		case MBR_PTYPE_HID_FAT32_LBA:
+		case MBR_PTYPE_HID_FAT16_LBA:
+		case MBR_PTYPE_MDOS_FAT12:
+		case MBR_PTYPE_MDOS_FAT16S:
+		case MBR_PTYPE_MDOS_EXT:
+		case MBR_PTYPE_MDOS_FAT16B:
+		case MBR_PTYPE_SPEEDSTOR_16S:
+		case MBR_PTYPE_EFI:
+			info->fs_type = FS_MSDOS;
+			break;
+		case MBR_PTYPE_XENIX_ROOT:
+		case MBR_PTYPE_XENIX_USR:
+			info->fs_type = FS_SYSV;
+			break;
+		case MBR_PTYPE_NTFS:
+			info->fs_type = FS_NTFS;
+			break;
+		case MBR_PTYPE_APPLE_HFS:
+			info->fs_type = FS_HFS;
+			break;
+		case MBR_PTYPE_VMWARE:
+			info->fs_type = FS_VMFS;
+			break;
+		case MBR_PTYPE_AST_SWAP:
+		case MBR_PTYPE_DRDOS_LSWAP:
+		case MBR_PTYPE_LNXSWAP:
+		case MBR_PTYPE_BSDI_SWAP:
+		case MBR_PTYPE_HID_LNX_SWAP:
+		case MBR_PTYPE_VMWARE_SWAP:
+			info->fs_type = FS_SWAP;
+			break;
+		}
+	}
 	return true;
 }
 
 static bool
+get_wedge_devname(const struct disk_partitions *arg, part_id id,
+    const mbr_info_t *mb, int i, bool primary,   
+    const struct mbr_partition *mp, void *cookie)
+{
+	char **res = cookie;
+
+	if (!res)
+		return false;
+
+	*res = __UNCONST(mb->wedge[i]);
+	return true;
+}
+
+static bool
+mbr_part_get_wedge(const struct disk_partitions *arg, part_id id,
+    char **res)
+{
+	return mbr_part_apply(arg, id, get_wedge_devname, res);
+}
+
+static bool
 mbr_get_part_info(const struct disk_partitions *arg, part_id id,
     struct disk_part_info *info)
 {
@@ -2382,17 +2470,49 @@ mbr_can_add_partition(const struct disk_
 }
 
 static void
+mbr_free_wedge(int *fd, const char *disk, const char *wedge)
+{
+	struct dkwedge_info dkw;
+	char diskpath[MAXPATHLEN];
+
+	if (*fd == -1)
+		*fd = opendisk(disk, O_RDWR, diskpath,
+		    sizeof(diskpath), 0);
+	if (*fd != -1) {
+		memset(&dkw, 0, sizeof(dkw));
+		strlcpy(dkw.dkw_devname, wedge,
+		    sizeof(dkw.dkw_devname));
+		ioctl(*fd, DIOCDWEDGE, &dkw);
+	}
+}
+
+static void
 mbr_free(struct disk_partitions *arg)
 {
 	struct mbr_disk_partitions *parts = (struct mbr_disk_partitions*)arg;
+	mbr_info_t *m;
+	int i, fd;
 
 	assert(parts != NULL);
 
+	fd = -1;
+	m = &parts->mbr;
+	do {
+		for (i = 0; i < MBR_PART_COUNT; i++) {
+			if (m->wedge[i][0] != 0)
+				mbr_free_wedge(&fd, arg->disk, m->wedge[i]);
+		}
+	} while ((m = m->extended));
+
+	if (fd != -1)
+		close(fd);
+
 	if (parts->dlabel)
 		parts->dlabel->pscheme->free(parts->dlabel);
 
 	free_mbr_info(parts->mbr.extended);
 	free_last_mounted(&parts->mbr);
+	free(__UNCONST(parts->dp.disk));
 	free(parts);
 }
 
@@ -2677,6 +2797,103 @@ mbr_part_alignment(const struct disk_par
 }
 
 static bool
+add_wedge(const char *disk, daddr_t start, daddr_t size,
+    char *wname, size_t max_len)
+{
+	struct dkwedge_info dkw;
+	char diskpath[MAXPATHLEN];
+	int fd;
+
+	memset(&dkw, 0, sizeof(dkw));
+	dkw.dkw_offset = start;
+	dkw.dkw_size = size;
+	snprintf((char*)dkw.dkw_wname, sizeof dkw.dkw_wname,
+	    "%s_%" PRIi64 "@%" PRIi64, (const char*)disk, size, start);
+
+	*wname = 0;
+
+	fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0);
+	if (fd < 0)
+		return false;
+	if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) {
+		close(fd);
+		return false;
+	}
+	close(fd);
+	strlcpy(wname, dkw.dkw_devname, max_len);
+	return true;
+}
+
+static bool
+mbr_get_part_device(const struct disk_partitions *arg,
+    part_id ptn, char *devname, size_t max_devname_len, int *part,
+    enum dev_name_usage usage, bool with_path)
+{
+	const struct mbr_disk_partitions *parts =
+	    (const struct mbr_disk_partitions*)arg;
+	struct disk_part_info info, tmp;
+	part_id dptn;
+	char *wedge_dev;
+
+	if (!mbr_get_part_info(arg, ptn, &info))
+		return false;
+
+	if (!mbr_part_get_wedge(arg, ptn, &wedge_dev) || wedge_dev == NULL)
+		return false;
+
+	if (wedge_dev[0] == 0) {
+		/*
+		 * If we have secondary partitions, try to find a match there
+		 * and use that...
+		 */
+		if (parts->dlabel != NULL) {
+			for (dptn = 0; dptn < parts->dlabel->num_part; dptn++) {
+				if (!parts->dlabel->pscheme->get_part_info(
+				    parts->dlabel, dptn, &tmp))
+					continue;
+				if (tmp.start != info.start ||
+				    tmp.size != info.size)
+					continue;
+				return parts->dlabel->pscheme->get_part_device(
+				    parts->dlabel, dptn, devname,
+				     max_devname_len,
+				    part, usage, with_path);
+			}
+		}
+
+		/*
+		 * Configure a new wedge and remember the name
+		 */
+		if (!add_wedge(arg->disk, info.start, info.size, wedge_dev,
+		    MBR_DEV_LEN))
+			return false;
+	}
+
+	assert(wedge_dev[0] != 0);
+
+	switch (usage) {
+	case logical_name:
+	case plain_name:
+		if (with_path)
+			snprintf(devname, max_devname_len, _PATH_DEV "%s",
+			    wedge_dev);
+		else
+			strlcpy(devname, wedge_dev, max_devname_len);
+		return true;
+	case raw_dev_name:
+		if (with_path)
+			snprintf(devname, max_devname_len, _PATH_DEV "r%s",
+			    wedge_dev);
+		else
+			snprintf(devname, max_devname_len, "r%s",
+			    wedge_dev);
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool
 is_custom_attribute_writable(const struct disk_partitions *arg, part_id id,
     const mbr_info_t *mb, int i, bool primary,
     const struct mbr_partition *mp, void *cookie)
@@ -2766,16 +2983,19 @@ mbr_parts = {
 	.custom_attribute_toggle = mbr_custom_attribute_toggle,
 	.custom_attribute_set_str = mbr_custom_attribute_set_str,
 	.get_part_types_count = mbr_get_part_type_count,
+	.adapt_foreign_part_info = generic_adapt_foreign_part_info,
 	.get_part_type = mbr_get_part_type,
 	.get_fs_part_type = mbr_get_fs_part_type,
 	.get_generic_part_type = mbr_get_generic_part_type,
 	.create_custom_part_type = mbr_custom_part_type,
+	.create_unknown_part_type = mbr_create_unknown_part_type,
 	.secondary_partitions = mbr_read_disklabel,
 	.write_to_disk = mbr_write_to_disk,
 	.read_from_disk = mbr_read_from_disk,
 	.create_new_for_disk = mbr_create_new,
 	.guess_disk_geom = mbr_guess_geom,
 	.change_disk_geom = mbr_change_disk_geom,
+	.get_part_device = mbr_get_part_device,
 	.max_free_space_at = mbr_max_part_size,
 	.get_free_spaces = mbr_get_free_spaces,
 	.set_part_info = mbr_set_part_info,
Index: src/usr.sbin/sysinst/msg.mi.fr
diff -u src/usr.sbin/sysinst/msg.mi.fr:1.21 src/usr.sbin/sysinst/msg.mi.fr:1.22
--- src/usr.sbin/sysinst/msg.mi.fr:1.21	Thu Oct 17 08:54:50 2019
+++ src/usr.sbin/sysinst/msg.mi.fr	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg.mi.fr,v 1.21 2019/10/17 08:54:50 maxv Exp $	*/
+/*	$NetBSD: msg.mi.fr,v 1.22 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -1336,6 +1336,7 @@ message dl_type_invalid	{Invalid file sy
 message	cancel		{Annuler}
 
 message	out_of_range	{Invalid value}
+message	invalid_guid	{Invalid GUID}
 
 message	reedit_partitions	{Re-edit}
 message abort_installation	{Abort installation}
@@ -1490,6 +1491,8 @@ message newfs_flag	{N}
 message ptn_install	{installation}
 message ptn_instflag_desc	{(I)nstallation, }
 
+message clone_flag	{C}
+message clone_flag_desc	{, (C)lone}
 
 message parttype_gpt {Guid Partition Table (GPT)}
 message parttype_gpt_short {GPT}
@@ -1516,3 +1519,15 @@ message size_ptn_not_mounted		{(Other: $
 
 message running_system			{current system}
 
+message clone_from_elsewhere		{Clone external partition(s)}
+message select_foreign_part
+{Please select an external source partition:}
+message select_source_hdr
+{Your currently selected source partitions are:}
+message clone_with_data			{Clone with data}
+message	select_source_add		{Add another partition}
+message clone_target_end		{Add at end}
+message clone_target_hdr
+{Insert cloned partitions before:}
+message clone_target_disp		{cloned partition(s)}
+

Index: src/usr.sbin/sysinst/mbr.h
diff -u src/usr.sbin/sysinst/mbr.h:1.3 src/usr.sbin/sysinst/mbr.h:1.4
--- src/usr.sbin/sysinst/mbr.h:1.3	Wed Jun 19 17:32:31 2019
+++ src/usr.sbin/sysinst/mbr.h	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: mbr.h,v 1.3 2019/06/19 17:32:31 martin Exp $	*/
+/*	$NetBSD: mbr.h,v 1.4 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997, 1988 Piermont Information Systems Inc.
@@ -54,6 +54,8 @@
 #define MBR_PUT_LSCYL(c)		((c) & 0xff)
 #define MBR_PUT_MSCYLANDSEC(c,s)	(((s) & 0x3f) | (((c) >> 2) & 0xc0))
 
+#define MBR_DEV_LEN	16		/* for wedge names */
+
 typedef struct mbr_info_t mbr_info_t;
 struct mbr_info_t {
 	struct mbr_sector	mbr;
@@ -69,6 +71,8 @@ struct mbr_info_t {
 	/* only in first item... */
 	uint		bootsec;	/* start sector of bootmenu default */
 #endif
+	/* for temporary access */
+	char		wedge[MBR_PART_COUNT][MBR_DEV_LEN];
 };
 
 #ifdef BOOTSEL

Index: src/usr.sbin/sysinst/msg.mi.de
diff -u src/usr.sbin/sysinst/msg.mi.de:1.16 src/usr.sbin/sysinst/msg.mi.de:1.17
--- src/usr.sbin/sysinst/msg.mi.de:1.16	Wed Oct  2 11:16:04 2019
+++ src/usr.sbin/sysinst/msg.mi.de	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg.mi.de,v 1.16 2019/10/02 11:16:04 maya Exp $	*/
+/*	$NetBSD: msg.mi.de,v 1.17 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -318,7 +318,7 @@ message fspart
 {Nachfolgend sehen Sie Ihre $2 Partitionen für $0.
 Dies ist die letzte Chance, diese zu ändern.
 
-Flags: $3(F)ormatieren$4.   Gesamtgröße: $5,  noch frei: $6}
+Flags: $3(F)ormatieren$4.   Gesamtgröße: $5, frei: $6}
 
 message ptnheaders_start	{Start}
 message ptnheaders_end		{Ende}
@@ -1302,6 +1302,7 @@ message	custom_type	{Unbekannt}
 message	cancel		{Abbrechen}
 
 message	out_of_range	{Ungültiger Wert}
+message	invalid_guid	{Ungültige GUID}
 
 message	reedit_partitions	{Erneut bearbeiten}
 message abort_installation	{Installation abbrechen}
@@ -1459,6 +1460,9 @@ Wählen Sie die Partition, die Sie veränd
 message ptn_install {Installation}
 message	ptn_instflag_desc	{(I)nstalllieren, }
 
+message clone_flag	{C}
+message clone_flag_desc	{, (C)lone}
+
 message parttype_gpt {Guid Partition Table (GPT)}
 message parttype_gpt_short {GPT}
 
@@ -1483,3 +1487,14 @@ message	gpt_flag_desc	{, (S)tart}
 message size_ptn_not_mounted		{(Sonstige: $0)}
 
 message running_system			{aktuelles System}
+
+message clone_from_elsewhere		{Externe Partition(en) duplizieren}
+message select_foreign_part		{Wählen Sie eine Quellpartition:}
+message select_source_hdr
+{Zur Zeit ausgwählte Quellpartitionen:}
+message clone_with_data			{Auch die Inhalte duplizieren}
+message	select_source_add		{Weitere Partition hinzufügen}
+message clone_target_end		{Als letzte hinzufügen}
+message clone_target_hdr
+{Einfügen der duplizierten Partitionen vor:}
+message clone_target_disp		{duplizierte Partition(en)}

Index: src/usr.sbin/sysinst/msg.mi.en
diff -u src/usr.sbin/sysinst/msg.mi.en:1.23 src/usr.sbin/sysinst/msg.mi.en:1.24
--- src/usr.sbin/sysinst/msg.mi.en:1.23	Thu Oct 17 08:54:50 2019
+++ src/usr.sbin/sysinst/msg.mi.en	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg.mi.en,v 1.23 2019/10/17 08:54:50 maxv Exp $	*/
+/*	$NetBSD: msg.mi.en,v 1.24 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -307,7 +307,7 @@ message fspart
 {We now have your $2 partitions for $0 below.
 This is your last chance to change them.
 
-Flags: $3(N)ewfs$4.   Total size: $5,  free space: $6}
+Flags: $3(N)ewfs$4.   Total size: $5, free: $6}
 
 message ptnheaders_start	{Start}
 message ptnheaders_end		{End}
@@ -1234,6 +1234,7 @@ message dl_type_invalid	{Invalid file sy
 message	cancel		{Cancel}
 
 message	out_of_range	{Invalid value}
+message	invalid_guid	{Invalid GUID}
 
 message	reedit_partitions	{Re-edit}
 message abort_installation	{Abort installation}
@@ -1395,6 +1396,9 @@ Select the partition you wish to change:
 message install_flag	{I}
 message newfs_flag	{N}
 
+message clone_flag	{C}
+message clone_flag_desc	{, (C)lone}
+
 message ptn_install	{install}
 message ptn_instflag_desc	{(I)nstall, }
 
@@ -1423,3 +1427,15 @@ message size_ptn_not_mounted		{(Other: $
 
 message running_system			{current system}
 
+message clone_from_elsewhere		{Clone external partition(s)}
+message select_foreign_part
+{Please select an external source partition:}
+message select_source_hdr
+{Your currently selected source partitions are:}
+message clone_with_data			{Clone with data}
+message	select_source_add		{Add another partition}
+message clone_target_end		{Add at end}
+message clone_target_hdr
+{Insert cloned partitions before:}
+message clone_target_disp		{cloned partition(s)}
+

Index: src/usr.sbin/sysinst/msg.mi.pl
diff -u src/usr.sbin/sysinst/msg.mi.pl:1.24 src/usr.sbin/sysinst/msg.mi.pl:1.25
--- src/usr.sbin/sysinst/msg.mi.pl:1.24	Wed Oct 23 18:08:31 2019
+++ src/usr.sbin/sysinst/msg.mi.pl	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: msg.mi.pl,v 1.24 2019/10/23 18:08:31 kamil Exp $	*/
+/*	$NetBSD: msg.mi.pl,v 1.25 2019/11/12 16:33:14 martin Exp $	*/
 /*	Based on english version: */
 /*	NetBSD: msg.mi.pl,v 1.36 2004/04/17 18:55:35 atatat Exp       */
 
@@ -1234,6 +1234,7 @@ message dl_type_invalid	{Nieznany typ sy
 message	cancel		{Anuluj}
 
 message	out_of_range	{Nieprawidlowa wartosc}
+message	invalid_guid	{Nieprawidlowa GUID}
 
 message	reedit_partitions	{Edytuj ponownie}
 message abort_installation	{Anuluj instalacje}
@@ -1385,6 +1386,8 @@ message newfs_flag	{N}
 message ptn_install	{do instalacji}
 message ptn_instflag_desc	{(I)nstalacja, }
 
+message clone_flag	{C}
+message clone_flag_desc	{, (C)lone}
 
 message parttype_gpt {Guid Partition Table (GPT)}
 message parttype_gpt_short {GPT}
@@ -1411,3 +1414,15 @@ message size_ptn_not_mounted		{(Inna: $0
 
 message running_system			{current system}
 
+message clone_from_elsewhere		{Clone external partition(s)}
+message select_foreign_part
+{Please select an external source partition:}
+message select_source_hdr
+{Your currently selected source partitions are:}
+message clone_with_data			{Clone with data}
+message	select_source_add		{Add another partition}
+message clone_target_end		{Add at end}
+message clone_target_hdr
+{Insert cloned partitions before:}
+message clone_target_disp		{cloned partition(s)}
+

Index: src/usr.sbin/sysinst/part_edit.c
diff -u src/usr.sbin/sysinst/part_edit.c:1.10 src/usr.sbin/sysinst/part_edit.c:1.11
--- src/usr.sbin/sysinst/part_edit.c:1.10	Sat Oct 26 07:32:52 2019
+++ src/usr.sbin/sysinst/part_edit.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: part_edit.c,v 1.10 2019/10/26 07:32:52 martin Exp $ */
+/*	$NetBSD: part_edit.c,v 1.11 2019/11/12 16:33:14 martin Exp $ */
 
 /*
  * Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -57,8 +57,19 @@ struct part_edit_info {
 	bool num_changed;		/* number of partitions has changed */
 };
 
+struct single_clone_data {
+	struct selected_partitions clone_src;
+	part_id *clone_ids;	/* partition IDs in target */
+};
+struct outer_parts_data {
+	struct arg_rv av;
+	struct single_clone_data *clones;
+	size_t num_clone_entries;
+};
+
 static menu_ent *part_menu_opts;		/* the currently edited partitions */
 static menu_ent *outer_fill_part_menu_opts(const struct disk_partitions *parts, size_t *cnt);
+static void draw_outer_part_line(menudesc *m, int opt, void *arg);
 
 static char 	outer_part_sep_line[MENUSTRSIZE],
 		outer_part_title[2*MENUSTRSIZE];
@@ -388,15 +399,16 @@ fill_part_edit_menu_opts(struct disk_par
 static int
 edit_part_entry(menudesc *m, void *arg)
 {
-	arg_rv *av = arg;
-	struct part_edit_info data = { .parts = av->arg, .cur_id = m->cursel,
+	struct outer_parts_data *pdata = arg;
+	struct part_edit_info data = { .parts = pdata->av.arg,
+	    .cur_id = m->cursel,
 	    .first_custom_opt = __arraycount(common_ptn_edit_opts) };
 	int ptn_menu;
 	const char *err;
 	menu_ent *opts;
 	size_t num_opts;
 
-	opts = fill_part_edit_menu_opts(av->arg, true, ptn_edit_opts,
+	opts = fill_part_edit_menu_opts(data.parts, true, ptn_edit_opts,
 	    __arraycount(ptn_edit_opts), &num_opts);
 	if (opts == NULL)
 		return 1;
@@ -434,10 +446,120 @@ edit_part_entry(menudesc *m, void *arg)
 }
 
 static int
+add_part_clone(menudesc *menu, void *arg)
+{
+	struct outer_parts_data *pdata = arg;
+	struct disk_partitions *parts = pdata->av.arg;
+	struct clone_target_menu_data data;
+	menu_ent *men;
+	int num_men, i;
+	struct disk_part_info sinfo, cinfo;
+	struct disk_partitions *csrc;
+	struct disk_part_free_space space;
+	daddr_t offset, align;
+	size_t s, clone_cnt;
+	part_id cid;
+	struct selected_partitions selected;
+	struct single_clone_data *new_clones;
+
+	if (!select_partitions(&selected, parts))
+		return 0;
+
+	new_clones = realloc(pdata->clones,
+	    sizeof(*pdata->clones)*(pdata->num_clone_entries+1));
+	if (new_clones == NULL)
+		return 0;
+	pdata->num_clone_entries++;
+	pdata->clones = new_clones;
+	new_clones += (pdata->num_clone_entries-1);
+	memset(new_clones, 0, sizeof *new_clones);
+	new_clones->clone_src = selected;
+
+	memset(&data, 0, sizeof data);
+	data.usage.parts = parts;
+
+	/* if we already have partitions, ask for the target position */
+	if (parts->num_part > 0) {
+		data.res = -1;
+		num_men = parts->num_part+1;
+		men = calloc(num_men, sizeof *men);
+		if (men == NULL)
+			return 0;
+		for (i = 0; i < num_men; i++)
+			men[i].opt_action = clone_target_select;
+		men[num_men-1].opt_name = MSG_clone_target_end;
+
+		data.usage.menu = new_menu(MSG_clone_target_hdr,
+		    men, num_men, 3, 2, 0, 65, MC_SCROLL,
+		    NULL, draw_outer_part_line, NULL, NULL, MSG_cancel);
+		process_menu(data.usage.menu, &data);
+		free_menu(data.usage.menu);
+		free(men);
+
+		if (data.res < 0)
+			goto err;
+	} else {
+		data.res = 0;
+	}
+
+	/* find selected offset from data.res and insert clones there */
+	align = parts->pscheme->get_part_alignment(parts);
+	offset = -1;
+	if (data.res > 0) {
+		for (cid = 0; cid < (size_t)data.res; cid++) {
+			if (!parts->pscheme->get_part_info(parts, cid, &sinfo))
+			continue;
+			offset = sinfo.start + sinfo.size;
+		}
+	} else {
+		offset = 0;
+	}
+
+	new_clones->clone_ids = calloc(selected.num_sel,
+	    sizeof(*new_clones->clone_ids));
+	if (new_clones->clone_ids == NULL)
+		goto err;
+	for (s = 0; s < selected.num_sel; s++) {
+		csrc = selected.selection[s].parts;
+		cid = selected.selection[s].id;
+		csrc->pscheme->get_part_info(csrc, cid, &sinfo);
+		if (!parts->pscheme->adapt_foreign_part_info(
+		    parts, &cinfo, csrc->pscheme, &sinfo))
+			continue;
+		size_t cnt = parts->pscheme->get_free_spaces(
+		    parts, &space, 1, cinfo.size-align, align,
+		    offset, -1);
+		if (cnt == 0)
+			continue;
+		cinfo.start = space.start;
+		cid = parts->pscheme->add_partition(
+		    parts, &cinfo, NULL);
+		new_clones->clone_ids[s] = cid;
+		if (cid == NO_PART)
+			continue;
+		parts->pscheme->get_part_info(parts, cid, &cinfo);
+			clone_cnt++;
+		offset = rounddown(cinfo.start+cinfo.size+align, align);
+	}
+
+	/* reload menu and start again */
+	menu_opts_reload(menu, parts);
+	menu->cursel = parts->num_part+1;
+	if (parts->num_part == 0)
+		menu->cursel++;
+	return -1;
+
+err:
+	free_selected_partitions(&selected);
+	return -1;
+}
+
+
+static int
 add_part_entry(menudesc *m, void *arg)
 {
-	arg_rv *av = arg;
-	struct part_edit_info data = { .parts = av->arg,
+	struct outer_parts_data *pdata = arg;
+	struct part_edit_info data = { .parts = pdata->av.arg,
 	    .first_custom_opt = PTN_OPTS_COMMON };
 	int ptn_menu;
 	daddr_t ptn_alignment;
@@ -446,7 +568,7 @@ add_part_entry(menudesc *m, void *arg)
 	struct disk_part_free_space space;
 	const char *err;
 
-	opts = fill_part_edit_menu_opts(av->arg, false, ptn_add_opts,
+	opts = fill_part_edit_menu_opts(data.parts, false, ptn_add_opts,
 	    __arraycount(ptn_add_opts), &num_opts);
 	if (opts == NULL)
 		return 1;
@@ -617,8 +739,8 @@ draw_outer_ptn_header(menudesc *m, void 
 static void
 draw_outer_part_line(menudesc *m, int opt, void *arg)
 {
-	arg_rv *args = arg;
-	struct disk_partitions *parts = args->arg;
+	struct outer_parts_data *pdata = arg;
+	struct disk_partitions *parts = pdata->av.arg;
 	int len;
 	part_id pno = opt;
 	struct disk_part_info info;
@@ -677,9 +799,9 @@ draw_outer_part_line(menudesc *m, int op
 static int
 part_edit_abort(menudesc *m, void *arg)
 {
-	arg_rv *args = arg;
+	struct outer_parts_data *pdata = arg;
 
-	args->rv = -1;
+	pdata->av.rv = -1;
 	return 0;
 }
 
@@ -692,7 +814,7 @@ outer_fill_part_menu_opts(const struct d
 	bool may_add;
 
 	may_add = parts->pscheme->can_add_partition(parts);
-	num_opts = 3 + parts->num_part;
+	num_opts = 4 + parts->num_part;
 	if (parts->num_part == 0)
 		num_opts++;
 	if (may_add)
@@ -730,6 +852,11 @@ outer_fill_part_menu_opts(const struct d
 		op++;
 	}
 
+	/* and a partition cloner */
+	op->opt_name = MSG_clone_from_elsewhere;
+	op->opt_action = add_part_clone;
+	op++;
+
 	/* and unit changer */
 	op->opt_name = MSG_askunits;
 	op->opt_menu = MENU_sizechoice;
@@ -743,6 +870,9 @@ outer_fill_part_menu_opts(const struct d
 	op->opt_action = part_edit_abort;
 	op++;
 
+	/* counts are consistent? */
+	assert((op - opts) >= 0 && (size_t)(op - opts) == num_opts);
+
 	*cnt = num_opts;
 	return opts;
 }
@@ -750,8 +880,8 @@ outer_fill_part_menu_opts(const struct d
 static void
 draw_outer_part_header(menudesc *m, void *arg)
 {
-	arg_rv *av = arg;
-	struct disk_partitions *parts = av->arg;
+	struct outer_parts_data *pdata = arg;
+	struct disk_partitions *parts = pdata->av.arg;
 	char start[SSTRSIZE], size[SSTRSIZE], col[SSTRSIZE],
 	    *disk_info, total[SSTRSIZE], avail[SSTRSIZE];
 	size_t sep;
@@ -1028,8 +1158,8 @@ ask_outer_partsizes(struct disk_partitio
 {
 	int j;
 	int part_menu;
-	size_t num_opts;
-	arg_rv av;
+	size_t num_opts, i, ci;
+	struct outer_parts_data data;
 
 	part_menu_opts = outer_fill_part_menu_opts(parts, &num_opts);
 	part_menu = new_menu(outer_part_title, part_menu_opts, num_opts,
@@ -1048,18 +1178,18 @@ ask_outer_partsizes(struct disk_partitio
 		pm->current_cylsize = 16065;	/* noone cares nowadays */
 	pm->ptstart = 0;
 	pm->ptsize = 0;
-	av.rv = 0;
+	memset(&data, 0, sizeof data);
+	data.av.arg = parts;
 
 	for (;;) {
-		av.arg = parts;
-		av.rv = 0;
-		process_menu(part_menu, &av);
-		if (av.rv < 0)
+		data.av.rv = 0;
+		process_menu(part_menu, &data);
+		if (data.av.rv < 0)
 			break;
 
 		j = verify_outer_parts(parts, false);
 		if (j == 0) {
-			av.rv = -1;
+			data.av.rv = -1;
 			return false;
 		} else if (j == 1) {
 			continue;
@@ -1067,10 +1197,29 @@ ask_outer_partsizes(struct disk_partitio
 		break;
 	}
 
+	/* handle cloned partitions content copies now */
+	for (i = 0; i < data.num_clone_entries; i++) {
+		for (ci = 0; ci < data.clones[i].clone_src.num_sel; ci++) {
+			if (data.clones[i].clone_src.with_data)
+				clone_partition_data(parts,
+				    data.clones[i].clone_ids[ci],
+				    data.clones[i].clone_src.selection[ci].
+				    parts,
+				    data.clones[i].clone_src.selection[ci].id);
+		}
+	}
+
+	/* free clone data */
+	if (data.clones) {
+		for (i = 0; i < data.num_clone_entries; i++)
+			free_selected_partitions(&data.clones[i].clone_src);
+		free(data.clones);
+	}
+
 	free_menu(part_menu);
 	free(part_menu_opts);
 
-	return av.rv == 0;
+	return data.av.rv == 0;
 }
 
 bool

Index: src/usr.sbin/sysinst/partitions.c
diff -u src/usr.sbin/sysinst/partitions.c:1.4 src/usr.sbin/sysinst/partitions.c:1.5
--- src/usr.sbin/sysinst/partitions.c:1.4	Sat Oct 26 07:32:52 2019
+++ src/usr.sbin/sysinst/partitions.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: partitions.c,v 1.4 2019/10/26 07:32:52 martin Exp $	*/
+/*	$NetBSD: partitions.c,v 1.5 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 2018 The NetBSD Foundation, Inc.
@@ -65,6 +65,34 @@ partitions_read_disk(const char *dev, da
 	return NULL;
 }
 
+bool
+generic_adapt_foreign_part_info(const struct disk_partitions *myself,
+    struct disk_part_info *dest,
+    const struct disk_partitioning_scheme *src_scheme,
+    const struct disk_part_info *src)
+{
+	*dest = *src;
+	if (myself->pscheme == src_scheme)
+		return true;	/* no conversion needed */
+
+	if (src->nat_type == NULL)
+		return false;
+
+	/* slightly simplistic, enhance when needed */
+	dest->nat_type = myself->pscheme->get_fs_part_type(dest->fs_type,
+	    dest->fs_sub_type);
+	if (dest->nat_type == NULL)
+		dest->nat_type = myself->pscheme->get_generic_part_type(
+		    src->nat_type->generic_ptype);
+	if (dest->nat_type == NULL)
+		dest->nat_type = myself->pscheme->create_unknown_part_type();
+	if (dest->nat_type == NULL)
+		dest->nat_type = myself->pscheme->get_generic_part_type(
+		    PT_unknown);
+
+	return true;
+}
+
 /*************** global init ****************************************/
 /*
  * Helper structure to fill our global list of available partitioning
@@ -171,3 +199,15 @@ static const struct part_scheme_desc all
 
 	free(is_available);
 }
+
+/*
+ * Final cleanup
+ */
+void
+partitions_cleanup(void)
+{
+	for (size_t i = 0; i < num_available_part_schemes; i++)
+		if (available_part_schemes[i]->cleanup != NULL)
+			available_part_schemes[i]->cleanup();
+	free(available_part_schemes);
+}

Index: src/usr.sbin/sysinst/partitions.h
diff -u src/usr.sbin/sysinst/partitions.h:1.7 src/usr.sbin/sysinst/partitions.h:1.8
--- src/usr.sbin/sysinst/partitions.h:1.7	Fri Oct 25 12:49:58 2019
+++ src/usr.sbin/sysinst/partitions.h	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: partitions.h,v 1.7 2019/10/25 12:49:58 martin Exp $	*/
+/*	$NetBSD: partitions.h,v 1.8 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 2018 The NetBSD Foundation, Inc.
@@ -233,6 +233,15 @@ struct disk_partitioning_scheme {
 	 */
 	const struct part_type_desc * (*create_custom_part_type)
 	    (const char *custom, const char **err_msg);
+	/*
+	 * Return a usable internal partition type representation
+	 * for types that are not otherwise mappable.
+	 * This could be FS_OTHER for disklabel, or a randomly
+	 * created type guid for GPT. This type may or may not be
+	 * in the regular type list. If not, it needs to behave like a
+	 * custom type.
+	 */
+	const struct part_type_desc * (*create_unknown_part_type)(void);
 
 	/*
 	 * Global attributes
@@ -343,8 +352,10 @@ struct disk_partitioning_scheme {
 	 * This mostly adjusts flags and partition type pointers (using
 	 * more lose matching than add_partition would do).
 	 */
-	bool (*adapt_foreign_part_info)(const struct disk_partitions*,
-	    const struct disk_part_info *src, struct disk_part_info *dest);
+	bool (*adapt_foreign_part_info)(
+	    const struct disk_partitions *myself, struct disk_part_info *dest,
+	    const struct disk_partitioning_scheme *src_scheme,
+	    const struct disk_part_info *src);
 
 	/*
 	 * Update data for an existing partition
@@ -491,6 +502,9 @@ struct disk_partitioning_scheme {
 
 	/* Free all the data */
 	void (*free)(struct disk_partitions*);
+
+	/* Scheme global cleanup */
+	void (*cleanup)(void);
 };
 
 /*
@@ -552,6 +566,16 @@ struct disk_partitions *
 partitions_read_disk(const char *, daddr_t disk_size, bool no_mbr);
 
 /*
- * One time initialization
+ * Generic part info adaption, may be overriden by individual partitionin
+ * schemes
+ */
+bool generic_adapt_foreign_part_info(
+    const struct disk_partitions *myself, struct disk_part_info *dest,
+    const struct disk_partitioning_scheme *src_scheme,
+    const struct disk_part_info *src);
+
+/*
+ * One time initialization and clenaup
  */
 void partitions_init(void);
+void partitions_cleanup(void);

Index: src/usr.sbin/sysinst/util.c
diff -u src/usr.sbin/sysinst/util.c:1.34 src/usr.sbin/sysinst/util.c:1.35
--- src/usr.sbin/sysinst/util.c:1.34	Fri Oct  4 21:36:02 2019
+++ src/usr.sbin/sysinst/util.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: util.c,v 1.34 2019/10/04 21:36:02 mrg Exp $	*/
+/*	$NetBSD: util.c,v 1.35 2019/11/12 16:33:14 martin Exp $	*/
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -2043,42 +2043,48 @@ usage_set_from_parts(struct partition_us
 	return usage_info_list_from_parts(&wanted->infos, &wanted->num, parts);
 }
 
-bool
-install_desc_from_parts(struct install_partition_desc *install,
-    struct disk_partitions *parts)
+struct disk_partitions *
+get_inner_parts(struct disk_partitions *parts)
 {
-	struct disk_partitions *inner_parts;
 	daddr_t start, size;
 	part_id pno;
 	struct disk_part_info info;
 
-	memset(install, 0, sizeof(*install));
+	if (parts->pscheme->secondary_scheme == NULL)
+		return NULL;
 
-	if (parts->pscheme->secondary_scheme != NULL) {
-		start = -1;
-		size = -1;
-		if (parts->pscheme->guess_install_target != NULL &&
-		    parts->pscheme->guess_install_target(parts,
-			&start, &size)) {
-		} else {
-			for (pno = 0; pno < parts->num_part; pno++) {
-				if (!parts->pscheme->get_part_info(parts, pno,
-				    &info))
-					continue;
-				if (!(info.flags & PTI_SEC_CONTAINER))
-					continue;
-				start = info.start;
-				size = info.size;
-			}
-		}
-		if (size > 0) {
-			inner_parts = parts->pscheme->secondary_partitions(
-			    parts, start, false);
-			if (inner_parts != NULL)
-				parts = inner_parts;
+	start = -1;
+	size = -1;
+	if (parts->pscheme->guess_install_target == NULL ||
+	    !parts->pscheme->guess_install_target(parts, &start, &size)) {
+		for (pno = 0; pno < parts->num_part; pno++) {
+			if (!parts->pscheme->get_part_info(parts, pno, &info))
+				continue;
+			if (!(info.flags & PTI_SEC_CONTAINER))
+				continue;
+			start = info.start;
+			size = info.size;
 		}
 	}
 
+	if (size > 0)
+		return parts->pscheme->secondary_partitions(parts, start,
+		    false);
+
+	return NULL;
+}
+
+bool
+install_desc_from_parts(struct install_partition_desc *install,
+    struct disk_partitions *parts)
+{
+	struct disk_partitions *inner_parts;
+
+	memset(install, 0, sizeof(*install));
+	inner_parts = get_inner_parts(parts);
+	if (inner_parts != NULL)
+		parts = inner_parts;
+
 	return usage_info_list_from_parts(&install->infos, &install->num,
 	    parts);
 }
@@ -2086,6 +2092,7 @@ install_desc_from_parts(struct install_p
 void
 free_usage_set(struct partition_usage_set *wanted)
 {
+	/* XXX - free parts? free clone src? */
 	free(wanted->menu_opts);
 	free(wanted->infos);
 }
@@ -2093,6 +2100,18 @@ free_usage_set(struct partition_usage_se
 void
 free_install_desc(struct install_partition_desc *install)
 {
+	size_t i, j;
+
+	for (i = 0; i < install->num; i++) {
+		struct selected_partitions *src = install->infos[i].clone_src;
+		if (!(install->infos[i].flags & PUIFLG_CLONE_PARTS) ||
+		    src == NULL)
+			continue;
+		free_selected_partitions(src);
+		for (j = i+1; j < install->num; j++)
+			if (install->infos[j].clone_src == src)
+				install->infos[j].clone_src = NULL; 
+	}
 	free(install->infos);
 }
 

Index: src/usr.sbin/sysinst/arch/i386/md.c
diff -u src/usr.sbin/sysinst/arch/i386/md.c:1.21 src/usr.sbin/sysinst/arch/i386/md.c:1.22
--- src/usr.sbin/sysinst/arch/i386/md.c:1.21	Wed Aug 14 12:55:36 2019
+++ src/usr.sbin/sysinst/arch/i386/md.c	Tue Nov 12 16:33:14 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: md.c,v 1.21 2019/08/14 12:55:36 martin Exp $ */
+/*	$NetBSD: md.c,v 1.22 2019/11/12 16:33:14 martin Exp $ */
 
 /*
  * Copyright 1997 Piermont Information Systems Inc.
@@ -895,7 +895,7 @@ x86_md_part_defaults(struct pm_devs *cur
 		if (info.nat_type->generic_ptype != boot->type)
 			continue;
 		boot->flags &= ~PUIFLAG_ADD_OUTER;
-		boot->flags |= PUIFLG_IS_OUTER|PUIFLAG_ADD_INNER;
+		boot->flags |= PUIFLG_IS_OUTER|PUIFLG_ADD_INNER;
 		boot->size = info.size;
 		boot->cur_start = info.start;
 		boot->cur_flags = info.flags;

Reply via email to