1/*	$NetBSD: partman.c,v 1.57 2023/11/25 19:43:26 martin Exp $ */
2
3/*
4 * Copyright 2012 Eugene Lozovoy
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of Eugene Lozovoy may not be used to endorse
16 *    or promote products derived from this software without specific prior
17 *    written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33/*
34 * Copyright 2010 The NetBSD Foundation, Inc.
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 *
46 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
47 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
50 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
53 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
54 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
56 * THE POSSIBILITY OF SUCH DAMAGE.
57 *
58 */
59
60/* partman.c - extended partitioning */
61
62#include <assert.h>
63#include <fcntl.h>
64#include <errno.h>
65#include <libgen.h>
66#include <paths.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <unistd.h>
70#include <util.h>
71
72#include "defs.h"
73#include "msg_defs.h"
74#include "menu_defs.h"
75
76/* XXX: replace all MAX_* defines with vars that depend on kernel settings */
77#define MAX_ENTRIES 96
78
79#define MAX_RAID 8
80#define MAX_IN_RAID 48
81struct raid_comp {
82	char name[SSTRSIZE];	/* display name for this component */
83	struct disk_partitions *parts;	/* where is this on? */
84	part_id id;		/* which partition in parts */
85	bool is_spare;		/* is this a spare component? */
86};
87struct raid_desc {
88	int enabled;
89	int blocked;
90	int node;		/* the N in raid${N} */
91	int numRow, numCol, numSpare;
92	int sectPerSU, SUsPerParityUnit, SUsPerReconUnit, raid_level;
93	daddr_t total_size;
94	struct raid_comp comp[MAX_IN_RAID];
95};
96struct raid_desc *raids;
97
98#define MAX_VND 4
99struct vnd_desc {
100	int enabled;
101	int blocked;
102	int node;	/* the N in vnd${N} */
103	char filepath[STRSIZE];
104	daddr_t size;
105	int readonly;
106	int is_exist;
107	int manual_geom;
108	int secsize, nsectors, ntracks, ncylinders;
109	struct pm_devs *pm;	/* device this is on */
110	part_id pm_part;	/* which partition (in pm->parts) */
111};
112struct vnd_desc *vnds;
113
114#define MAX_CGD 4
115struct cgd_desc {
116	int enabled;
117	int blocked;
118	int node;	/* the N in cgd${N} */
119	char pm_name[SSTRSIZE];
120	const char *keygen_type;
121	const char *verify_type;
122	const char *enc_type;
123	const char *iv_type;
124	int key_size;
125	struct pm_devs *pm;	/* device this is on */
126	part_id pm_part;	/* which partition (in pm->parts) */
127};
128struct cgd_desc *cgds;
129
130#define MAX_LVM_VG 16
131#define MAX_LVM_PV 255
132#define MAX_LVM_LV 255
133
134struct lvm_pv_reg {
135	struct pm_devs *pm;
136	daddr_t start;
137};
138struct lvm_pv_reg lvm_pvs[MAX_LVM_PV];	/* XXX - make dynamic */
139
140typedef struct pv_t {
141	struct pm_devs *pm;
142	char pm_name[SSTRSIZE];
143	part_id pm_part;
144	int metadatasize;
145	int metadatacopies;
146	int labelsector;
147	int setphysicalvolumesize;
148} pv_t;
149typedef struct lv_t {
150	int blocked;
151	daddr_t size;
152	char name[SSTRSIZE];
153	int readonly;
154	int contiguous;
155	char extents[SSTRSIZE];
156	int minor;
157	int mirrors;
158	int regionsize;
159	int persistent;
160	int readahead;
161	int stripes;
162	int stripesize;
163	int zero;
164} lv_t;
165typedef struct lvms_t {
166	int enabled;
167	int blocked;
168	char name[SSTRSIZE];
169	int maxlogicalvolumes;
170	int maxphysicalvolumes;
171	int physicalextentsize;
172	daddr_t total_size;
173	pv_t pv[MAX_LVM_PV];
174	lv_t lv[MAX_LVM_LV];
175} lvms_t;
176lvms_t *lvms;
177
178typedef struct structinfo_t {
179	int max;
180	uint entry_size;
181	uint parent_size;
182	void *entry_first;
183	void *entry_enabled;
184	void *entry_blocked;
185	void *entry_node;
186} structinfo_t;
187structinfo_t raids_t_info, vnds_t_info, cgds_t_info, lvms_t_info, lv_t_info;
188
189typedef struct pm_upddevlist_adv_t {
190	const char *create_msg;
191	int pe_type;
192	structinfo_t *s;
193	int sub_num;
194	struct pm_upddevlist_adv_t *sub;
195} pm_upddevlist_adv_t;
196
197#define MAX_MNTS 48
198struct {
199    char dev[STRSIZE];
200    const char *mnt_opts, *on;
201} *mnts;
202
203static int pm_cursel; /* Number of selected entry in main menu */
204static int pm_changed; /* flag indicating that we have unsaved changes */
205static int pm_raid_curspare; /* XXX: replace by true way */
206static int pm_retvalue;
207
208enum { /* RAIDframe menu enum */
209	PMR_MENU_DEVS, PMR_MENU_DEVSSPARE, PMR_MENU_RAIDLEVEL, PMR_MENU_NUMROW,
210	PMR_MENU_NUMCOL, PMR_MENU_NUMSPARE,	PMR_MENU_SECTPERSU,	PMR_MENU_SUSPERPARITYUNIT,
211	PMR_MENU_SUSPERRECONUNIT, PMR_MENU_REMOVE, PMR_MENU_END
212};
213
214enum { /* VND menu enum */
215	PMV_MENU_FILEPATH, PMV_MENU_EXIST, PMV_MENU_SIZE, PMV_MENU_RO, PMV_MENU_MGEOM,
216	PMV_MENU_SECSIZE, PMV_MENU_NSECTORS, PMV_MENU_NTRACKS, PMV_MENU_NCYLINDERS,
217	PMV_MENU_REMOVE, PMV_MENU_END
218};
219
220enum { /* CGD menu enum */
221	PMC_MENU_DEV, PMC_MENU_ENCTYPE, PMC_MENU_KEYSIZE, PMC_MENU_IVTYPE,
222	PMC_MENU_KEYGENTYPE, PMC_MENU_VERIFYTYPE, PMC_MENU_REMOVE, PMC_MENU_END
223};
224
225enum { /* LVM menu enum */
226	PML_MENU_PV, PML_MENU_NAME, PML_MENU_MAXLOGICALVOLUMES,
227	PML_MENU_MAXPHYSICALVOLUMES, PML_MENU_PHYSICALEXTENTSIZE,
228	PML_MENU_REMOVE, PML_MENU_END
229};
230
231enum { /* LVM submenu (logical volumes) enum */
232	PMLV_MENU_NAME, PMLV_MENU_SIZE, PMLV_MENU_READONLY, PMLV_MENU_CONTIGUOUS,
233	PMLV_MENU_EXTENTS, PMLV_MENU_MINOR, PMLV_MENU_PERSISTENT,
234	PMLV_MENU_MIRRORS, PMLV_MENU_REGIONSIZE, PMLV_MENU_READAHEAD,
235	PMLV_MENU_STRIPES, PMLV_MENU_STRIPESIZE, PMLV_MENU_ZERO,
236	PMLV_MENU_REMOVE, PMLV_MENU_END
237};
238
239struct part_entry pm_dev_list(int);
240static int pm_raid_disk_add(menudesc *, void *);
241static int pm_raid_disk_del(menudesc *, void *);
242static int pm_cgd_disk_set(struct cgd_desc *, struct part_entry *);
243static int pm_mount(struct pm_devs *, int);
244static int pm_upddevlist(menudesc *, void *, struct install_partition_desc *);
245static void pm_select(struct pm_devs *);
246
247
248static int
249pm_do_upddevlist(menudesc *m, void *arg)
250{
251	return pm_upddevlist(m, arg, ((struct part_entry*)arg)->install);
252}
253
254static void
255pm_edit_size_value(msg prompt_msg, daddr_t bps, daddr_t cylsec, daddr_t *size)
256{
257
258	char answer[16], dflt[16];
259	daddr_t new_size_val, mult;
260
261	snprintf(dflt, sizeof dflt, "%" PRIu64 "%s", *size / sizemult,
262	    multname);
263
264	msg_prompt_win(prompt_msg, -1, 18, 0, 0, dflt, answer, sizeof answer);
265
266	mult = sizemult;
267	new_size_val = parse_disk_pos(answer, &mult, bps, cylsec, NULL);
268
269	if (new_size_val > 0)
270		*size = new_size_val * mult;
271}
272
273static const char *
274pm_get_mount(struct pm_devs *p, part_id id)
275{
276
277	if (p->mounted == NULL)
278		return NULL;
279	if (id >= p->parts->num_part)
280		return NULL;
281	return p->mounted[id];
282}
283
284bool pm_set_mount(struct pm_devs *p, part_id id, const char *path);
285
286bool
287pm_set_mount(struct pm_devs *p, part_id id, const char *path)
288{
289
290	if (p->parts == NULL || id >= p->parts->num_part)
291		return false;
292
293	if (p->mounted == NULL) {
294		p->mounted = calloc(p->parts->num_part, sizeof(char*));
295		if (p->mounted == NULL)
296			return false;
297	}
298	free(p->mounted[id]);
299	p->mounted[id] = strdup(path);
300	return p->mounted[id] != NULL;
301}
302
303/* Universal menu for RAID/VND/CGD/LVM entry edit */
304static int
305pm_edit(int menu_entries_count, void (*menu_fmt)(menudesc *, int, void *),
306	int (*action)(menudesc *, void *), int (*check_fun)(void *),
307	void (*entry_init)(void *, void *),	void *entry_init_arg,
308	void *dev_ptr, int dev_ptr_delta, structinfo_t *s)
309{
310	int i, ok = 0;
311	menu_ent *menu_entries;
312
313	if (dev_ptr == NULL) {
314		/* We should create new device */
315		for (i = 0; i < s->max && !ok; i++)
316			if (*(int*)((char*)s->entry_enabled + dev_ptr_delta + s->entry_size * i) == 0) {
317				dev_ptr = (char*)s->entry_first + dev_ptr_delta + s->entry_size * i;
318				entry_init(dev_ptr, entry_init_arg);
319				ok = 1;
320			}
321		if (!ok) {
322			/* We do not have free device slots */
323			hit_enter_to_continue(NULL, MSG_limitcount);
324			return -1;
325		}
326	}
327
328	menu_entries = calloc(menu_entries_count, sizeof *menu_entries);
329	for (i = 0; i < menu_entries_count - 1; i++)
330		menu_entries[i] = (menu_ent) { .opt_action=action };
331	menu_entries[i] = (menu_ent) {	.opt_name=MSG_fremove,
332					.opt_flags=OPT_EXIT,
333					.opt_action=action };
334
335	int menu_no = -1;
336	menu_no = new_menu(NULL, menu_entries, menu_entries_count,
337		-1, -1, 0, 40, MC_NOCLEAR | MC_SCROLL,
338		NULL, menu_fmt, NULL, NULL, MSG_DONE);
339
340	process_menu(menu_no, dev_ptr);
341	free_menu(menu_no);
342	free(menu_entries);
343
344	return check_fun(dev_ptr);
345}
346
347/* Show filtered partitions menu */
348struct part_entry
349pm_dev_list(int type)
350{
351	int dev_num = -1, num_devs = 0;
352	bool ok;
353	part_id i;
354	int menu_no;
355	struct disk_part_info info;
356	menu_ent menu_entries[MAX_DISKS*MAXPARTITIONS];
357	struct part_entry disk_entries[MAX_DISKS*MAXPARTITIONS];
358	struct pm_devs *pm_i;
359
360	SLIST_FOREACH(pm_i, &pm_head, l) {
361		if (pm_i->parts == NULL)
362			continue;
363		for (i = 0; i < pm_i->parts->num_part; i++) {
364			ok = false;
365			if (!pm_i->parts->pscheme->get_part_info(pm_i->parts,
366			    i, &info))
367				continue;
368			if (info.flags &
369			    (PTI_WHOLE_DISK|PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
370				continue;
371			switch (type) {
372				case PM_RAID:
373					if (info.fs_type == FS_RAID)
374						ok = 1;
375					break;
376				case PM_CGD:
377					if (info.fs_type == FS_CGD)
378						ok = 1;
379					break;
380				case PM_LVM:
381					if (pm_is_lvmpv(pm_i, i, &info))
382						ok = 1;
383					break;
384			}
385			if (!ok)
386				continue;
387			if (pm_partusage(pm_i, i, 0) != 0)
388				continue;
389
390			disk_entries[num_devs].dev_ptr = pm_i;
391			disk_entries[num_devs].id = i;
392			disk_entries[num_devs].parts = pm_i->parts;
393
394			pm_i->parts->pscheme->get_part_device(
395			    pm_i->parts, i, disk_entries[num_devs].fullname,
396			    sizeof disk_entries[num_devs].fullname,
397			    NULL, plain_name, false, true);
398
399			menu_entries[num_devs] = (struct menu_ent) {
400				.opt_name = disk_entries[num_devs].fullname,
401				.opt_action = set_menu_select,
402				.opt_flags = OPT_EXIT,
403			};
404			num_devs++;
405		}
406	}
407
408	menu_no = new_menu(MSG_avdisks,
409		menu_entries, num_devs, -1, -1,
410		(num_devs+1<3)?3:num_devs+1, 13,
411		MC_SCROLL | MC_NOCLEAR, NULL, NULL, NULL, NULL, MSG_cancel);
412	if (menu_no == -1)
413		return (struct part_entry) { 0 };
414	process_menu(menu_no, &dev_num);
415	free_menu(menu_no);
416
417	if (dev_num < 0 || dev_num >= num_devs)
418		return (struct part_entry) { 0 };
419
420	pm_retvalue = dev_num;
421	return disk_entries[dev_num];
422}
423
424/* Get unused raid*, cgd* or vnd* device */
425static int
426pm_manage_getfreenode(void *node, const char *d, structinfo_t *s)
427{
428	int i, ii, ok;
429	char buf[SSTRSIZE];
430	struct pm_devs *pm_i;
431
432	*(int*)node = -1;
433	for (i = 0; i < s->max; i++) {
434		ok = 1;
435		/* Check that node is not already reserved */
436		for (ii = 0; ii < s->max; ii++) {
437			if (*(int*)((char*)s->entry_enabled + s->entry_size
438			    * ii) == 0)
439				continue;
440			if (*(int*)((char*)s->entry_node + s->entry_size * ii)
441			    == i) {
442				ok = 0;
443				break;
444			}
445		}
446		if (! ok)
447			continue;
448		/* Check that node is not in the device list */
449		snprintf(buf, SSTRSIZE, "%s%d", d, i);
450		SLIST_FOREACH(pm_i, &pm_head, l)
451			if (! strcmp(pm_i->diskdev, buf)) {
452				ok = 0;
453				break;
454			}
455		if (ok) {
456			*(int*)node = i;
457			return i;
458		}
459	}
460	hit_enter_to_continue(NULL, MSG_nofreedev);
461	return -1;
462}
463
464/*
465 * Show a line for a device, usually with full size in the right
466 * column, alternatively (if != NULL) with no_size_display
467 * instead in parentheses (used for error displays or to note
468 * a action that can be done to this device.
469 */
470static void
471pm_fmt_disk_line(WINDOW *w, const char *line, const char *on,
472    daddr_t total, const char *no_size_display)
473{
474	char out[STRSIZE], human[6];
475
476	if (on != NULL) {
477		snprintf(out, sizeof out, "%s %s %s", line,
478		    msg_string(MSG_pm_menu_on), on);
479		line = out;
480	}
481	if (no_size_display != NULL) {
482		wprintw(w, "   %-56s (%s)", line, no_size_display);
483	} else {
484		humanize_number(human, sizeof(human),
485	            total * 512, "",
486		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
487		wprintw(w, "   %-56s %s", line, human);
488	}
489}
490
491/***
492 RAIDs
493 ***/
494
495static void
496pm_raid_menufmt(menudesc *m, int opt, void *arg)
497{
498	int i, ok = 0;
499	char buf[STRSIZE], rdev[STRSIZE], level[STRSIZE], *line;
500	struct raid_desc *dev_ptr = ((struct part_entry *)arg)[opt].dev_ptr;
501
502	if (dev_ptr->enabled == 0)
503		return;
504	buf[0] = '\0';
505	sprintf(rdev, "raid%d", dev_ptr->node);
506	for (i = 0; i < MAX_IN_RAID; i++) {
507		if (dev_ptr->comp[i].parts != NULL) {
508			strlcat(buf, dev_ptr->comp[i].name, sizeof buf);
509			strlcat(buf, " ", sizeof buf);
510			ok = 1;
511		}
512	}
513	if (ok) {
514		sprintf(level, "%u", dev_ptr->raid_level);
515		const char *args[] = { rdev, level };
516		line = str_arg_subst(msg_string(MSG_raid_menufmt),
517		    __arraycount(args), args);
518		pm_fmt_disk_line(m->mw, line, buf, dev_ptr->total_size, NULL);
519		free(line);
520	} else {
521		pm_fmt_disk_line(m->mw, buf, NULL, 0,
522		    msg_string(MSG_raid_err_menufmt));
523	}
524}
525
526static void
527pm_raid_edit_menufmt(menudesc *m, int opt, void *arg)
528{
529	int i;
530	char buf[STRSIZE];
531	struct raid_desc *dev_ptr = arg;
532
533	buf[0] = '\0';
534	switch (opt) {
535		case PMR_MENU_DEVS:
536			strlcpy(buf, msg_string(MSG_raid_disks_fmt),
537			    sizeof buf);
538			strlcat(buf, ": ", sizeof buf);
539			for (i = 0; i < MAX_IN_RAID; i++) {
540				if (dev_ptr->comp[i].parts == NULL ||
541				     dev_ptr->comp[i].is_spare)
542					continue;
543				strlcat(buf, " ", sizeof buf);
544				strlcat(buf, dev_ptr->comp[i].name, sizeof buf);
545			}
546			wprintw(m->mw, "%s", buf);
547			break;
548		case PMR_MENU_DEVSSPARE:
549			strlcpy(buf, msg_string(MSG_raid_spares_fmt),
550			    sizeof buf);
551			strlcat(buf, ": ", sizeof buf);
552			for (i = 0; i < MAX_IN_RAID; i++) {
553				if (dev_ptr->comp[i].parts == NULL ||
554				     !dev_ptr->comp[i].is_spare)
555					continue;
556				strlcat(buf, " ", sizeof buf);
557				strlcat(buf, dev_ptr->comp[i].name, sizeof buf);
558			}
559			wprintw(m->mw, "%s", buf);
560			break;
561		case PMR_MENU_RAIDLEVEL:
562			wprintw(m->mw, "%s: %u",
563			    msg_string(MSG_raid_level_fmt),
564			    dev_ptr->raid_level);
565			break;
566		case PMR_MENU_NUMROW:
567			wprintw(m->mw, "%s: %u",
568			    msg_string(MSG_raid_numrow_fmt), dev_ptr->numRow);
569			break;
570		case PMR_MENU_NUMCOL:
571			wprintw(m->mw, "%s: %u",
572			    msg_string(MSG_raid_numcol_fmt), dev_ptr->numCol);
573			break;
574		case PMR_MENU_NUMSPARE:
575			wprintw(m->mw, "%s: %u",
576			    msg_string(MSG_raid_numspare_fmt),
577			    dev_ptr->numSpare);
578			break;
579		case PMR_MENU_SECTPERSU:
580			wprintw(m->mw, "%s: %u",
581			    msg_string(MSG_raid_sectpersu_fmt),
582			    dev_ptr->sectPerSU);
583			break;
584		case PMR_MENU_SUSPERPARITYUNIT:
585			wprintw(m->mw, "%s: %u",
586			    msg_string(MSG_raid_superpar_fmt),
587			    dev_ptr->SUsPerParityUnit);
588			break;
589		case PMR_MENU_SUSPERRECONUNIT:
590			wprintw(m->mw, "%s: %u",
591			    msg_string(MSG_raid_superrec_fmt),
592			    dev_ptr->SUsPerReconUnit);
593			break;
594	}
595}
596
597static int
598pm_raid_set_value(menudesc *m, void *arg)
599{
600	int retvalue = -1;
601	int *out_var = NULL;
602	char buf[SSTRSIZE];
603	const char *msg_to_show = NULL;
604	struct raid_desc *dev_ptr = arg;
605
606	static menu_ent menuent_disk_adddel[] = {
607	    { .opt_name=MSG_add, .opt_flags=OPT_EXIT,
608	      .opt_action=pm_raid_disk_add },
609	    { .opt_name=MSG_remove, .opt_flags=OPT_EXIT,
610	      .opt_action=pm_raid_disk_del }
611	};
612	static int menu_disk_adddel = -1;
613	if (menu_disk_adddel == -1) {
614		menu_disk_adddel = new_menu(NULL, menuent_disk_adddel,
615			__arraycount(menuent_disk_adddel),
616			-1, -1, 0, 10, MC_NOCLEAR, NULL, NULL, NULL, NULL,
617			MSG_cancel);
618	}
619
620	switch (m->cursel) {
621		case PMR_MENU_DEVS:
622			pm_raid_curspare = 0;
623			process_menu(menu_disk_adddel, dev_ptr);
624			return 0;
625		case PMR_MENU_DEVSSPARE:
626			pm_raid_curspare = 1;
627			process_menu(menu_disk_adddel, dev_ptr);
628			return 0;
629		case PMR_MENU_RAIDLEVEL:
630			process_menu(MENU_raidlevel, &retvalue);
631			if (retvalue >= 0)
632				dev_ptr->raid_level = retvalue;
633			return 0;
634		case PMR_MENU_NUMROW:
635			hit_enter_to_continue(NULL, MSG_raid_nomultidim);
636			return 0;
637#if 0 /* notyet */
638			msg_to_show = MSG_raid_numrow_ask;
639			out_var = &(dev_ptr->numRow);
640			break;
641#endif
642		case PMR_MENU_NUMCOL:
643			msg_to_show = MSG_raid_numcol_ask;
644			out_var = &(dev_ptr->numCol);
645			break;
646		case PMR_MENU_NUMSPARE:
647			msg_to_show = MSG_raid_numspare_ask;
648			out_var = &(dev_ptr->numSpare);
649			break;
650		case PMR_MENU_SECTPERSU:
651			msg_to_show = MSG_raid_sectpersu_ask;
652			out_var = &(dev_ptr->sectPerSU);
653			break;
654		case PMR_MENU_SUSPERPARITYUNIT:
655			msg_to_show = MSG_raid_superpar_ask;
656			out_var = &(dev_ptr->SUsPerParityUnit);
657			break;
658		case PMR_MENU_SUSPERRECONUNIT:
659			msg_to_show = MSG_raid_superrec_ask;
660			out_var = &(dev_ptr->SUsPerReconUnit);
661			break;
662		case PMR_MENU_REMOVE:
663			dev_ptr->enabled = 0;
664			return 0;
665	}
666	if (out_var == NULL || msg_to_show == NULL)
667		return -1;
668	snprintf(buf, SSTRSIZE, "%d", *out_var);
669	msg_prompt_win(msg_to_show, -1, 18, 0, 0, buf, buf, SSTRSIZE);
670	if (atoi(buf) >= 0)
671		*out_var = atoi(buf);
672	return 0;
673}
674
675static void
676pm_raid_init(void *arg, void *none)
677{
678	struct raid_desc *dev_ptr = arg;
679	memset(dev_ptr, 0, sizeof(*dev_ptr));
680	*dev_ptr = (struct raid_desc) {
681		.enabled = 1,
682		.blocked = 0,
683		.sectPerSU = 32,
684		.SUsPerParityUnit = 1,
685		.SUsPerReconUnit = 1,
686	};
687}
688
689static int
690pm_raid_check(void *arg)
691{
692	size_t i, dev_num = 0;
693	daddr_t min_size = 0, cur_size = 0;
694	struct raid_desc *dev_ptr = arg;
695	struct disk_part_info info;
696	struct disk_partitions *parts;
697
698	if (dev_ptr->blocked)
699		return 0;
700
701	for (i = 0; i < MAX_IN_RAID; i++) {
702		if (dev_ptr->comp[i].parts != NULL) {
703			parts = dev_ptr->comp[i].parts;
704			if (!parts->pscheme->get_part_info(parts,
705			    dev_ptr->comp[i].id, &info))
706				continue;
707			cur_size = info.size;
708			if (cur_size < min_size || dev_num == 0)
709				min_size = cur_size;
710			if (dev_ptr->comp[i].is_spare)
711				continue;
712			dev_num++;
713		}
714	}
715
716	/* Calculate sum of available space */
717	if (dev_num > 0) {
718		switch (dev_ptr->raid_level) {
719			case 0:
720				dev_ptr->total_size = min_size * dev_num;
721				break;
722			case 1:
723				dev_ptr->total_size = min_size;
724				break;
725			case 4:
726			case 5:
727				dev_ptr->total_size = min_size * (dev_num - 1);
728				break;
729		}
730		pm_manage_getfreenode(&(dev_ptr->node), "raid", &raids_t_info);
731		if (dev_ptr->node < 0)
732			dev_ptr->enabled = 0;
733	}
734	else
735		dev_ptr->enabled = 0;
736	return dev_ptr->enabled;
737}
738
739static int
740pm_raid_disk_add(menudesc *m, void *arg)
741{
742	int i;
743	struct raid_desc *dev_ptr = arg;
744	struct part_entry disk_entrie = pm_dev_list(PM_RAID);
745	if (pm_retvalue < 0)
746		return pm_retvalue;
747
748	for (i = 0; i < MAX_IN_RAID; i++)
749		if (dev_ptr->comp[i].parts == NULL) {
750			dev_ptr->comp[i].parts = disk_entrie.parts;
751			dev_ptr->comp[i].id = disk_entrie.id;
752			dev_ptr->comp[i].is_spare = pm_raid_curspare;
753			strlcpy(dev_ptr->comp[i].name, disk_entrie.fullname,
754			    sizeof dev_ptr->comp[i].name);
755			if (pm_raid_curspare)
756				dev_ptr->numSpare++;
757			else
758				dev_ptr->numCol++;
759			dev_ptr->numRow = 1;
760			break;
761		}
762	return 0;
763}
764
765static int
766pm_raid_disk_del(menudesc *m, void *arg)
767{
768	int retvalue = -1, num_devs = 0;
769	int i, pm_cur;
770	int menu_no;
771	struct raid_desc *dev_ptr = arg;
772	menu_ent menu_entries[MAX_IN_RAID];
773	struct part_entry submenu_args[MAX_IN_RAID];
774
775	for (i = 0; i < MAX_IN_RAID; i++) {
776		if (dev_ptr->comp[i].parts == NULL ||
777			dev_ptr->comp[i].is_spare != pm_raid_curspare)
778			continue;
779		menu_entries[num_devs] = (struct menu_ent) {
780			.opt_name = dev_ptr->comp[i].name,
781			.opt_action = set_menu_select,
782			.opt_flags = OPT_EXIT,
783		};
784		submenu_args[num_devs].dev_ptr = dev_ptr;
785		submenu_args[num_devs].index = i;
786		num_devs++;
787	}
788
789	menu_no = new_menu(MSG_raid_disks,
790		menu_entries, num_devs, -1, -1,
791		(num_devs+1<3)?3:num_devs+1, 13,
792		MC_SCROLL | MC_NOCLEAR, NULL, NULL, NULL, NULL, MSG_cancel);
793	if (menu_no == -1)
794		return -1;
795	process_menu(menu_no, &retvalue);
796	free_menu(menu_no);
797
798	if (retvalue < 0 || retvalue >= num_devs)
799		return -1;
800
801	pm_cur = submenu_args[retvalue].index;
802
803	if (dev_ptr->comp[pm_cur].is_spare)
804		dev_ptr->numSpare--;
805	else
806		dev_ptr->numCol--;
807	dev_ptr->numRow = (dev_ptr->numCol)?1:0;
808	dev_ptr->comp[pm_cur].parts = NULL;
809
810	return 0;
811}
812
813static int
814pm_raid_commit(void)
815{
816	int i, ii;
817	FILE *f;
818	char f_name[STRSIZE], devname[STRSIZE];
819
820	if (!have_raid)
821		return 0;
822	for (i = 0; i < MAX_RAID; i++) {
823		if (! pm_raid_check(&raids[i]))
824			continue;
825
826		/* Generating configure file for our raid */
827		snprintf(f_name, SSTRSIZE, "/tmp/raid.%d.conf", raids[i].node);
828		f = fopen(f_name, "w");
829		if (f == NULL) {
830			endwin();
831			(void)fprintf(stderr,
832			    "Could not open %s for writing\n", f_name);
833			if (logfp)
834				(void)fprintf(logfp,
835				    "Could not open %s for writing\n", f_name);
836			return 1;
837		}
838		scripting_fprintf(NULL, "cat <<EOF >%s\n", f_name);
839		scripting_fprintf(f, "START array\n%d %d %d\n",
840		    raids[i].numRow, raids[i].numCol, raids[i].numSpare);
841
842		scripting_fprintf(f, "\nSTART disks\n");
843		for (ii = 0; ii < MAX_IN_RAID; ii++) {
844			if (raids[i].comp[ii].parts != NULL &&
845			    !raids[i].comp[ii].is_spare) {
846				strcpy(devname, raids[i].comp[ii].name);
847				if (raids[i].comp[ii].parts != NULL &&
848				    raids[i].comp[ii].id != NO_PART) {
849					/* wedge may have moved */
850					raids[i].comp[ii].parts->pscheme->
851					    get_part_device(
852					    raids[i].comp[ii].parts,
853					    raids[i].comp[ii].id,
854					    devname, sizeof devname, NULL,
855					    logical_name, true, true);
856					raids[i].comp[ii].parts->pscheme->
857					    get_part_device(
858					    raids[i].comp[ii].parts,
859					    raids[i].comp[ii].id,
860					    raids[i].comp[ii].name,
861					    sizeof raids[i].comp[ii].name,
862					    NULL, plain_name, true, true);
863				}
864				scripting_fprintf(f,  "%s\n", devname);
865			}
866		}
867
868		scripting_fprintf(f, "\nSTART spare\n");
869		for (ii = 0; ii < MAX_IN_RAID; ii++) {
870			if (raids[i].comp[ii].parts != NULL &&
871			    raids[i].comp[ii].is_spare) {
872				strcpy(devname, raids[i].comp[ii].name);
873				if (raids[i].comp[ii].parts != NULL &&
874				    raids[i].comp[ii].id != NO_PART) {
875					/* wedge may have moved */
876					raids[i].comp[ii].parts->pscheme->
877					    get_part_device(
878					    raids[i].comp[ii].parts,
879					    raids[i].comp[ii].id,
880					    devname, sizeof devname, NULL,
881					    logical_name, true, true);
882					raids[i].comp[ii].parts->pscheme->
883					    get_part_device(
884					    raids[i].comp[ii].parts,
885					    raids[i].comp[ii].id,
886					    raids[i].comp[ii].name,
887					    sizeof raids[i].comp[ii].name,
888					    NULL, plain_name, true, true);
889				}
890
891				scripting_fprintf(f,  "%s\n",
892				    devname);
893			}
894		}
895
896		scripting_fprintf(f, "\nSTART layout\n%d %d %d %d\n",
897		    raids[i].sectPerSU,	raids[i].SUsPerParityUnit,
898		    raids[i].SUsPerReconUnit, raids[i].raid_level);
899
900		scripting_fprintf(f, "\nSTART queue\nfifo 100\n\n");
901		scripting_fprintf(NULL, "EOF\n");
902		fclose (f);
903		fflush(NULL);
904
905		/* Raid initialization */
906		if (run_program(RUN_DISPLAY | RUN_PROGRESS,
907			"raidctl -C %s raid%d", f_name, raids[i].node) == 0 &&
908		    run_program(RUN_DISPLAY | RUN_PROGRESS,
909			"raidctl -I %d raid%d", rand(), raids[i].node) == 0 &&
910		    run_program(RUN_DISPLAY | RUN_PROGRESS,
911		        "raidctl -vi raid%d", raids[i].node) == 0 &&
912		    run_program(RUN_DISPLAY | RUN_PROGRESS,
913			"raidctl -v -A yes raid%d", raids[i].node) == 0) {
914			/*
915			 * RAID creation done, remove it from list to
916			 * prevent its repeated reinitialization
917			 */
918			raids[i].blocked = 1;
919		}
920	}
921	return 0;
922}
923
924/***
925 VND
926 ***/
927
928static void
929pm_vnd_menufmt(menudesc *m, int opt, void *arg)
930{
931	struct vnd_desc *dev_ptr = ((struct part_entry *)arg)[opt].dev_ptr;
932	char dev[STRSIZE];
933
934	if (dev_ptr->enabled == 0)
935		return;
936	sprintf(dev, "vnd%d", dev_ptr->node);
937	if (strlen(dev_ptr->filepath) < 1)
938		pm_fmt_disk_line(m->mw, dev, NULL,
939		    0, msg_string(MSG_vnd_err_menufmt));
940	else if (dev_ptr->is_exist)
941		pm_fmt_disk_line(m->mw, dev, dev_ptr->filepath,
942		    0, msg_string(MSG_vnd_assign));
943	else
944		pm_fmt_disk_line(m->mw, dev, dev_ptr->filepath,
945		    dev_ptr->size, NULL);
946}
947
948static int
949max_msg_length(const msg *p, size_t cnt)
950{
951	int len, m = 0;
952
953	while (cnt > 0) {
954		len = strlen(msg_string(*p));
955		if (len > m)
956			m = len;
957		cnt--; p++;
958	}
959
960	return m;
961}
962
963static void
964pm_vnd_edit_menufmt(menudesc *m, int opt, void *arg)
965{
966	struct vnd_desc *dev_ptr = arg;
967	char buf[SSTRSIZE];
968	strcpy(buf, "-");
969	static int lcol_width;
970	if (lcol_width == 0) {
971		static const msg labels[] = {
972		    MSG_vnd_path_fmt, MSG_vnd_assign_fmt, MSG_vnd_size_fmt,
973		    MSG_vnd_ro_fmt, MSG_vnd_geom_fmt, MSG_vnd_bps_fmt,
974		    MSG_vnd_spt_fmt, MSG_vnd_tpc_fmt, MSG_vnd_cyl_fmt
975		};
976		lcol_width = max_msg_length(labels, __arraycount(labels)) + 3;
977	}
978
979	switch (opt) {
980		case PMV_MENU_FILEPATH:
981			wprintw(m->mw, "%*s %s", -lcol_width,
982			    msg_string(MSG_vnd_path_fmt), dev_ptr->filepath);
983			break;
984		case PMV_MENU_EXIST:
985			wprintw(m->mw, "%*s %s", -lcol_width,
986			    msg_string(MSG_vnd_assign_fmt),
987			    dev_ptr->is_exist?
988				msg_string(MSG_No) : msg_string(MSG_Yes));
989			break;
990		case PMV_MENU_SIZE:
991			if (!dev_ptr->is_exist)
992				snprintf(buf, SSTRSIZE, "%" PRIu64,
993				    dev_ptr->size / sizemult);
994			wprintw(m->mw, "%*s %s", -lcol_width,
995			    msg_string(MSG_vnd_size_fmt), buf);
996			break;
997		case PMV_MENU_RO:
998			wprintw(m->mw, "%*s %s", -lcol_width,
999			    msg_string(MSG_vnd_ro_fmt),
1000			    dev_ptr->readonly?
1001				msg_string(MSG_Yes) : msg_string(MSG_No));
1002			break;
1003		case PMV_MENU_MGEOM:
1004			if (!dev_ptr->is_exist)
1005				snprintf(buf, SSTRSIZE, "%s",
1006				    dev_ptr->manual_geom?
1007				    msg_string(MSG_Yes) : msg_string(MSG_No));
1008			wprintw(m->mw, "%*s %s", -lcol_width,
1009			    msg_string(MSG_vnd_geom_fmt), buf);
1010			break;
1011		case PMV_MENU_SECSIZE:
1012			if (dev_ptr->manual_geom && !dev_ptr->is_exist)
1013				snprintf(buf, SSTRSIZE, "%d", dev_ptr->secsize);
1014			wprintw(m->mw, "%*s %s", -lcol_width,
1015			    msg_string(MSG_vnd_bps_fmt), buf);
1016			break;
1017		case PMV_MENU_NSECTORS:
1018			if (dev_ptr->manual_geom && !dev_ptr->is_exist)
1019				snprintf(buf, SSTRSIZE, "%d",
1020				    dev_ptr->nsectors);
1021			wprintw(m->mw, "%*s %s", -lcol_width,
1022			    msg_string(MSG_vnd_spt_fmt), buf);
1023			break;
1024		case PMV_MENU_NTRACKS:
1025			if (dev_ptr->manual_geom && !dev_ptr->is_exist)
1026				snprintf(buf, SSTRSIZE, "%d", dev_ptr->ntracks);
1027			wprintw(m->mw, "%*s %s", -lcol_width,
1028			    msg_string(MSG_vnd_tpc_fmt), buf);
1029			break;
1030		case PMV_MENU_NCYLINDERS:
1031			if (dev_ptr->manual_geom && !dev_ptr->is_exist)
1032				snprintf(buf, SSTRSIZE, "%d",
1033				    dev_ptr->ncylinders);
1034			wprintw(m->mw, "%*s %s", -lcol_width,
1035			    msg_string(MSG_vnd_cyl_fmt), buf);
1036			break;
1037	}
1038}
1039
1040static int
1041pm_vnd_set_value(menudesc *m, void *arg)
1042{
1043	struct vnd_desc *dev_ptr = arg;
1044	char buf[STRSIZE];
1045	const char *msg_to_show = NULL;
1046	int *out_var = NULL;
1047
1048	switch (m->cursel) {
1049		case PMV_MENU_FILEPATH:
1050			msg_prompt_win(MSG_vnd_path_ask, -1, 18, 0, 0,
1051			    dev_ptr->filepath, dev_ptr->filepath, STRSIZE);
1052			if (dev_ptr->filepath[0] != '/') {
1053				strlcpy(buf, dev_ptr->filepath, MOUNTLEN);
1054				snprintf(dev_ptr->filepath, MOUNTLEN, "/%s",
1055				    buf);
1056			}
1057			if (dev_ptr->filepath[strlen(dev_ptr->filepath) - 1]
1058			    == '/')
1059				dev_ptr->filepath[strlen(dev_ptr->filepath)
1060				     - 1] = '\0';
1061			return 0;
1062		case PMV_MENU_EXIST:
1063			dev_ptr->is_exist = !dev_ptr->is_exist;
1064			return 0;
1065		case PMV_MENU_SIZE:
1066			if (dev_ptr->is_exist)
1067				return 0;
1068
1069			pm_edit_size_value(MSG_vnd_size_ask,
1070			    pm->sectorsize, pm->dlcylsize,
1071			    &dev_ptr->size);
1072
1073			break;
1074		case PMV_MENU_RO:
1075			dev_ptr->readonly = !dev_ptr->readonly;
1076			return 0;
1077		case PMV_MENU_MGEOM:
1078			if (dev_ptr->is_exist)
1079				return 0;
1080			dev_ptr->manual_geom = !dev_ptr->manual_geom;
1081			return 0;
1082		case PMV_MENU_SECSIZE:
1083			if (!dev_ptr->manual_geom || dev_ptr->is_exist)
1084				return 0;
1085			msg_to_show = MSG_vnd_bps_ask;
1086			out_var = &(dev_ptr->secsize);
1087			break;
1088		case PMV_MENU_NSECTORS:
1089			if (!dev_ptr->manual_geom || dev_ptr->is_exist)
1090				return 0;
1091			msg_to_show = MSG_vnd_spt_ask;
1092			out_var = &(dev_ptr->nsectors);
1093			break;
1094		case PMV_MENU_NTRACKS:
1095			if (!dev_ptr->manual_geom || dev_ptr->is_exist)
1096				return 0;
1097			msg_to_show = MSG_vnd_tpc_ask;
1098			out_var = &(dev_ptr->ntracks);
1099			break;
1100		case PMV_MENU_NCYLINDERS:
1101			if (!dev_ptr->manual_geom || dev_ptr->is_exist)
1102				return 0;
1103			msg_to_show = MSG_vnd_cyl_ask;
1104			out_var = &(dev_ptr->ncylinders);
1105			break;
1106		case PMV_MENU_REMOVE:
1107			dev_ptr->enabled = 0;
1108			return 0;
1109	}
1110	if (out_var == NULL || msg_to_show == NULL)
1111		return -1;
1112	snprintf(buf, SSTRSIZE, "%d", *out_var);
1113	msg_prompt_win(msg_to_show, -1, 18, 0, 0, buf, buf, SSTRSIZE);
1114	if (atoi(buf) >= 0)
1115		*out_var = atoi(buf);
1116	return 0;
1117}
1118
1119static void
1120pm_vnd_init(void *arg, void *none)
1121{
1122	struct vnd_desc *dev_ptr = arg;
1123	memset(dev_ptr, 0, sizeof(*dev_ptr));
1124	*dev_ptr = (struct vnd_desc) {
1125		.enabled = 1,
1126		.blocked = 0,
1127		.filepath[0] = '\0',
1128		.is_exist = 0,
1129		.size = 1024,
1130		.readonly = 0,
1131		.manual_geom = 0,
1132		.secsize = 512,
1133		.nsectors = 18,
1134		.ntracks = 2,
1135		.ncylinders = 80
1136	};
1137}
1138
1139static int
1140pm_vnd_check(void *arg)
1141{
1142	struct vnd_desc *dev_ptr = arg;
1143
1144	if (dev_ptr->blocked)
1145		return 0;
1146	if (strlen(dev_ptr->filepath) < 1 ||
1147			dev_ptr->size < 1)
1148		dev_ptr->enabled = 0;
1149	else {
1150		pm_manage_getfreenode(&(dev_ptr->node), "vnd", &vnds_t_info);
1151		if (dev_ptr->node < 0)
1152			dev_ptr->enabled = 0;
1153	}
1154	return dev_ptr->enabled;
1155}
1156
1157/* XXX: vndconfig always return 0? */
1158static int
1159pm_vnd_commit(void)
1160{
1161	int i, error;
1162	char r_o[3], buf[MOUNTLEN+3], resultpath[STRSIZE];
1163	const char *mpath, *mp_suit = NULL, *rp;
1164	struct pm_devs *pm_i, *pm_suit = NULL;
1165	part_id id, part_suit = NO_PART;
1166	struct disk_part_info info;
1167
1168	if (!have_vnd)
1169		return 0;
1170	for (i = 0; i < MAX_VND; i++) {
1171		error = 0;
1172		if (! pm_vnd_check(&vnds[i]))
1173			continue;
1174
1175		/* Trying to assign target device */
1176		SLIST_FOREACH(pm_i, &pm_head, l) {
1177			for (id = 0; id < pm_i->parts->num_part; id++) {
1178				if (!pm_i->parts->pscheme->get_part_info(
1179				    pm_i->parts, id, &info))
1180					continue;
1181				if (info.flags & (PTI_SEC_CONTAINER|
1182				    PTI_WHOLE_DISK|PTI_PSCHEME_INTERNAL|
1183				    PTI_RAW_PART))
1184					continue;
1185				if (info.last_mounted == NULL ||
1186				    info.last_mounted[0] == 0)
1187					continue;
1188				mpath = info.last_mounted;
1189				strcpy(buf, mpath);
1190				if (buf[strlen(buf)-1] != '/')
1191					strcat(buf, "/");
1192				if (strstr(vnds[i].filepath, buf) !=
1193				    vnds[i].filepath)
1194					continue;
1195				if (part_suit == NO_PART || pm_suit == NULL ||
1196				    strlen(buf) > strlen(mp_suit)) {
1197					part_suit = id;
1198					pm_suit = pm_i;
1199					mp_suit = mpath;
1200				}
1201			}
1202		}
1203		if (part_suit == NO_PART || pm_suit == NULL ||
1204		   mp_suit == NULL)
1205			continue;
1206
1207		/* Mounting assigned partition and try to get real file path*/
1208		if (pm_mount(pm_suit, part_suit) != 0)
1209			continue;
1210		rp = pm_get_mount(pm_suit, part_suit);
1211		snprintf(resultpath, STRSIZE, "%s/%s",
1212			rp,
1213			&(vnds[i].filepath[strlen(rp)]));
1214
1215		strcpy(r_o, vnds[i].readonly?"-r":"");
1216		/* If this is a new image */
1217		if (!vnds[i].is_exist) {
1218			run_program(RUN_DISPLAY | RUN_PROGRESS, "mkdir -p %s ",
1219			    dirname(resultpath));
1220			if (error == 0)
1221				error = run_program(RUN_DISPLAY | RUN_PROGRESS,
1222				    "dd if=/dev/zero of=%s bs=1m "
1223				    "count=% " PRIi64 " progress=100 "
1224				    "msgfmt=human",
1225				    resultpath, vnds[i].size*(MEG/512));
1226		}
1227		if (error)
1228			continue;
1229
1230		/* If this is a new image with manual geometry */
1231		if (!vnds[i].is_exist && vnds[i].manual_geom)
1232			error = run_program(RUN_DISPLAY | RUN_PROGRESS,
1233			    "vndconfig %s vnd%d %s %d %d %d %d",
1234			    r_o, vnds[i].node,
1235			    resultpath, vnds[i].secsize,
1236			    vnds[i].nsectors,
1237			    vnds[i].ntracks, vnds[i].ncylinders);
1238		else
1239			/* If this is a existing image or image without manual
1240			 * geometry */
1241			error = run_program(RUN_DISPLAY | RUN_PROGRESS,
1242			    "vndconfig %s vnd%d %s",
1243			    r_o, vnds[i].node, resultpath);
1244
1245		if (error == 0) {
1246			vnds[i].blocked = 1;
1247			vnds[i].pm_part = part_suit;
1248			vnds[i].pm = pm_suit;
1249			vnds[i].pm->blocked++;
1250		}
1251	}
1252	return 0;
1253}
1254
1255/***
1256 CGD
1257 ***/
1258
1259static void
1260pm_cgd_menufmt(menudesc *m, int opt, void *arg)
1261{
1262	struct cgd_desc *dev_ptr = ((struct part_entry *)arg)[opt].dev_ptr;
1263	char desc[STRSIZE];
1264	struct disk_part_info info;
1265
1266	if (dev_ptr->enabled == 0)
1267		return;
1268	if (dev_ptr->pm == NULL)
1269		wprintw(m->mw, "%s", msg_string(MSG_cgd_err_menufmt));
1270	else {
1271		snprintf(desc, sizeof desc, "cgd%d (%s-%d)",
1272		    dev_ptr->node, dev_ptr->enc_type, dev_ptr->key_size);
1273		dev_ptr->pm->parts->pscheme->get_part_info(dev_ptr->pm->parts,
1274		    dev_ptr->pm_part, &info);
1275		pm_fmt_disk_line(m->mw, desc, dev_ptr->pm_name,
1276		    info.size, NULL);
1277	}
1278}
1279
1280static void
1281pm_cgd_edit_menufmt(menudesc *m, int opt, void *arg)
1282{
1283	struct cgd_desc *dev_ptr = arg;
1284	switch (opt) {
1285		case PMC_MENU_DEV:
1286			wprintw(m->mw, "%-15s: %s",
1287			    msg_string(MSG_cgd_dev_fmt), dev_ptr->pm_name);
1288			break;
1289		case PMC_MENU_ENCTYPE:
1290			wprintw(m->mw, "%-15s: %s",
1291			    msg_string(MSG_cgd_enc_fmt), dev_ptr->enc_type);
1292			break;
1293		case PMC_MENU_KEYSIZE:
1294			wprintw(m->mw, "%-15s: %d",
1295			    msg_string(MSG_cgd_key_fmt), dev_ptr->key_size);
1296			break;
1297		case PMC_MENU_IVTYPE:
1298			wprintw(m->mw, "%-15s: %s",
1299			    msg_string(MSG_cgd_iv_fmt), dev_ptr->iv_type);
1300			break;
1301		case PMC_MENU_KEYGENTYPE:
1302			wprintw(m->mw, "%-15s: %s",
1303			    msg_string(MSG_cgd_keygen_fmt), dev_ptr->keygen_type);
1304			break;
1305		case PMC_MENU_VERIFYTYPE:
1306			wprintw(m->mw, "%-15s: %s",
1307			    msg_string(MSG_cgd_verif_fmt), dev_ptr->verify_type);
1308			break;
1309	}
1310}
1311
1312static int
1313pm_cgd_set_value(menudesc *m, void *arg)
1314{
1315	char *retstring;
1316	struct cgd_desc *dev_ptr = arg;
1317
1318	switch (m->cursel) {
1319		case PMC_MENU_DEV:
1320			pm_cgd_disk_set(dev_ptr, NULL);
1321			return 0;
1322		case PMC_MENU_ENCTYPE:
1323			process_menu(MENU_cgd_enctype, &retstring);
1324			dev_ptr->enc_type = retstring;
1325			if (! strcmp(retstring, "aes-xts"))
1326				dev_ptr->key_size = 256;
1327			if (! strcmp(retstring, "aes-cbc"))
1328				dev_ptr->key_size = 192;
1329			if (! strcmp(retstring, "blowfish-cbc"))
1330				dev_ptr->key_size = 128;
1331			if (! strcmp(retstring, "3des-cbc"))
1332				dev_ptr->key_size = 192;
1333			return 0;
1334		case PMC_MENU_KEYSIZE:
1335			if (! strcmp(dev_ptr->enc_type, "aes-xts"))
1336				dev_ptr->key_size +=
1337					(dev_ptr->key_size < 512)? 256 : -256;
1338			if (! strcmp(dev_ptr->enc_type, "aes-cbc"))
1339				dev_ptr->key_size +=
1340					(dev_ptr->key_size < 256)? 64 : -128;
1341			if (! strcmp(dev_ptr->enc_type, "blowfish-cbc"))
1342				dev_ptr->key_size = 128;
1343			if (! strcmp(dev_ptr->enc_type, "3des-cbc"))
1344				dev_ptr->key_size = 192;
1345			return 0;
1346		case PMC_MENU_IVTYPE:
1347			process_menu(MENU_cgd_ivtype, &retstring);
1348			dev_ptr->iv_type = retstring;
1349			return 0;
1350		case PMC_MENU_KEYGENTYPE:
1351			process_menu(MENU_cgd_keygentype, &retstring);
1352			dev_ptr->keygen_type = retstring;
1353			return 0;
1354		case PMC_MENU_VERIFYTYPE:
1355			process_menu(MENU_cgd_verifytype, &retstring);
1356			dev_ptr->verify_type = retstring;
1357			return 0;
1358		case PMC_MENU_REMOVE:
1359			dev_ptr->enabled = 0;
1360			return 0;
1361	}
1362	return -1;
1363}
1364
1365static void
1366pm_cgd_init(void *arg1, void *arg2)
1367{
1368	struct cgd_desc *dev_ptr = arg1;
1369	struct part_entry *disk_entrie = arg2;
1370
1371	memset(dev_ptr, 0, sizeof(*dev_ptr));
1372	*dev_ptr = (struct cgd_desc) {
1373		.enabled = 1,
1374		.blocked = 0,
1375		.pm = NULL,
1376		.pm_name[0] = '\0',
1377		.pm_part = 0,
1378		.keygen_type = "pkcs5_pbkdf2/sha1",
1379		.verify_type = "disklabel",
1380		.enc_type = "aes-xts",
1381		.iv_type = "encblkno1",
1382		.key_size = 256,
1383	};
1384	if (disk_entrie != NULL) {
1385		disk_entrie->parts->pscheme->get_part_device(
1386		    disk_entrie->parts,  disk_entrie->id,
1387		    disk_entrie->fullname, sizeof(disk_entrie->fullname),
1388		    NULL, logical_name, false, true);
1389		pm_cgd_disk_set(dev_ptr, disk_entrie);
1390	}
1391}
1392
1393static int
1394pm_cgd_check(void *arg)
1395{
1396	struct cgd_desc *dev_ptr = arg;
1397
1398	if (dev_ptr->blocked)
1399		return 0;
1400	if (dev_ptr->pm == NULL)
1401		dev_ptr->enabled = 0;
1402	else {
1403		pm_manage_getfreenode(&(dev_ptr->node), "cgd", &cgds_t_info);
1404		if (dev_ptr->node < 0)
1405			dev_ptr->enabled = 0;
1406	}
1407	return dev_ptr->enabled;
1408}
1409
1410static int
1411pm_cgd_disk_set(struct cgd_desc *dev_ptr, struct part_entry *disk_entrie)
1412{
1413	int alloc_disk_entrie = 0;
1414
1415	if (disk_entrie == NULL) {
1416		alloc_disk_entrie = 1;
1417		disk_entrie = malloc (sizeof(struct part_entry));
1418		if (disk_entrie == NULL)
1419			return -2;
1420		*disk_entrie = pm_dev_list(PM_CGD);
1421		if (pm_retvalue < 0) {
1422			free(disk_entrie);
1423			return -1;
1424		}
1425	}
1426	dev_ptr->pm = disk_entrie->dev_ptr;
1427	dev_ptr->pm_part = disk_entrie->id;
1428	strlcpy(dev_ptr->pm_name, disk_entrie->fullname, SSTRSIZE);
1429
1430	if (alloc_disk_entrie)
1431		free(disk_entrie);
1432	return 0;
1433}
1434
1435int
1436pm_cgd_edit_new(struct pm_devs *mypm, part_id id)
1437{
1438	struct part_entry pe = { .id = id, .parts = mypm->parts,
1439	    .dev_ptr = mypm, .type = PM_CGD };
1440
1441	return pm_edit(PMC_MENU_END, pm_cgd_edit_menufmt,
1442		pm_cgd_set_value, pm_cgd_check, pm_cgd_init,
1443		&pe, NULL, 0, &cgds_t_info);
1444}
1445
1446int
1447pm_cgd_edit_old(struct part_entry *pe)
1448{
1449	return pm_edit(PMC_MENU_END, pm_cgd_edit_menufmt,
1450		pm_cgd_set_value, pm_cgd_check, pm_cgd_init,
1451		pe->dev_ptr != NULL ? pe : NULL,
1452		pe->dev_ptr, 0, &cgds_t_info);
1453}
1454
1455static int
1456pm_cgd_commit(void)
1457{
1458	char devname[STRSIZE];
1459	int i, error = 0;
1460
1461	if (!have_cgd)
1462		return 0;
1463	for (i = 0; i < MAX_CGD; i++) {
1464		if (! pm_cgd_check(&cgds[i]))
1465			continue;
1466		if (run_program(RUN_DISPLAY | RUN_PROGRESS,
1467			"cgdconfig -g -V %s -i %s -k %s -o /tmp/cgd.%d.conf"
1468			" %s %d", cgds[i].verify_type,
1469			cgds[i].iv_type, cgds[i].keygen_type, cgds[i].node,
1470			cgds[i].enc_type, cgds[i].key_size) != 0) {
1471			error++;
1472			continue;
1473		}
1474		if (cgds[i].pm != NULL && cgds[i].pm->parts != NULL) {
1475			/* wedge device names may have changed */
1476			cgds[i].pm->parts->pscheme->get_part_device(
1477			    cgds[i].pm->parts, cgds[i].pm_part,
1478			    devname, sizeof devname, NULL,
1479			    logical_name, true, true);
1480			cgds[i].pm->parts->pscheme->get_part_device(
1481			    cgds[i].pm->parts, cgds[i].pm_part,
1482			    cgds[i].pm_name, sizeof cgds[i].pm_name, NULL,
1483			    plain_name, false, true);
1484		} else {
1485			continue;
1486		}
1487		if (run_program(RUN_DISPLAY | RUN_PROGRESS,
1488			"cgdconfig -V re-enter cgd%d '%s' /tmp/cgd.%d.conf",
1489			cgds[i].node, devname, cgds[i].node) != 0) {
1490			error++;
1491			continue;
1492		}
1493		cgds[i].pm->blocked++;
1494		cgds[i].blocked = 1;
1495	}
1496	return error;
1497}
1498
1499/***
1500 LVM
1501 ***/
1502
1503/* Add lvm logical volumes to pm list */
1504/* XXX: rewrite */
1505static void
1506pm_lvm_find(void)
1507{
1508	int i, ii, already_found;
1509	char dev[STRSIZE];
1510	struct pm_devs *pm_i;
1511
1512	for (i = 0; i < MAX_LVM_VG; i++) {
1513		if (! lvms[i].blocked)
1514			continue;
1515		for (ii = 0; ii < MAX_LVM_LV; ii++) {
1516			if (! lvms[i].lv[ii].blocked || lvms[i].lv[ii].size < 1)
1517				continue;
1518			snprintf(dev, STRSIZE, "%s/%s", lvms[i].name,
1519			    lvms[i].lv[ii].name);
1520			already_found = 0;
1521			SLIST_FOREACH(pm_i, &pm_head, l)
1522				if (!already_found && strcmp(pm_i->diskdev,
1523				    dev) == 0) {
1524					pm_i->found = 1;
1525					already_found = 1;
1526				}
1527			if (already_found)
1528				/* We already added this device, skipping */
1529				continue;
1530			pm_new->found = 1;
1531			pm_new->ptstart = 0;
1532			pm_new->ptsize = 0;
1533			pm_new->no_part = true;
1534			pm_new->refdev = &lvms[i].lv[ii];
1535			pm_new->sectorsize = 1;
1536			pm_new->dlcylsize = MEG;
1537			strlcpy(pm_new->diskdev, dev, SSTRSIZE);
1538			strlcpy(pm_new->diskdev_descr, dev, STRSIZE);
1539
1540			if (SLIST_EMPTY(&pm_head))
1541				 SLIST_INSERT_HEAD(&pm_head, pm_new, l);
1542			else
1543				 SLIST_INSERT_AFTER(pm_i, pm_new, l);
1544			pm_new = malloc(sizeof (struct pm_devs));
1545			memset(pm_new, 0, sizeof *pm_new);
1546		}
1547	}
1548}
1549
1550static int
1551pm_lvm_disk_add(menudesc *m, void *arg)
1552{
1553	int i;
1554	lvms_t *dev_ptr = arg;
1555	struct part_entry disk_entrie = pm_dev_list(PM_LVM);
1556	if (pm_retvalue < 0)
1557		return pm_retvalue;
1558
1559	for (i = 0; i < MAX_LVM_PV; i++) {
1560		if (dev_ptr->pv[i].pm == NULL) {
1561			dev_ptr->pv[i].pm = disk_entrie.dev_ptr;
1562			dev_ptr->pv[i].pm_part = disk_entrie.id;
1563			strlcpy(dev_ptr->pv[i].pm_name, disk_entrie.fullname,
1564			    sizeof(dev_ptr->pv[i].pm_name));
1565			break;
1566		}
1567	}
1568	pm_retvalue = 1;
1569	return 0;
1570}
1571
1572static int
1573pm_lvm_disk_del(menudesc *m, void *arg)
1574{
1575	int retvalue = -1, num_devs = 0;
1576	size_t i;
1577	int menu_no;
1578	lvms_t *dev_ptr = arg;
1579	menu_ent menu_entries[MAX_LVM_PV];
1580	struct part_entry submenu_args[MAX_LVM_PV];
1581
1582	for (i = 0; i < MAX_LVM_PV; i++) {
1583		if (dev_ptr->pv[i].pm == NULL)
1584			continue;
1585		menu_entries[num_devs] = (struct menu_ent) {
1586			.opt_name = dev_ptr->pv[i].pm_name,
1587			.opt_action = set_menu_select,
1588			.opt_flags = OPT_EXIT,
1589		};
1590		submenu_args[num_devs].index = i;
1591		num_devs++;
1592	}
1593
1594	menu_no = new_menu(MSG_lvm_disks,
1595		menu_entries, num_devs, -1, -1,
1596		(num_devs+1<3)?3:num_devs+1, 13,
1597		MC_SCROLL | MC_NOCLEAR, NULL, NULL, NULL, NULL, MSG_cancel);
1598	if (menu_no == -1)
1599		return -1;
1600	process_menu(menu_no, &retvalue);
1601	free_menu(menu_no);
1602
1603	if (retvalue < 0 || retvalue >= num_devs)
1604		return -1;
1605
1606	dev_ptr->pv[submenu_args[retvalue].index].pm = NULL;
1607
1608	return 0;
1609}
1610
1611static void
1612pm_lvm_menufmt(menudesc *m, int opt, void *arg)
1613{
1614	int i, ok = 0;
1615	daddr_t used_size = 0;
1616	char buf1[STRSIZE]; buf1[0] = 0;
1617	char buf2[STRSIZE]; buf2[0] = 0;
1618	char devs[STRSIZE]; devs[0] = 0;
1619	lvms_t *dev_ptr = ((struct part_entry *)arg)[opt].dev_ptr;
1620
1621	if (dev_ptr->enabled == 0)
1622		return;
1623	snprintf(buf1, STRSIZE, "VG '%s'", dev_ptr->name);
1624	for (i = 0; i < MAX_LVM_PV; i++)
1625		if (dev_ptr->pv[i].pm != NULL) {
1626			strlcat(devs, dev_ptr->pv[i].pm_name, STRSIZE);
1627			strlcat(devs, " ", STRSIZE);
1628			ok = 1;
1629		}
1630	for (i = 0; i < MAX_LVM_LV; i++)
1631		used_size += dev_ptr->lv[i].size;
1632	if (ok) {
1633		snprintf(buf2, STRSIZE, "%" PRIi64 "/%" PRIi64,
1634			dev_ptr->total_size - used_size, dev_ptr->total_size);
1635		pm_fmt_disk_line(m->mw, buf1, devs, 0, buf2);
1636	} else {
1637		pm_fmt_disk_line(m->mw, buf1, NULL, 0,
1638		    msg_string(MSG_lvm_err_menufmt));
1639	}
1640}
1641
1642static void
1643pm_lvm_edit_menufmt(menudesc *m, int opt, void *arg)
1644{
1645	int i;
1646	char buf[STRSIZE];
1647	lvms_t *dev_ptr = arg;
1648	strlcpy(buf, msg_string(MSG_auto), sizeof buf);
1649
1650	switch (opt) {
1651		case PML_MENU_PV:
1652			buf[0] = '\0';
1653			for (i = 0; i < MAX_LVM_PV; i++) {
1654				if (dev_ptr->pv[i].pm != NULL) {
1655					strlcat(buf, " ", sizeof buf);
1656					strlcat(buf, dev_ptr->pv[i].pm_name,
1657					    sizeof buf);
1658				}
1659			}
1660			wprintw(m->mw, "%-20s: %s",
1661			     msg_string(MSG_lvm_disks_fmt), buf);
1662			break;
1663		case PML_MENU_NAME:
1664			wprintw(m->mw, "%-20s: %s",
1665			    msg_string(MSG_lvm_name_fmt), dev_ptr->name);
1666			break;
1667		case PML_MENU_MAXLOGICALVOLUMES:
1668			if (dev_ptr->maxlogicalvolumes > 0)
1669				snprintf(buf, STRSIZE, "%d",
1670				    dev_ptr->maxlogicalvolumes);
1671			wprintw(m->mw, "%-20s: %s",
1672			    msg_string(MSG_lvm_maxlv_fmt), buf);
1673			break;
1674		case PML_MENU_MAXPHYSICALVOLUMES:
1675			if (dev_ptr->maxphysicalvolumes > 0)
1676				snprintf(buf, STRSIZE, "%d",
1677				    dev_ptr->maxphysicalvolumes);
1678			wprintw(m->mw, "%-20s: %s",
1679			    msg_string(MSG_lvm_maxpv_fmt), buf);
1680			break;
1681		case PML_MENU_PHYSICALEXTENTSIZE:
1682			if (dev_ptr->physicalextentsize > 0)
1683				snprintf(buf, STRSIZE, "%dM",
1684				    dev_ptr->physicalextentsize);
1685			wprintw(m->mw, "%-20s: %s",
1686			    msg_string(MSG_lvm_extsiz_fmt), buf);
1687			break;
1688	}
1689}
1690
1691static int
1692pm_lvm_set_value(menudesc *m, void *arg)
1693{
1694	char buf[STRSIZE];
1695	const char *msg_to_show = NULL;
1696	int *out_var = NULL;
1697	lvms_t *dev_ptr = arg;
1698
1699	static menu_ent menuent_disk_adddel[] = {
1700	    { .opt_name=MSG_add, .opt_flags=OPT_EXIT,
1701	      .opt_action=pm_lvm_disk_add },
1702	    { .opt_name=MSG_remove, .opt_flags=OPT_EXIT,
1703	      .opt_action=pm_lvm_disk_del }
1704	};
1705	static int menu_disk_adddel = -1;
1706	if (menu_disk_adddel == -1) {
1707		menu_disk_adddel = new_menu(NULL, menuent_disk_adddel,
1708			__arraycount(menuent_disk_adddel),
1709			-1, -1, 0, 10, MC_NOCLEAR, NULL, NULL, NULL, NULL,
1710			MSG_cancel);
1711	}
1712
1713	switch (m->cursel) {
1714		case PML_MENU_PV:
1715			process_menu(menu_disk_adddel, arg);
1716			return 0;
1717		case PML_MENU_NAME:
1718			msg_prompt_win(MSG_lvm_name_ask, -1, 18, 0, 0,
1719			    dev_ptr->name, dev_ptr->name, SSTRSIZE);
1720			return 0;
1721		case PML_MENU_MAXLOGICALVOLUMES:
1722			msg_to_show = MSG_lvm_maxlv_ask;
1723			out_var = &(dev_ptr->maxlogicalvolumes);
1724			break;
1725		case PML_MENU_MAXPHYSICALVOLUMES:
1726			msg_to_show = MSG_lvm_maxpv_ask;
1727			out_var = &(dev_ptr->maxphysicalvolumes);
1728			break;
1729		case PML_MENU_PHYSICALEXTENTSIZE:
1730			msg_to_show = MSG_lvm_extsiz_ask;
1731			out_var = &(dev_ptr->physicalextentsize);
1732			break;
1733		case PML_MENU_REMOVE:
1734			dev_ptr->enabled = 0;
1735			return 0;
1736	}
1737	if (out_var == NULL || msg_to_show == NULL)
1738		return -1;
1739	snprintf(buf, SSTRSIZE, "%d", *out_var);
1740	msg_prompt_win(msg_to_show, -1, 18, 0, 0, buf, buf, SSTRSIZE);
1741	if (atoi(buf) >= 0)
1742		*out_var = atoi(buf);
1743	return 0;
1744}
1745
1746static void
1747pm_lvm_init(void *arg, void* none)
1748{
1749	lvms_t *dev_ptr = arg;
1750
1751	memset(dev_ptr, 0, sizeof *dev_ptr);
1752	*dev_ptr = (struct lvms_t) {
1753		.enabled = 1,
1754		.blocked = 0,
1755		.maxlogicalvolumes = MAX_LVM_PV,
1756		.maxphysicalvolumes = MAX_LVM_LV,
1757		.physicalextentsize = -1,
1758	};
1759	sprintf(dev_ptr->name, "vg%.2d", rand()%100);
1760}
1761
1762static int
1763pm_lvm_check(void *arg)
1764{
1765	size_t i;
1766	bool ok = false;
1767	lvms_t *dev_ptr = arg;
1768	dev_ptr->total_size = 0;
1769	struct disk_part_info info;
1770
1771	for (i = 0; i < MAX_LVM_PV; i++) {
1772		if (dev_ptr->pv[i].pm != NULL) {
1773			if (!dev_ptr->pv[i].pm->parts->pscheme->get_part_info(
1774			    dev_ptr->pv[i].pm->parts, dev_ptr->pv[i].pm_part,
1775			    &info))
1776				continue;
1777			ok = 1;
1778			dev_ptr->total_size += info.size;
1779		}
1780	}
1781	if (!ok)
1782		dev_ptr->enabled = 0;
1783	return dev_ptr->enabled;
1784}
1785
1786static void
1787pm_lvmlv_menufmt(menudesc *m, int opt, void *arg)
1788{
1789	char buf[STRSIZE];
1790	lv_t *dev_ptr = ((struct part_entry *)arg)[opt].dev_ptr;
1791
1792	if (dev_ptr->size > 0) {
1793		snprintf(buf, STRSIZE, "'%s'", dev_ptr->name);
1794		pm_fmt_disk_line(m->mw, buf, NULL, dev_ptr->size, NULL);
1795	}
1796}
1797
1798static void
1799pm_lvmlv_edit_menufmt(menudesc *m, int opt, void *arg)
1800{
1801
1802	lv_t *dev_ptr = arg;
1803	char buf[STRSIZE];
1804	strlcpy(buf, msg_string(MSG_auto), STRSIZE);
1805
1806	switch (opt) {
1807		case PMLV_MENU_NAME:
1808			wprintw(m->mw, "%-20s: %s",
1809			    msg_string(MSG_lvmlv_name_fmt), dev_ptr->name);
1810			break;
1811		case PMLV_MENU_SIZE:
1812			wprintw(m->mw, "%-20s: %" PRIi64,
1813			    msg_string(MSG_lvmlv_size_fmt), dev_ptr->size);
1814			break;
1815		case PMLV_MENU_READONLY:
1816			wprintw(m->mw, "%-20s: %s",
1817			    msg_string(MSG_lvmlv_ro_fmt),
1818			    dev_ptr->readonly? msg_string(MSG_Yes) : msg_string(MSG_No));
1819			break;
1820		case PMLV_MENU_CONTIGUOUS:
1821			wprintw(m->mw, "%-20s: %s",
1822			    msg_string(MSG_lvmlv_cont_fmt),
1823			    dev_ptr->contiguous? msg_string(MSG_Yes) : msg_string(MSG_No));
1824			break;
1825		case PMLV_MENU_EXTENTS:
1826			wprintw(m->mw, "%-20s: %s",
1827			    msg_string(MSG_lvmlv_extnum_fmt),
1828			    (strlen(dev_ptr->extents) > 0) ?
1829			    dev_ptr->extents : msg_string(MSG_auto));
1830			break;
1831		case PMLV_MENU_MINOR:
1832			if (dev_ptr->minor > 0)
1833				snprintf(buf, STRSIZE, "%dK", dev_ptr->minor);
1834			wprintw(m->mw, "%-20s: %s",
1835			    msg_string(MSG_lvmlv_minor_fmt), buf);
1836			break;
1837		case PMLV_MENU_MIRRORS:
1838			wprintw(m->mw, "%-20s: %d",
1839			    msg_string(MSG_lvmlv_mirrors_fmt),
1840			    dev_ptr->mirrors);
1841			break;
1842		case PMLV_MENU_REGIONSIZE:
1843			if (dev_ptr->regionsize > 0)
1844				snprintf(buf, STRSIZE, "%dM",
1845				    dev_ptr->regionsize);
1846			wprintw(m->mw, "%-20s: %s",
1847			    msg_string(MSG_lvmlv_regsiz_fmt), buf);
1848			break;
1849		case PMLV_MENU_PERSISTENT:
1850			wprintw(m->mw, "%-20s: %s",
1851			    msg_string(MSG_lvmlv_pers_fmt),
1852			    dev_ptr->persistent ?
1853			    msg_string(MSG_Yes) : msg_string(MSG_No));
1854			break;
1855		case PMLV_MENU_READAHEAD:
1856			if (dev_ptr->readahead > 0)
1857				snprintf(buf, STRSIZE, "%d",
1858				    dev_ptr->readahead);
1859			wprintw(m->mw, "%-20s: %s",
1860			    msg_string(MSG_lvmlv_readahsect_fmt), buf);
1861			break;
1862		case PMLV_MENU_STRIPES:
1863			if (dev_ptr->stripes > 0)
1864				snprintf(buf, STRSIZE, "%d", dev_ptr->stripes);
1865			wprintw(m->mw, "%-20s: %s",
1866			    msg_string(MSG_lvmlv_stripes_fmt), buf);
1867			break;
1868		case PMLV_MENU_STRIPESIZE:
1869			if (dev_ptr->stripesize > 0)
1870				snprintf(buf, STRSIZE, "%dK", dev_ptr->stripesize);
1871			wprintw(m->mw, "%-20s: %s",
1872			    msg_string(MSG_lvmlv_stripesiz_fmt), buf);
1873			break;
1874		case PMLV_MENU_ZERO:
1875			wprintw(m->mw, "%-20s: %s",
1876			    msg_string(MSG_lvmlv_zero_fmt),
1877			    dev_ptr->zero ?
1878			    msg_string(MSG_Yes) : msg_string(MSG_No));
1879			break;
1880	}
1881}
1882
1883static int
1884pm_lvmlv_set_value(menudesc *m, void *arg)
1885{
1886	char buf[STRSIZE];
1887	const char *msg_to_show = NULL;
1888	int *out_var = NULL;
1889	lv_t *dev_ptr = arg;
1890
1891	switch (m->cursel) {
1892		case PMLV_MENU_NAME:
1893			msg_prompt_win(MSG_lvmlv_name_ask, -1, 18, 0, 0,
1894			    dev_ptr->name, dev_ptr->name, SSTRSIZE);
1895			return 0;
1896		case PMLV_MENU_SIZE:
1897			pm_edit_size_value(MSG_lvmlv_size_ask,
1898			    pm->sectorsize, pm->dlcylsize,
1899			    &dev_ptr->size); /* XXX cylsize? */
1900			break;
1901		case PMLV_MENU_READONLY:
1902			dev_ptr->readonly = !dev_ptr->readonly;
1903			return 0;
1904		case PMLV_MENU_CONTIGUOUS:
1905			dev_ptr->contiguous = !dev_ptr->contiguous;
1906			return 0;
1907		case PMLV_MENU_EXTENTS:
1908			msg_prompt_win(MSG_lvmlv_extnum_ask, -1, 18, 0, 0,
1909			    dev_ptr->extents, dev_ptr->extents, SSTRSIZE);
1910			return 0;
1911		case PMLV_MENU_MINOR:
1912			msg_to_show = MSG_lvmlv_minor_ask;
1913			out_var = &(dev_ptr->minor);
1914			break;
1915		case PMLV_MENU_MIRRORS:
1916			msg_to_show = MSG_lvmlv_mirrors_ask;
1917			out_var = &(dev_ptr->mirrors);
1918			break;
1919		case PMLV_MENU_REGIONSIZE:
1920			msg_to_show = MSG_lvmlv_regsiz_ask;
1921			out_var = &(dev_ptr->regionsize);
1922			break;
1923		case PMLV_MENU_PERSISTENT:
1924			dev_ptr->persistent = !dev_ptr->persistent;
1925			return 0;
1926		case PMLV_MENU_READAHEAD:
1927			msg_to_show = MSG_lvmlv_readahsect_ask;
1928			out_var = &(dev_ptr->readahead);
1929			break;
1930		case PMLV_MENU_STRIPES:
1931			msg_to_show = MSG_lvmlv_stripes_ask;
1932			out_var = &(dev_ptr->stripes);
1933			break;
1934		case PMLV_MENU_STRIPESIZE:
1935			if (dev_ptr->stripesize << 1 > 512)
1936				dev_ptr->stripesize = 4;
1937			else
1938				dev_ptr->stripesize <<= 1;
1939			return 0;
1940		case PMLV_MENU_ZERO:
1941			dev_ptr->zero = !dev_ptr->zero;
1942			return 0;
1943		case PMLV_MENU_REMOVE:
1944			dev_ptr->size = 0;
1945			return 0;
1946	}
1947	if (out_var == NULL || msg_to_show == NULL)
1948		return -1;
1949	snprintf(buf, SSTRSIZE, "%d", *out_var);
1950	msg_prompt_win(msg_to_show, -1, 18, 0, 0, buf, buf, SSTRSIZE);
1951	if (atoi(buf) >= 0)
1952		*out_var = atoi(buf);
1953	return 0;
1954}
1955
1956static void
1957pm_lvmlv_init(void *arg, void *none)
1958{
1959	lv_t *dev_ptr = arg;
1960	memset(dev_ptr, 0, sizeof *(dev_ptr));
1961	*dev_ptr = (struct lv_t) {
1962		.blocked = 0,
1963		.size = 1024,
1964		.stripesize = 64,
1965	};
1966	sprintf (dev_ptr->name, "lvol%.2d", rand()%100);
1967}
1968
1969static int
1970pm_lvmlv_check(void *arg)
1971{
1972	lv_t *dev_ptr = arg;
1973	if (dev_ptr->size > 0 && strlen(dev_ptr->name) > 0)
1974		return 1;
1975	else {
1976		dev_ptr->size = 0;
1977		return 0;
1978	}
1979}
1980
1981static int
1982pm_lvm_commit(void)
1983{
1984	int i, ii, error;
1985	uint used_size = 0;
1986	char params[STRSIZE*3], devs[STRSIZE*3], arg[STRSIZE];
1987
1988	if (!have_lvm)
1989		return 0;
1990	for (i = 0; i < MAX_LVM_VG; i++) {
1991		/* Stage 0: checks */
1992		if (! pm_lvm_check(&lvms[i]))
1993			continue;
1994		for (ii = 0; ii < MAX_LVM_LV; ii++)
1995			used_size += lvms[i].lv[ii].size;
1996		if (used_size > lvms[i].total_size)
1997			continue;
1998
1999		params[0] = '\0';
2000		devs[0] = '\0';
2001		error = 0;
2002		/* Stage 1: creating Physical Volumes (PV's) */
2003		for (ii = 0; ii < MAX_LVM_PV && ! error; ii++)
2004			if (lvms[i].pv[ii].pm != NULL) {
2005				run_program(RUN_SILENT | RUN_ERROR_OK,
2006				    "lvm pvremove -ffy /dev/r%s",
2007				    (char*)lvms[i].pv[ii].pm_name);
2008				error += run_program(RUN_DISPLAY | RUN_PROGRESS,
2009				    "lvm pvcreate -ffy /dev/r%s",
2010				    (char*)lvms[i].pv[ii].pm_name);
2011				if (error)
2012					break;
2013				strlcat(devs, " /dev/r", sizeof devs);
2014				strlcat(devs, lvms[i].pv[ii].pm_name,
2015				    sizeof devs);
2016			}
2017		if (error)
2018			continue;
2019		/* Stage 2: creating Volume Groups (VG's) */
2020		if (lvms[i].maxlogicalvolumes > 0) {
2021			snprintf(arg, sizeof arg, " -l %d",
2022			    lvms[i].maxlogicalvolumes);
2023			strlcat(params, arg, sizeof params);
2024		}
2025		if (lvms[i].maxphysicalvolumes > 0) {
2026			snprintf(arg, sizeof arg, " -p %d",
2027			    lvms[i].maxphysicalvolumes);
2028			strlcat(params, arg, sizeof params);
2029		}
2030		if (lvms[i].physicalextentsize > 0) {
2031			snprintf(arg, sizeof arg, " -s %d",
2032			    lvms[i].physicalextentsize);
2033			strlcat(params, arg, sizeof params);
2034		}
2035		error += run_program(RUN_DISPLAY | RUN_PROGRESS,
2036		    "lvm vgcreate %s %s %s", params, lvms[i].name, devs);
2037		if (error)
2038			continue;
2039		/* Stage 3: creating Logical Volumes (LV's) */
2040		for (ii = 0; ii < MAX_LVM_LV; ii++) {
2041			if (lvms[i].lv[ii].size <= 0)
2042				continue;
2043
2044			params[0] = '\0';
2045			snprintf(arg, sizeof arg, " -C %c",
2046			    lvms[i].lv[ii].contiguous?'y':'n');
2047			strlcat(params, arg, sizeof params);
2048			snprintf(arg, sizeof arg, " -M %c",
2049			    lvms[i].lv[ii].persistent?'y':'n');
2050			strlcat(params, arg, sizeof params);
2051			snprintf(arg, sizeof arg, " -p %s",
2052			    lvms[i].lv[ii].readonly?"r":"rw");
2053			strlcat(params, arg, sizeof params);
2054			snprintf(arg, sizeof arg, " -Z %c",
2055			    lvms[i].lv[ii].zero?'y':'n');
2056			strlcat(params, arg, sizeof params);
2057			if (strlen(lvms[i].lv[ii].name) > 0) {
2058				snprintf(arg, sizeof arg, " -n %s",
2059				    lvms[i].lv[ii].name);
2060				strlcat(params, arg, sizeof params);
2061			}
2062			if (strlen(lvms[i].lv[ii].extents) > 0) {
2063				snprintf(arg, sizeof arg, " -l %s",
2064				    lvms[i].lv[ii].extents);
2065				strlcat(params, arg, sizeof params);
2066			}
2067			if (lvms[i].lv[ii].minor > 0) {
2068				snprintf(arg, sizeof arg, " --minor %d",
2069				    lvms[i].lv[ii].minor);
2070				strlcat(params, arg, sizeof params);
2071			}
2072			if (lvms[i].lv[ii].mirrors > 0) {
2073				snprintf(arg, sizeof arg, " -m %d",
2074				    lvms[i].lv[ii].mirrors);
2075				strlcat(params, arg, sizeof params);
2076				if (lvms[i].lv[ii].regionsize > 0) {
2077					snprintf(arg, sizeof arg, " -R %d",
2078					     lvms[i].lv[ii].regionsize);
2079					strlcat(params, arg, sizeof params);
2080				}
2081			}
2082			if (lvms[i].lv[ii].readahead > 0) {
2083				snprintf(arg, sizeof arg, " -r %d",
2084				    lvms[i].lv[ii].readahead);
2085				strlcat(params, arg, sizeof params);
2086			}
2087			if (lvms[i].lv[ii].stripes > 0) {
2088				snprintf(arg, sizeof arg, " -i %d",
2089				    lvms[i].lv[ii].stripes);
2090				strlcat(params, arg, sizeof params);
2091				if (lvms[i].lv[ii].stripesize > 0) {
2092					snprintf(arg, sizeof arg, " -I %d",
2093					    lvms[i].lv[ii].stripesize);
2094					strlcat(params, arg, sizeof params);
2095				}
2096			}
2097			snprintf(arg, sizeof arg, " -L %" PRIi64 "M",
2098			    lvms[i].lv[ii].size);
2099			strlcat(params, arg, sizeof params);
2100
2101			error += run_program(RUN_DISPLAY | RUN_PROGRESS,
2102			    "lvm lvcreate %s %s", params, lvms[i].name);
2103		}
2104		if (! error) {
2105			lvms[i].blocked = 1;
2106			for (ii = 0; ii < MAX_LVM_PV; ii++)
2107				if (lvms[i].pv[ii].pm != NULL)
2108					lvms[i].pv[ii].pm->blocked++;
2109			for (ii = 0; ii < MAX_LVM_LV; ii++)
2110				if (lvms[i].lv[ii].size > 0)
2111					lvms[i].lv[ii].blocked = 1;
2112		}
2113	}
2114
2115	return 0;
2116}
2117
2118/***
2119 Partman generic functions
2120 ***/
2121
2122int
2123pm_getrefdev(struct pm_devs *pm_cur)
2124{
2125	int i, ii, dev_num, num_devs, num_devs_s;
2126	char descr[MENUSTRSIZE], dev[MENUSTRSIZE] = "";
2127
2128	pm_cur->refdev = NULL;
2129	if (have_cgd && strncmp(pm_cur->diskdev, "cgd", 3) == 0) {
2130		dev_num = pm_cur->diskdev[3] - '0';
2131		for (i = 0; i < MAX_CGD; i++)
2132			if (cgds[i].blocked && cgds[i].node == dev_num) {
2133				pm_cur->refdev = &cgds[i];
2134				snprintf(descr, sizeof descr,
2135				    " (%s, %s-%d)", cgds[i].pm_name,
2136				    cgds[i].enc_type, cgds[i].key_size);
2137				strlcat(pm_cur->diskdev_descr, descr,
2138				    sizeof(pm_cur->diskdev_descr));
2139				break;
2140			}
2141 	} else if (have_vnd && strncmp(pm_cur->diskdev, "vnd", 3) == 0) {
2142 		dev_num = pm_cur->diskdev[3] - '0';
2143 		for (i = 0; i < MAX_VND; i++)
2144			if (vnds[i].blocked && vnds[i].node == dev_num) {
2145				pm_cur->refdev = &vnds[i];
2146				vnds[i].pm->parts->pscheme->get_part_device(
2147				    vnds[i].pm->parts, vnds[i].pm_part,
2148				    dev, sizeof dev, NULL, plain_name, false,
2149				    true);
2150				snprintf(descr, sizeof descr, " (%s, %s)",
2151				    dev, vnds[i].filepath);
2152				strlcat(pm_cur->diskdev_descr, descr,
2153				    sizeof(pm_cur->diskdev_descr));
2154				break;
2155			}
2156	} else if (have_raid && strncmp(pm_cur->diskdev, "raid", 4) == 0) {
2157		dev_num = pm_cur->diskdev[4] - '0';
2158 		for (i = 0; i < MAX_RAID; i++)
2159			if (raids[i].blocked && raids[i].node == dev_num) {
2160				pm_cur->refdev = &raids[i];
2161				num_devs = 0; num_devs_s = 0;
2162				for (ii = 0; ii < MAX_IN_RAID; ii++)
2163					if (raids[i].comp[ii].parts != NULL) {
2164						if(raids[i].comp[ii].is_spare)
2165							num_devs_s++;
2166						else
2167							num_devs++;
2168					}
2169				snprintf(descr, sizeof descr,
2170				    " (lvl %d, %d disks, %d spare)",
2171				    raids[i].raid_level, num_devs,
2172				    num_devs_s);
2173				strlcat(pm_cur->diskdev_descr, descr,
2174				    sizeof(pm_cur->diskdev_descr));
2175				break;
2176			}
2177	} else
2178		return -1;
2179	return 0;
2180}
2181
2182/*
2183 * Enable/disable items in the extended partition disk/partition action
2184 * menu
2185 */
2186void
2187pmdiskentry_enable(menudesc *menu, struct part_entry *pe)
2188{
2189	int i;
2190	menu_ent *m;
2191	bool enable;
2192
2193	for (i = 0; i < menu->numopts; i++) {
2194		m = &menu->opts[i];
2195
2196		enable = false;
2197		if (m->opt_name == MSG_unconfig) {
2198			if (pe->type == PM_DISK)
2199				enable = ((struct pm_devs *)pe->dev_ptr)
2200				    ->refdev != NULL;
2201		} else if (m->opt_name == MSG_undo) {
2202			if (pe->type != PM_DISK)
2203				continue;
2204			enable = ((struct pm_devs *)pe->dev_ptr)->unsaved;
2205		} else if (m->opt_name == MSG_switch_parts) {
2206			enable = pm_from_pe(pe)->parts != NULL;
2207		} else {
2208			continue;
2209		}
2210
2211		if (enable)
2212			m->opt_flags &= ~OPT_IGNORE;
2213		else
2214			m->opt_flags |= OPT_IGNORE;
2215	}
2216}
2217
2218/* Detect that partition is in use */
2219int
2220pm_partusage(struct pm_devs *pm_cur, int part_num, int do_del)
2221{
2222	int i, ii, retvalue = 0;
2223	struct disk_part_info info;
2224	part_id id;
2225
2226	if (pm_cur->parts == NULL)
2227		return -1;	/* nothing can be in use */
2228
2229	if (part_num < 0) {
2230		/* Check all partitions on device */
2231		for (id = 0; id < pm_cur->parts->num_part; id++) {
2232			if (!pm_cur->parts->pscheme->get_part_info(
2233			    pm_cur->parts, id, &info))
2234				continue;
2235			if (info.flags & (PTI_SEC_CONTAINER|
2236			    PTI_WHOLE_DISK|PTI_PSCHEME_INTERNAL|
2237			    PTI_RAW_PART))
2238				continue;
2239			retvalue += pm_partusage(pm_cur, id, do_del);
2240		}
2241		return retvalue;
2242	}
2243
2244	id = (part_id)part_num;
2245	if (id >= pm_cur->parts->num_part)
2246		return 0;
2247
2248	for (i = 0; have_cgd && i < MAX_CGD; i++)
2249		if (cgds[i].enabled &&
2250			cgds[i].pm == pm_cur &&
2251			cgds[i].pm_part == id) {
2252			if (do_del) {
2253				cgds[i].pm = NULL;
2254				cgds[i].pm_name[0] = '\0';
2255			}
2256			return 1;
2257		}
2258	for (i = 0; have_raid && i < MAX_RAID; i++)
2259		for (ii = 0; ii < MAX_IN_RAID; ii++)
2260			if (raids[i].enabled &&
2261				raids[i].comp[ii].parts == pm_cur->parts &&
2262				raids[i].comp[ii].id == id) {
2263					if (do_del)
2264						raids[i].comp[ii].parts = NULL;
2265					return 1;
2266			}
2267	for (i = 0; have_lvm && i < MAX_LVM_VG; i++)
2268		for (ii = 0; ii < MAX_LVM_PV; ii++)
2269			if (lvms[i].enabled &&
2270				lvms[i].pv[ii].pm == pm_cur &&
2271				lvms[i].pv[ii].pm_part == id) {
2272					if (do_del)
2273						lvms[i].pv[ii].pm = NULL;
2274					return 1;
2275			}
2276
2277	return 0;
2278}
2279
2280static void
2281pm_destroy_one(struct pm_devs *pm_i)
2282{
2283	part_id i;
2284
2285	if (pm_i->parts != NULL) {
2286		if (pm_i->mounted != NULL) {
2287			for (i = 0; i < pm_i->parts->num_part; i++)
2288				free(pm_i->mounted[i]);
2289		}
2290
2291		pm_i->parts->pscheme->free(pm_i->parts);
2292	}
2293	free(pm_i);
2294}
2295
2296/* Clean up removed devices */
2297static int
2298pm_clean(void)
2299{
2300	int count = 0;
2301	struct pm_devs *pm_i, *tmp;
2302
2303	SLIST_FOREACH_SAFE(pm_i, &pm_head, l, tmp)
2304		if (! pm_i->found) {
2305			count++;
2306			SLIST_REMOVE(&pm_head, pm_i, pm_devs, l);
2307			pm_destroy_one(pm_i);
2308		}
2309	return count;
2310}
2311
2312/* Free all pm storage */
2313void
2314pm_destroy_all(void)
2315{
2316	struct pm_devs *pm_i, *tmp;
2317
2318	if (pm_new != pm)
2319		pm_destroy_one(pm_new);
2320	SLIST_FOREACH_SAFE(pm_i, &pm_head, l, tmp) {
2321		SLIST_REMOVE(&pm_head, pm_i, pm_devs, l);
2322		pm_destroy_one(pm_i);
2323	}
2324}
2325
2326void
2327pm_set_lvmpv(struct pm_devs *my_pm, part_id pno, bool add)
2328{
2329	size_t i;
2330	struct disk_part_info info;
2331
2332	if (!my_pm->parts->pscheme->get_part_info(my_pm->parts, pno, &info))
2333		return;
2334
2335	if (add) {
2336		for (i = 0; i < __arraycount(lvm_pvs); i++)
2337			if (lvm_pvs[i].pm == NULL && lvm_pvs[i].start == 0)
2338				break;
2339		if (i >= __arraycount(lvm_pvs))
2340			return;
2341		lvm_pvs[i].pm = my_pm;
2342		lvm_pvs[i].start = info.start;
2343		return;
2344	} else {
2345		for (i = 0; i < __arraycount(lvm_pvs); i++)
2346			if (lvm_pvs[i].pm == my_pm &&
2347			    lvm_pvs[i].start == info.start)
2348				break;
2349		if (i >= __arraycount(lvm_pvs))
2350			return;
2351		lvm_pvs[i].pm = NULL;
2352		lvm_pvs[i].start = 0;
2353	}
2354}
2355
2356bool
2357pm_is_lvmpv(struct pm_devs *my_pm, part_id id,
2358    const struct disk_part_info *info)
2359{
2360	size_t i;
2361
2362	for (i = 0; i < __arraycount(lvm_pvs); i++) {
2363		if (lvm_pvs[i].pm != my_pm)
2364			continue;
2365		if (lvm_pvs[i].start == info->start)
2366			return true;
2367	}
2368
2369	return false;
2370}
2371
2372void
2373pm_setfstype(struct pm_devs *pm_cur, part_id id, int fstype, int fs_subtype)
2374{
2375	struct disk_part_info info;
2376
2377	if (!pm_cur->parts->pscheme->get_part_info(pm_cur->parts, id, &info))
2378		return;
2379
2380	info.nat_type = pm_cur->parts->pscheme->get_fs_part_type(PT_root,
2381	    fstype, fs_subtype);
2382	if (info.nat_type == NULL)
2383		return;
2384	info.fs_type = fstype;
2385	info.fs_sub_type = fs_subtype;
2386	pm_cur->parts->pscheme->set_part_info(pm_cur->parts, id, &info, NULL);
2387}
2388
2389static void
2390pm_select(struct pm_devs *pm_devs_in)
2391{
2392	pm = pm_devs_in;
2393	if (logfp)
2394		(void)fprintf(logfp, "Partman device: %s\n", pm->diskdev);
2395}
2396
2397void
2398pm_rename(struct pm_devs *pm_cur)
2399{
2400#if 0	// XXX - convert to custom attribute or handle similar
2401	pm_select(pm_cur);
2402	msg_prompt_win(MSG_packname, -1, 18, 0, 0, pm_cur->bsddiskname,
2403		pm_cur->bsddiskname, sizeof pm_cur->bsddiskname);
2404#ifndef NO_DISKLABEL
2405	(void) savenewlabel(pm_cur->bsdlabel, MAXPARTITIONS);
2406#endif
2407#endif
2408}
2409
2410int
2411pm_editpart(int part_num)
2412{
2413	struct partition_usage_set pset = {};
2414
2415	usage_set_from_parts(&pset, pm->parts);
2416	edit_ptn(&(struct menudesc){.cursel = part_num}, &pset);
2417	free_usage_set(&pset);
2418	if (checkoverlap(pm->parts)) {
2419		hit_enter_to_continue(MSG_cantsave, NULL);
2420		return -1;
2421	}
2422	pm->unsaved = 1;
2423	return 0;
2424}
2425
2426/* Safe erase of disk */
2427void
2428pm_shred(struct part_entry *pe, int shredtype)
2429{
2430	const char *srcdev;
2431	char dev[SSTRSIZE];
2432	struct pm_devs *my_pm;
2433
2434	my_pm = pe->dev_ptr;
2435	if (pe->type == PM_DISK) {
2436		snprintf(dev, sizeof dev,
2437		    _PATH_DEV "r%s%c", my_pm->diskdev, 'a' + RAW_PART);
2438		if (pe->parts != NULL) {
2439			pe->parts->pscheme->free(pe->parts);
2440			pe->parts = NULL;
2441			my_pm->parts = NULL;
2442		}
2443	} else if (pe->type == PM_PART) {
2444		pe->parts->pscheme->get_part_device(pe->parts, pe->id,
2445		    dev, sizeof dev, NULL, raw_dev_name, true, true);
2446	}
2447
2448	switch (shredtype) {
2449		case SHRED_ZEROS:
2450			srcdev = _PATH_DEVZERO;
2451			break;
2452		case SHRED_RANDOM:
2453			srcdev = _PATH_URANDOM;
2454			break;
2455		default:
2456			return;
2457	}
2458	run_program(RUN_DISPLAY | RUN_PROGRESS,
2459	    "progress -f %s -b 1m dd bs=1m of=%s", srcdev, dev);
2460	pm_partusage(my_pm, -1, 1);
2461}
2462
2463#if 0 // XXX
2464static int
2465pm_mountall_sort(const void *a, const void *b)
2466{
2467	return strcmp(mnts[*(const int *)a].on, mnts[*(const int *)b].on);
2468}
2469#endif
2470
2471#if 0	// XXX
2472/* Mount all available partitions */
2473static int
2474pm_mountall(void)
2475{
2476	int num_devs = 0;
2477	int mnts_order[MAX_MNTS];
2478	int i, ii, error, ok;
2479	char dev[SSTRSIZE]; dev[0] = '\0';
2480	struct pm_devs *pm_i;
2481
2482	localfs_dev[0] = '\0';
2483	if (mnts == NULL)
2484		mnts = calloc(MAX_MNTS, sizeof(*mnts));
2485
2486	SLIST_FOREACH(pm_i, &pm_head, l) {
2487		ok = 0;
2488		for (i = 0; i < MAXPARTITIONS; i++) {
2489			if (!(pm_i->bsdlabel[i].pi_flags & PIF_MOUNT &&
2490					pm_i->bsdlabel[i].mnt_opts != NULL))
2491				continue;
2492			mnts[num_devs].mnt_opts = pm_i->bsdlabel[i].mnt_opts;
2493			if (strlen(pm_i->bsdlabel[i].mounted) > 0) {
2494				/* Device is already mounted. So, doing mount_null */
2495				strlcpy(mnts[num_devs].dev, pm_i->bsdlabel[i].mounted, MOUNTLEN);
2496				mnts[num_devs].mnt_opts = "-t null";
2497			} else {
2498				pm_getdevstring(dev, SSTRSIZE, pm_i, i);
2499				snprintf(mnts[num_devs].dev, STRSIZE, "/dev/%s", dev);
2500			}
2501			mnts[num_devs].on = pm_i->bsdlabel[i].pi_mount;
2502			if (strcmp(pm_i->bsdlabel[i].pi_mount, "/") == 0) {
2503				/* Use disk with / as a default if the user has
2504				the sets on a local disk */
2505				strlcpy(localfs_dev, pm_i->diskdev, SSTRSIZE);
2506			}
2507			num_devs++;
2508			ok = 1;
2509		}
2510		if (ok)
2511			md_pre_mount(NULL, 0);
2512	}
2513	if (strlen(localfs_dev) < 1) {
2514		hit_enter_to_continue(MSG_noroot, NULL);
2515		return -1;
2516	}
2517	for (i = 0; i < num_devs; i++)
2518		mnts_order[i] = i;
2519	qsort(mnts_order, num_devs, sizeof mnts_order[0], pm_mountall_sort);
2520
2521	for (i = 0; i < num_devs; i++) {
2522		ii = mnts_order[i];
2523		make_target_dir(mnts[ii].on);
2524		error = target_mount_do(mnts[ii].mnt_opts, mnts[ii].dev, mnts[ii].on);
2525		if (error)
2526			return error;
2527	}
2528	return 0;
2529}
2530#endif
2531
2532/* Mount partition bypassing ordinary order */
2533static int
2534pm_mount(struct pm_devs *pm_cur, int part_num)
2535{
2536	int error = 0;
2537#if 0 // XXX
2538	char buf[MOUNTLEN];
2539
2540	if (strlen(pm_cur->bsdlabel[part_num].mounted) > 0)
2541		return 0;
2542
2543	snprintf(buf, sizeof(buf), "/tmp/%s%c", pm_cur->diskdev,
2544	    part_num + 'a');
2545	if (! dir_exists_p(buf))
2546		run_program(RUN_DISPLAY | RUN_PROGRESS, "/bin/mkdir -p %s", buf);
2547	if (pm_cur->bsdlabel[part_num].pi_flags & PIF_MOUNT &&
2548		pm_cur->bsdlabel[part_num].mnt_opts != NULL &&
2549		strlen(pm_cur->bsdlabel[part_num].mounted) < 1)
2550		error += run_program(RUN_DISPLAY | RUN_PROGRESS, "/sbin/mount %s /dev/%s%c %s",
2551				pm_cur->bsdlabel[part_num].mnt_opts,
2552				pm_cur->diskdev, part_num + 'a', buf);
2553
2554	if (error)
2555		pm_cur->bsdlabel[part_num].mounted[0] = '\0';
2556	else {
2557		strlcpy(pm_cur->bsdlabel[part_num].mounted, buf, MOUNTLEN);
2558		pm_cur->blocked++;
2559	}
2560#endif
2561	return error;
2562}
2563
2564void
2565pm_umount(struct pm_devs *pm_cur, int part_num)
2566{
2567	char buf[SSTRSIZE]; buf[0] = '\0';
2568	part_id id;
2569
2570	if (part_num < 0)
2571		return;
2572	id = (part_id)part_num;
2573
2574	pm_cur->parts->pscheme->get_part_device(pm_cur->parts, id, buf,
2575	    sizeof buf, NULL, plain_name, false, true);
2576
2577	if (run_program(RUN_DISPLAY | RUN_PROGRESS,
2578			"umount -f /dev/%s", buf) == 0) {
2579		free(pm_cur->mounted[id]);
2580		pm_cur->mounted[id] = NULL;
2581		if (pm_cur->blocked > 0)
2582			pm_cur->blocked--;
2583	}
2584}
2585
2586int
2587pm_unconfigure(struct pm_devs *pm_cur)
2588{
2589	int error = 0;
2590	if (! strncmp(pm_cur->diskdev, "cgd", 3)) {
2591 		error = run_program(RUN_DISPLAY | RUN_PROGRESS, "cgdconfig -u %s", pm_cur->diskdev);
2592 		if (! error && pm_cur->refdev != NULL) {
2593			((struct cgd_desc*)pm_cur->refdev)->pm->blocked--;
2594			((struct cgd_desc*)pm_cur->refdev)->blocked = 0;
2595 		}
2596 	} else if (! strncmp(pm_cur->diskdev, "vnd", 3)) {
2597		error = run_program(RUN_DISPLAY | RUN_PROGRESS, "vndconfig -u %s", pm_cur->diskdev);
2598 		if (! error && pm_cur->refdev != NULL) {
2599			((struct vnd_desc*)pm_cur->refdev)->pm->blocked--;
2600			((struct vnd_desc*)pm_cur->refdev)->blocked = 0;
2601 		}
2602	} else if (! strncmp(pm_cur->diskdev, "raid", 4)) {
2603		error = run_program(RUN_DISPLAY | RUN_PROGRESS, "raidctl -u %s", pm_cur->diskdev);
2604		if (! error && pm_cur->refdev != NULL) {
2605			((struct raid_desc*)pm_cur->refdev)->blocked = 0;
2606#if 0 // XXX
2607			for (i = 0; i < MAX_IN_RAID; i++)
2608				if (((struct raid_desc*)pm_cur->refdev)->comp[i].parts != NULL)
2609					((raids_t*)pm_cur->refdev)->pm[i]->blocked--;
2610#endif
2611		}
2612	} else if (! strncmp(pm_cur->diskdev, "dk", 2)) {
2613		if (pm_cur->refdev == NULL)
2614			return -2;
2615		/* error = */
2616		run_program(RUN_DISPLAY | RUN_PROGRESS, "dkctl %s delwedge %s",
2617			((struct pm_devs*)pm_cur->refdev)->diskdev, pm_cur->diskdev);
2618#if 0 // XXX
2619		if (! error) {
2620			if (pm_cur->refdev != NULL && ((struct pm_devs*)pm_cur->refdev)->blocked > 0)
2621				((struct pm_devs*)pm_cur->refdev)->blocked--;
2622			sscanf(pm_cur->diskdev, "dk%d", &num);
2623			if (num >= 0 && num < MAX_WEDGES)
2624				wedges[num].allocated = 0;
2625		}
2626#endif
2627	} /* XXX: lvm */
2628	else
2629		error = run_program(RUN_DISPLAY | RUN_PROGRESS, "eject -t disk /dev/%sd",
2630			pm_cur->diskdev);
2631	if (!error)
2632		pm_cur->found = 0;
2633	return error;
2634}
2635
2636/* Last checks before leaving partition manager */
2637#if 0
2638static int
2639pm_lastcheck(void)
2640{
2641	FILE *file_tmp = fopen(concat_paths(targetroot_mnt, "/etc/fstab"), "r");
2642	if (file_tmp == NULL)
2643		return 1;
2644	fclose(file_tmp);
2645	return 0;
2646}
2647#endif
2648
2649/* Are there unsaved changes? */
2650static int
2651pm_needsave(void)
2652{
2653	struct pm_devs *pm_i;
2654	SLIST_FOREACH(pm_i, &pm_head, l)
2655		if (pm_i->unsaved) {
2656			/* Oops, we have unsaved changes */
2657			pm_changed = 1;
2658			msg_display(MSG_saveprompt);
2659			return ask_yesno(NULL);
2660		}
2661	return 0;
2662}
2663
2664/* Write all changes to disk */
2665static int
2666pm_commit(menudesc *m, void *arg)
2667{
2668	int retcode;
2669	struct pm_devs *pm_i;
2670	struct disk_partitions *secondary;
2671
2672	pm_retvalue = -1;
2673	SLIST_FOREACH(pm_i, &pm_head, l) {
2674		if (! pm_i->unsaved)
2675			continue;
2676		if (pm_i->parts == NULL) {
2677			pm_i->unsaved = false;
2678			continue;
2679		}
2680		if (!pm_i->parts->pscheme->write_to_disk(pm_i->parts)) {
2681			if (logfp)
2682				fprintf(logfp, "partitining error %s\n",
2683				    pm_i->diskdev);
2684			return -1;
2685		}
2686		if (pm_i->parts->pscheme->secondary_scheme != NULL) {
2687			secondary = pm_i->parts->pscheme->
2688			    secondary_partitions(pm_i->parts, -1, false);
2689			if (secondary != NULL) {
2690				if (!secondary->pscheme->write_to_disk(
2691				    secondary)) {
2692					if (logfp)
2693						fprintf(logfp,
2694						    "partitining error %s\n",
2695						    pm_i->diskdev);
2696					return -1;
2697				}
2698			}
2699		}
2700	}
2701
2702	/* Call all functions that may create new devices */
2703	if ((retcode = pm_raid_commit()) != 0) {
2704		if (logfp)
2705			fprintf(logfp, "RAIDframe configuring error #%d\n", retcode);
2706		return -1;
2707	}
2708	if ((retcode = pm_cgd_commit()) != 0) {
2709		if (logfp)
2710			fprintf(logfp, "CGD configuring error #%d\n", retcode);
2711		return -1;
2712	}
2713	if ((retcode = pm_lvm_commit()) != 0) {
2714		if (logfp)
2715			fprintf(logfp, "LVM configuring error #%d\n", retcode);
2716		return -1;
2717	}
2718	if ((retcode = pm_vnd_commit()) != 0) {
2719		if (logfp)
2720			fprintf(logfp, "VND configuring error #%d\n", retcode);
2721		return -1;
2722	}
2723	if (m != NULL && arg != NULL)
2724		pm_do_upddevlist(m, arg);
2725	if (logfp)
2726		fflush (logfp);
2727
2728	pm_retvalue = 0;
2729	return 0;
2730}
2731
2732#if 0 // XXX
2733static int
2734pm_savebootsector(void)
2735{
2736	struct pm_devs *pm_i;
2737	SLIST_FOREACH(pm_i, &pm_head, l)
2738		if (pm_i->bootable) {
2739			if (! strncmp("raid", pm_i->diskdev, 4))
2740				if (run_program(RUN_DISPLAY | RUN_PROGRESS,
2741					"raidctl -v -A root %s", pm_i->diskdev) != 0) {
2742					if (logfp)
2743						fprintf(logfp, "Error writing RAID bootsector to %s\n",
2744							pm_i->diskdev);
2745					continue;
2746				}
2747			if (pm_i->no_part) {
2748				if (pm_i->rootpart < 0 ||
2749					run_program(RUN_DISPLAY | RUN_PROGRESS,
2750					"gpt biosboot -i %d %s",
2751					pm_i->rootpart + 1, pm_i->diskdev) != 0) {
2752			 		if (logfp)
2753						fprintf(logfp, "Error writing GPT bootsector to %s\n",
2754							pm_i->diskdev);
2755					continue;
2756				}
2757			} else {
2758				pm_select(pm_i);
2759			 	if (
2760#ifndef NO_DISKLABEL
2761				    !check_partitions(pm_i, pm_i->parts) ||
2762#endif
2763				    md_post_newfs() != 0) {
2764		 			if (logfp)
2765						fprintf(logfp, "Error writing bootsector to %s\n",
2766							pm_i->diskdev);
2767					continue;
2768				}
2769			}
2770		}
2771	return 0;
2772}
2773#endif
2774
2775/* Function for 'Enter'-menu */
2776static int
2777pm_submenu(menudesc *m, void *arg)
2778{
2779	struct pm_devs *pm_cur = NULL;
2780	pm_retvalue = m->cursel + 1;
2781	struct part_entry *cur_pe = (struct part_entry *)arg + m->cursel;
2782
2783	switch (cur_pe->type) {
2784		case PM_DISK:
2785		case PM_PART:
2786		case PM_SPEC:
2787			if (cur_pe->dev_ptr != NULL) {
2788				pm_cur = cur_pe->dev_ptr;
2789				if (pm_cur == NULL)
2790					return -1;
2791				if (pm_cur->blocked) {
2792					clear();
2793					refresh();
2794					msg_display(MSG_wannaunblock);
2795					if (!ask_noyes(NULL))
2796						return -2;
2797					pm_cur->blocked = 0;
2798				}
2799				pm_select(pm_cur);
2800			}
2801		default:
2802			break;
2803	}
2804
2805	switch (cur_pe->type) {
2806		case PM_DISK:
2807			process_menu(MENU_pmdiskentry, cur_pe);
2808			break;
2809		case PM_PART:
2810			process_menu(MENU_pmpartentry, cur_pe);
2811			break;
2812		case PM_SPEC:
2813			process_menu(MENU_pmpartentry, cur_pe);
2814			break;
2815		case PM_RAID:
2816			pm_edit(PMR_MENU_END, pm_raid_edit_menufmt,
2817			    pm_raid_set_value, pm_raid_check, pm_raid_init,
2818			    NULL, cur_pe->dev_ptr, 0, &raids_t_info);
2819			break;
2820		case PM_VND:
2821			return pm_edit(PMV_MENU_END, pm_vnd_edit_menufmt,
2822			    pm_vnd_set_value, pm_vnd_check, pm_vnd_init,
2823			    NULL, cur_pe->dev_ptr, 0, &vnds_t_info);
2824		case PM_CGD:
2825			pm_cgd_edit_old(cur_pe);
2826			break;
2827		case PM_LVM:
2828			return pm_edit(PML_MENU_END, pm_lvm_edit_menufmt,
2829			    pm_lvm_set_value, pm_lvm_check, pm_lvm_init,
2830			    NULL, cur_pe->dev_ptr, 0, &lvms_t_info);
2831		case PM_LVMLV:
2832			return pm_edit(PMLV_MENU_END, pm_lvmlv_edit_menufmt,
2833			    pm_lvmlv_set_value, pm_lvmlv_check, pm_lvmlv_init,
2834			    NULL, cur_pe->dev_ptr,
2835			    cur_pe->dev_ptr_delta, &lv_t_info);
2836	}
2837	return 0;
2838}
2839
2840/* Functions that generate menu entries text */
2841static void
2842pm_menufmt(menudesc *m, int opt, void *arg)
2843{
2844	const char *dev_status = "";
2845	char buf[STRSIZE], dev[STRSIZE];
2846	part_id part_num = ((struct part_entry *)arg)[opt].id;
2847	struct pm_devs *pm_cur = ((struct part_entry *)arg)[opt].dev_ptr;
2848	struct disk_partitions *parts = ((struct part_entry *)arg)[opt].parts;
2849	struct disk_part_info info;
2850	const char *mount_point, *fstype;
2851
2852	switch (((struct part_entry *)arg)[opt].type) {
2853		case PM_DISK:
2854			if (pm_cur->blocked)
2855				dev_status = msg_string(MSG_pmblocked);
2856			else if (! pm_cur->unsaved)
2857				dev_status = msg_string(MSG_pmunchanged);
2858			else
2859				dev_status = msg_string(MSG_pmused);
2860			wprintw(m->mw, "%-43.42s %25.24s",
2861				pm_cur->diskdev_descr,
2862				dev_status);
2863			break;
2864		case PM_PART:
2865			if (parts->pscheme->get_part_device == NULL ||
2866			    !parts->pscheme->get_part_device(
2867				parts,  part_num,
2868				dev, sizeof dev, NULL, plain_name, false,
2869				true))
2870					strcpy(dev, "-");
2871
2872			parts->pscheme->get_part_info(parts,
2873			    part_num, &info);
2874			if (pm_cur->mounted != NULL &&
2875			    pm_cur->mounted[part_num] != NULL &&
2876			    pm_cur->mounted[part_num][0] != 0)
2877				mount_point = msg_string(MSG_pmmounted);
2878			else
2879				mount_point = msg_string(MSG_pmunused);
2880			fstype = getfslabelname(info.fs_type,
2881			    info.fs_sub_type);
2882			if (info.last_mounted != NULL) {
2883				snprintf(buf, STRSIZE, "%s (%s) %s",
2884				    info.last_mounted, fstype,
2885				     mount_point);
2886				pm_fmt_disk_line(m->mw, dev, buf,
2887				    info.size, NULL);
2888			} else {
2889				if (fstype != NULL) {
2890					strlcat(dev, " (", sizeof(dev));
2891					strlcat(dev, fstype, sizeof(dev));
2892					strlcat(dev, ")", sizeof(dev));
2893				}
2894				pm_fmt_disk_line(m->mw, dev, NULL,
2895				    info.size, NULL);
2896			}
2897			break;
2898		case PM_SPEC:
2899			/* XXX ? */
2900			pm_fmt_disk_line(m->mw, pm_cur->diskdev_descr, NULL,
2901			    pm_cur->dlsize, NULL);
2902			break;
2903		case PM_RAID:
2904			pm_raid_menufmt(m, opt, arg);
2905			break;
2906		case PM_VND:
2907			pm_vnd_menufmt(m, opt, arg);
2908			break;
2909		case PM_CGD:
2910			pm_cgd_menufmt(m, opt, arg);
2911			break;
2912		case PM_LVM:
2913			pm_lvm_menufmt(m, opt, arg);
2914			break;
2915		case PM_LVMLV:
2916			pm_lvmlv_menufmt(m, opt, arg);
2917			break;
2918	}
2919}
2920
2921/* Submenu for RAID/LVM/CGD/VND */
2922static void
2923pm_upddevlist_adv(menudesc *m, void *arg, int *i,
2924	pm_upddevlist_adv_t *d)
2925{
2926	int ii;
2927	if (d->create_msg != NULL) {
2928		/* We want to have menu entry that creates a new device */
2929		((struct part_entry *)arg)[*i].type = d->pe_type;
2930		((struct part_entry *)arg)[*i].dev_ptr = NULL;
2931		((struct part_entry *)arg)[*i].dev_ptr_delta = d->s->parent_size * d->sub_num;
2932		m->opts[(*i)++] = (struct menu_ent) {
2933			.opt_name = d->create_msg,
2934			.opt_action = pm_submenu,
2935		};
2936	}
2937	for (ii = 0; ii < d->s->max; ii++) {
2938		if (d->s->entry_enabled == NULL ||
2939			d->s->entry_blocked == NULL ||
2940			*(int*)((char*)d->s->entry_enabled + d->s->entry_size * ii +
2941				d->s->parent_size * d->sub_num) == 0 ||
2942			*(int*)((char*)d->s->entry_blocked + d->s->entry_size * ii +
2943				d->s->parent_size * d->sub_num) != 0)
2944			continue;
2945		/* We have a entry for displaying */
2946		pm_changed = 1;
2947		m->opts[*i] = (struct menu_ent) {
2948			.opt_name = NULL,
2949			.opt_action = pm_submenu,
2950		};
2951		((struct part_entry *)arg)[*i].type = d->pe_type;
2952		((struct part_entry *)arg)[*i].dev_ptr = (char*)d->s->entry_first +
2953			d->s->entry_size * ii + d->s->parent_size * d->sub_num;
2954		(*i)++;
2955		/* We should show submenu for current entry */
2956		if (d->sub != NULL) {
2957			d->sub->sub_num = ii;
2958			pm_upddevlist_adv(m, arg, i, d->sub);
2959		}
2960	}
2961}
2962
2963/* Update partman main menu with devices list */
2964static int
2965pm_upddevlist(menudesc *m, void *arg, struct install_partition_desc *install)
2966{
2967	int i = 0;
2968	size_t ii;
2969	struct pm_devs *pm_i;
2970	struct disk_partitions *secondary;
2971	const struct disk_partitioning_scheme *ps;
2972	struct disk_part_info info;
2973
2974	if (arg != NULL)
2975		pm_retvalue = m->cursel + 1;
2976
2977	pm_changed = 0;
2978	/* Mark all devices as not found */
2979	SLIST_FOREACH(pm_i, &pm_head, l) {
2980		if (pm_i->parts != NULL && !pm_i->unsaved) {
2981			pm_i->parts->pscheme->free(pm_i->parts);
2982			pm_i->parts = NULL;
2983		}
2984		if (pm_i->found > 0)
2985			pm_i->found = 0;
2986	}
2987
2988	/* Detect all present devices */
2989	(void)find_disks("partman", false);
2990	if (have_lvm)
2991		pm_lvm_find();
2992	pm_clean();
2993
2994	if (m == NULL || arg == NULL)
2995		return -1;
2996
2997	SLIST_FOREACH(pm_i, &pm_head, l) {
2998		struct part_entry *cur_entry = ((struct part_entry *)arg)+i;
2999		memset(&m->opts[i], 0, sizeof m->opts[i]);
3000		m->opts[i].opt_action = pm_submenu;
3001		cur_entry->dev_ptr = pm_i;
3002		cur_entry->id = NO_PART;
3003		cur_entry->install = install;
3004		if (pm_i->no_part)
3005			cur_entry->type = PM_SPEC;
3006		else {
3007			ps = pm_i->parts != NULL ? pm_i->parts->pscheme : NULL;
3008			secondary = NULL;
3009
3010			cur_entry->type = PM_DISK;
3011
3012			for (ii = 0; pm_i->parts != NULL &&
3013			    ii < pm_i->parts->num_part; ii++) {
3014				if (!ps->get_part_info(
3015				    pm_i->parts, ii, &info))
3016					continue;
3017				if (info.flags & PTI_SEC_CONTAINER) {
3018					if (secondary == NULL &&
3019					    ps->secondary_scheme != NULL)
3020						secondary = ps->
3021						    secondary_partitions(
3022						    pm_i->parts,
3023						    info.start, false);
3024					continue;
3025				}
3026				if (info.flags & (PTI_WHOLE_DISK|
3027				    PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
3028					continue;
3029				if (info.fs_type == FS_UNUSED)
3030					continue;
3031				if (i >= MAX_ENTRIES)
3032					break;
3033				i++;
3034				cur_entry = ((struct part_entry *)arg)+i;
3035				memset(&m->opts[i], 0, sizeof m->opts[i]);
3036				m->opts[i].opt_action = pm_submenu;
3037				cur_entry->parts = pm_i->parts;
3038				cur_entry->dev_ptr = pm_i;
3039				cur_entry->install = install;
3040				cur_entry->id = ii;
3041				cur_entry->type = PM_PART;
3042			}
3043
3044			for (ii = 0; secondary != NULL &&
3045			    ii < secondary->num_part; ii++) {
3046				if (!secondary->pscheme->get_part_info(
3047				    secondary, ii, &info))
3048					continue;
3049				if (info.flags & (PTI_WHOLE_DISK|
3050				    PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
3051					continue;
3052				if (info.fs_type == FS_UNUSED)
3053					continue;
3054				if (i >= MAX_ENTRIES)
3055					break;
3056				i++;
3057				cur_entry = ((struct part_entry *)arg)+i;
3058				memset(&m->opts[i], 0, sizeof m->opts[i]);
3059				m->opts[i].opt_action = pm_submenu;
3060				cur_entry->parts = secondary;
3061				cur_entry->dev_ptr = pm_i;
3062				cur_entry->install = install;
3063				cur_entry->id = ii;
3064				cur_entry->type = PM_PART;
3065			}
3066		}
3067		i++;
3068	}
3069	for (ii = 0; ii <= (size_t)i; ii++) {
3070		m->opts[ii].opt_flags = OPT_EXIT;
3071	}
3072	if (have_cgd) {
3073		pm_upddevlist_adv(m, arg, &i,
3074		    &(pm_upddevlist_adv_t) {MSG_create_cgd, PM_CGD,
3075		    &cgds_t_info, 0, NULL});
3076	}
3077	pm_upddevlist_adv(m, arg, &i,
3078	    &(pm_upddevlist_adv_t) {MSG_create_vnd, PM_VND,
3079	    &vnds_t_info, 0, NULL});
3080	if (have_lvm) {
3081		pm_upddevlist_adv(m, arg, &i,
3082		    &(pm_upddevlist_adv_t) {MSG_create_vg, PM_LVM,
3083		    &lvms_t_info, 0,
3084		    &(pm_upddevlist_adv_t) {MSG_create_lv, PM_LVMLV,
3085		    &lv_t_info, 0,
3086		    NULL}});
3087	}
3088	if (have_raid) {
3089		pm_upddevlist_adv(m, arg, &i,
3090		    &(pm_upddevlist_adv_t) {MSG_create_raid, PM_RAID, &raids_t_info, 0, NULL});
3091	}
3092
3093	m->opts[i++] = (struct menu_ent) {
3094		.opt_name = MSG_updpmlist,
3095		.opt_action = pm_do_upddevlist,
3096	};
3097	m->opts[i  ] = (struct menu_ent) {
3098		.opt_name = MSG_savepm,
3099		.opt_action = pm_commit,
3100	};
3101
3102	if (pm_retvalue >= 0)
3103		m->cursel = pm_retvalue - 1;
3104	return i;
3105}
3106
3107static void
3108pm_menuin(menudesc *m, void *arg)
3109{
3110	if (pm_cursel > m->numopts)
3111		m->cursel = m->numopts;
3112	else if (pm_cursel < 0)
3113		m->cursel = 0;
3114	else
3115		m->cursel = pm_cursel;
3116}
3117
3118static void
3119pm_menuout(menudesc *m, void *arg)
3120{
3121	pm_cursel = m->cursel;
3122}
3123
3124/* Main partman function */
3125int
3126partman(struct install_partition_desc *install)
3127{
3128	int menu_no, menu_num_entries;
3129	static int firstrun = 1;
3130	menu_ent menu_entries[MAX_ENTRIES+6];
3131	struct part_entry args[MAX_ENTRIES] = { 0 };
3132
3133	if (firstrun) {
3134		check_available_binaries();
3135
3136		if (!have_raid)
3137			remove_raid_options();
3138		else if (!(raids = calloc(MAX_RAID, sizeof(*raids))))
3139			have_raid = 0;
3140
3141#define remove_vnd_options() (void)0
3142		if (!have_vnd)
3143			remove_vnd_options();
3144		else if (!(vnds = calloc(MAX_VND, sizeof(*vnds))))
3145			have_vnd = 0;
3146
3147		if (!have_cgd)
3148			remove_cgd_options();
3149		else if (!(cgds = calloc(MAX_CGD, sizeof(*cgds))))
3150			have_cgd = 0;
3151
3152		if (!have_lvm)
3153			remove_lvm_options();
3154		else if (!(lvms = calloc(MAX_LVM_VG, sizeof(*lvms))))
3155			have_lvm = 0;
3156
3157		raids_t_info = (structinfo_t) {
3158			.max = MAX_RAID,
3159			.entry_size = sizeof raids[0],
3160			.entry_first = &raids[0],
3161			.entry_enabled = &(raids[0].enabled),
3162			.entry_blocked = &(raids[0].blocked),
3163			.entry_node = &(raids[0].node),
3164		};
3165		vnds_t_info = (structinfo_t) {
3166			.max = MAX_VND,
3167			.entry_size = sizeof vnds[0],
3168			.entry_first = &vnds[0],
3169			.entry_enabled = &(vnds[0].enabled),
3170			.entry_blocked = &(vnds[0].blocked),
3171			.entry_node = &(vnds[0].node),
3172		};
3173		cgds_t_info = (structinfo_t) {
3174			.max = MAX_CGD,
3175			.entry_size = sizeof cgds[0],
3176			.entry_first = &cgds[0],
3177			.entry_enabled = &(cgds[0].enabled),
3178			.entry_blocked = &(cgds[0].blocked),
3179			.entry_node = &(cgds[0].node),
3180		};
3181		lvms_t_info = (structinfo_t) {
3182			.max = MAX_LVM_VG,
3183			.entry_size = sizeof lvms[0],
3184			.entry_first = &lvms[0],
3185			.entry_enabled = &(lvms[0].enabled),
3186			.entry_blocked = &(lvms[0].blocked),
3187			.entry_node = NULL,
3188		};
3189		lv_t_info = (structinfo_t) {
3190			.max = MAX_LVM_LV,
3191			.entry_size = sizeof lvms[0].lv[0],
3192			.entry_first = &lvms[0].lv[0],
3193			.entry_enabled = &(lvms[0].lv[0].size),
3194			.entry_blocked = &(lvms[0].lv[0].blocked),
3195			.parent_size = sizeof lvms[0],
3196		};
3197
3198		pm_cursel = 0;
3199		pm_changed = 0;
3200		firstrun = 0;
3201	}
3202
3203	do {
3204		menu_num_entries = pm_upddevlist(&(menudesc){.opts = menu_entries},
3205		    args, install);
3206		menu_no = new_menu(MSG_partman_header,
3207			menu_entries, menu_num_entries+1, 1, 1, 0, 75, /* Fixed width */
3208			MC_ALWAYS_SCROLL | MC_NOBOX | MC_NOCLEAR,
3209			pm_menuin, pm_menufmt, pm_menuout, NULL, MSG_finishpm);
3210		if (menu_no == -1)
3211			pm_retvalue = -1;
3212		else {
3213			pm_retvalue = 0;
3214			clear();
3215			refresh();
3216			process_menu(menu_no, &args);
3217			free_menu(menu_no);
3218		}
3219
3220		if (pm_retvalue == 0 && pm->parts != NULL)
3221			if (pm_needsave())
3222				pm_commit(NULL, NULL);
3223
3224	} while (pm_retvalue > 0);
3225
3226	/* retvalue <0 - error, retvalue ==0 - user quits, retvalue >0 - all ok */
3227	return (pm_retvalue >= 0)?0:-1;
3228}
3229
3230void
3231update_wedges(const char *disk)
3232{
3233	check_available_binaries();
3234
3235	if (!have_dk)
3236		return;
3237
3238	run_program(RUN_SILENT | RUN_ERROR_OK,
3239	    "dkctl %s makewedges", disk);
3240}
3241
3242bool
3243pm_force_parts(struct pm_devs *my_pm)
3244{
3245	if (my_pm == NULL)
3246		return false;
3247	if (my_pm->parts != NULL)
3248		return true;
3249
3250	const struct disk_partitioning_scheme *ps =
3251	    select_part_scheme(my_pm, NULL, false, NULL);
3252	if (ps == NULL)
3253		return false;
3254
3255	struct disk_partitions *parts =
3256	   (*ps->create_new_for_disk)(my_pm->diskdev, 0,
3257	   my_pm->dlsize, false, NULL);
3258	if (parts == NULL)
3259		return false;
3260
3261	my_pm->parts = parts;
3262	if (pm->dlsize > ps->size_limit)
3263		pm->dlsize = ps->size_limit;
3264
3265	return true;
3266}
3267
3268void
3269pm_edit_partitions(struct part_entry *pe)
3270{
3271	struct pm_devs *my_pm = pm_from_pe(pe);
3272	struct partition_usage_set pset = { 0 };
3273	struct disk_partitions *parts, *np;
3274
3275	if (!my_pm)
3276		return;
3277
3278	if (!pm_force_parts(my_pm))
3279		return;
3280	parts = my_pm->parts;
3281
3282	clear();
3283	refresh();
3284
3285	if (my_pm->parts->pscheme->secondary_scheme != NULL) {
3286		if (!edit_outer_parts(my_pm->parts))
3287			goto done;
3288		np = get_inner_parts(parts);
3289		if (np != NULL)
3290			parts = np;
3291	}
3292
3293	if (parts != NULL) {
3294		if (!pe->install ||
3295		    !usage_set_from_install_desc(&pset, pe->install, parts))
3296			usage_set_from_parts(&pset, parts);
3297		edit_and_check_label(my_pm, &pset, false);
3298
3299		if (pe->install)
3300			merge_usage_set_into_install_desc(pe->install,
3301			    &pset);
3302		free_usage_set(&pset);
3303	}
3304
3305done:
3306	pm_partusage(my_pm, -1, -1);
3307	my_pm->unsaved = true;
3308	pm_retvalue = 1;
3309}
3310
3311part_id
3312pm_whole_disk(struct part_entry *pe, int t)
3313{
3314	struct pm_devs *my_pm = pm_from_pe(pe);
3315	struct disk_partitions *parts, *outer;
3316	struct disk_part_info info, oinfo;
3317	struct disk_part_free_space space;
3318	daddr_t align;
3319	int fst;
3320	struct partition_usage_set pset = { 0 };
3321	part_id new_part, id;
3322	size_t i, cnt;
3323
3324	if (!my_pm)
3325		return NO_PART;
3326
3327	if (!pm_force_parts(my_pm))
3328		return NO_PART;
3329
3330	parts = my_pm->parts;
3331	parts->pscheme->delete_all_partitions(parts);
3332	if (parts->pscheme->secondary_scheme != NULL) {
3333		outer = parts;
3334		parts = parts->pscheme->secondary_partitions(outer,
3335		    0, true);
3336		if (parts == NULL) {
3337			parts = outer;
3338		} else {
3339			if (outer->pscheme->write_to_disk(outer))
3340				my_pm->parts = parts;
3341		}
3342	}
3343
3344	align = parts->pscheme->get_part_alignment(parts);
3345
3346	memset(&info, 0, sizeof info);
3347	switch (t) {
3348	case SY_NEWRAID:
3349		fst = FS_RAID;
3350		break;
3351	case SY_NEWLVM:
3352		fst = FS_BSDFFS;
3353		break;
3354	case SY_NEWCGD:
3355		fst = FS_CGD;
3356		break;
3357	default:
3358		assert(false);
3359		return NO_PART;
3360	}
3361	info.nat_type = parts->pscheme->get_fs_part_type(PT_root, fst, 0);
3362	if (info.nat_type != NULL && parts->pscheme->get_default_fstype != NULL)
3363		parts->pscheme->get_default_fstype(info.nat_type,
3364		    &info.fs_type, &info.fs_sub_type);
3365	if (parts->pscheme->get_free_spaces(parts, &space, 1,
3366	    5*align, align, -1, -1) != 1)
3367		return NO_PART;
3368	info.start = space.start;
3369	info.size = space.size;
3370	new_part = parts->pscheme->add_partition(parts, &info, NULL);
3371	if (new_part == NO_PART)
3372		return NO_PART;
3373
3374	parts->pscheme->get_part_info(parts, new_part, &oinfo);
3375
3376	clear();
3377	refresh();
3378
3379	usage_set_from_parts(&pset, parts);
3380	edit_and_check_label(my_pm, &pset, false);
3381	free_usage_set(&pset);
3382
3383	/*
3384	 * Try to match our new partition after user edit
3385	 */
3386	new_part = NO_PART;
3387	for (cnt = i = 0; i < parts->num_part; i++) {
3388		if (!parts->pscheme->get_part_info(parts,i, &info))
3389			continue;
3390		if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK|
3391		    PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
3392			continue;
3393		if (info.nat_type != oinfo.nat_type)
3394			continue;
3395		if (new_part == NO_PART)
3396			new_part = i;
3397		cnt++;
3398	}
3399	if (cnt > 1) {
3400		/* multiple matches, retry matching with start */
3401		id = NO_PART;
3402		for (cnt = i = 0; i < parts->num_part; i++) {
3403			if (!parts->pscheme->get_part_info(parts, i, &info))
3404				continue;
3405			if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK|
3406			    PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
3407				continue;
3408			if (info.nat_type != oinfo.nat_type)
3409				continue;
3410			if (info.start != oinfo.start)
3411				continue;
3412			if (id == NO_PART)
3413				id = i;
3414			cnt++;
3415		}
3416		if (id != NO_PART)
3417			new_part = id;
3418	}
3419
3420	clear();
3421	refresh();
3422
3423	pm_partusage(my_pm, -1, -1);
3424	my_pm->unsaved = true;
3425	pm_retvalue = 1;
3426
3427	return new_part;
3428}
3429
3430struct pm_devs *
3431pm_from_pe(struct part_entry *pe)
3432{
3433	switch (pe->type) {
3434	case PM_DISK:
3435		return pe->dev_ptr;
3436	default:
3437		assert(false);
3438	}
3439	return NULL;
3440}
3441