1/*	$NetBSD: bsddisklabel.c,v 1.72 2023/01/06 18:19:27 martin Exp $	*/
2
3/*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Based on code written by Philip A. Nelson for Piermont Information
8 * Systems Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of Piermont Information Systems Inc. may not be used to endorse
19 *    or promote products derived from this software without specific prior
20 *    written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35/* bsddisklabel.c -- generate standard BSD disklabel */
36/* Included by appropriate arch/XXXX/md.c */
37
38#include <sys/param.h>
39#include <sys/sysctl.h>
40#include <sys/exec.h>
41#include <sys/utsname.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <machine/cpu.h>
45#include <assert.h>
46#include <stdio.h>
47#include <stddef.h>
48#include <util.h>
49#include <dirent.h>
50#include "defs.h"
51#include "md.h"
52#include "defsizes.h"
53#include "endian.h"
54#include "msg_defs.h"
55#include "menu_defs.h"
56
57static size_t fill_ptn_menu(struct partition_usage_set *pset);
58
59/*
60 * The default partition layout.
61 */
62static const struct part_usage_info
63default_parts_init[] =
64{
65	/*
66	 * Pretty complex setup for boot partitions.
67	 * This is copy&pasted below, please keep in sync!
68	 */
69#ifdef PART_BOOT
70	{ .size = PART_BOOT/512,	/* PART_BOOT is in BYTE, not MB! */
71#ifdef PART_BOOT_MOUNT
72	  .mount = PART_BOOT_MOUNT,
73	  .instflags = PUIINST_MOUNT|PUIINST_BOOT,
74#else
75	  .instflags = PUIINST_BOOT,
76#endif
77#ifdef PART_BOOT_TYPE
78	  .fs_type = PART_BOOT_TYPE,
79#if (PART_BOOT_TYPE == FS_MSDOS) || (PART_BOOT_TYPE == FS_EX2FS)
80	  .flags = PUIFLAG_ADD_OUTER,
81#endif
82#endif
83#ifdef PART_BOOT_SUBT
84	  .fs_version = PART_BOOT_SUBT,
85#endif
86	},
87#endif
88
89	/*
90	 * Two more copies of above for _BOOT1 and _BOOT2, please
91	 * keep in sync!
92	 */
93#ifdef PART_BOOT1
94	{ .size = PART_BOOT1/512,	/* PART_BOOT1 is in BYTE, not MB! */
95#ifdef PART_BOOT1_MOUNT
96	  .mount = PART_BOOT1_MOUNT,
97	  .instflags = PUIINST_MOUNT|PUIINST_BOOT,
98#else
99	  .instflags = PUIINST_MOUNT|PUIINST_BOOT,
100#endif
101#ifdef PART_BOOT1_TYPE
102	  .fs_type = PART_BOOT1_TYPE,
103#if (PART_BOOT1_TYPE == FS_MSDOS) || (PART_BOOT1_TYPE == FS_EX2FS)
104	  .flags = PUIFLAG_ADD_OUTER,
105#endif
106#endif
107#ifdef PART_BOOT1_SUBT
108	  .fs_version = PART_BOOT1_SUBT,
109#endif
110	},
111#endif
112#ifdef PART_BOOT2
113	{ .size = PART_BOOT2/512,	/* PART_BOOT2 is in BYTE, not MB! */
114#ifdef PART_BOOT2_MOUNT
115	  .mount = PART_BOOT2_MOUNT,
116	  .instflags = PUIINST_MOUNT|PUIINST_BOOT,
117#else
118	  .instflags = PUIINST_MOUNT|PUIINST_BOOT,
119#endif
120#ifdef PART_BOOT2_TYPE
121	  .fs_type = PART_BOOT2_TYPE,
122#if (PART_BOOT2_TYPE == FS_MSDOS) || (PART_BOOT2_TYPE == FS_EX2FS)
123	  .flags = PUIFLAG_ADD_OUTER,
124#endif
125#endif
126#ifdef PART_BOOT2_SUBT
127	  .fs_version = PART_BOOT2_SUBT,
128#endif
129	},
130#endif
131
132	{ .size = DEFROOTSIZE*(MEG/512), .mount = "/", .type = PT_root,
133	  .flags = PUIFLAG_EXTEND },
134	{
135#if DEFSWAPSIZE > 0
136	  .size = DEFSWAPSIZE*(MEG/512),
137#endif
138	  .type = PT_swap, .fs_type = FS_SWAP },
139#ifdef HAVE_TMPFS
140	{ .type = PT_root, .mount = "/tmp", .fs_type = FS_TMPFS,
141	  .flags = PUIFLG_JUST_MOUNTPOINT },
142#else
143	{ .type = PT_root, .mount = "/tmp", .fs_type = FS_MFS,
144	  .flags = PUIFLG_JUST_MOUNTPOINT },
145#endif
146	{ .def_size = DEFUSRSIZE*(MEG/512), .mount = "/usr", .type = PT_root,
147	  .fs_type = FS_BSDFFS, .fs_version = 3 },
148	{ .def_size = DEFVARSIZE*(MEG/512), .mount = "/var", .type = PT_root,
149	  .fs_type = FS_BSDFFS, .fs_version = 3 },
150};
151
152static const char size_separator[] =
153    "----------------------------------- - --------------------";
154static char size_menu_title[STRSIZE];
155static char size_menu_exit[MENUSTRSIZE];
156
157static void
158set_pset_exit_str(struct partition_usage_set *pset)
159{
160	char *str, num[25];
161	const char *args[2];
162	bool overrun;
163	daddr_t free_space = pset->cur_free_space;
164
165	/* format exit string */
166	overrun = free_space < 0;
167	if (overrun)
168		free_space = -free_space;
169
170	snprintf(num, sizeof(num), "%" PRIu64, free_space / sizemult);
171	args[0] = num;
172	args[1] = multname;
173	str = str_arg_subst(
174	    msg_string(overrun ? MSG_fssizesbad : MSG_fssizesok),
175	    2, args);
176	strlcpy(size_menu_exit, str, sizeof(size_menu_exit));
177	free(str);
178}
179
180static void
181draw_size_menu_header(menudesc *m, void *arg)
182{
183	struct partition_usage_set *pset = arg;
184	size_t i;
185	char col1[70], desc[MENUSTRSIZE];
186	bool need_ext = false, need_existing = false;
187
188	msg_display(MSG_ptnsizes);
189
190	for (i = 0; i < pset->num; i++) {
191		if (pset->infos[i].flags & PUIFLG_IS_OUTER)
192			need_ext = true;
193		else if (pset->infos[i].cur_part_id != NO_PART)
194			need_existing = true;
195	}
196	if (need_ext && need_existing)
197		snprintf(desc, sizeof desc, "%s, %s",
198		    msg_string(MSG_ptnsizes_mark_existing),
199		    msg_string(MSG_ptnsizes_mark_external));
200	else if (need_existing)
201		strlcpy(desc, msg_string(MSG_ptnsizes_mark_existing),
202		    sizeof desc);
203	else if (need_ext)
204		strlcpy(desc, msg_string(MSG_ptnsizes_mark_external),
205		    sizeof desc);
206	if (need_ext || need_existing) {
207		msg_printf("\n");
208		msg_display_add_subst(msg_string(MSG_ptnsizes_markers),
209		    1, &desc);
210	}
211	msg_printf("\n\n");
212
213	/* update menu title */
214	snprintf(col1, sizeof col1, "%s (%s)", msg_string(MSG_ptnheaders_size),
215	    multname);
216	snprintf(size_menu_title, sizeof size_menu_title,
217	    "   %-37.37s %s\n   %s", col1,
218	    msg_string(MSG_ptnheaders_filesystem), size_separator);
219}
220
221static void
222draw_size_menu_line(menudesc *m, int opt, void *arg)
223{
224	struct partition_usage_set *pset = arg;
225	daddr_t size;
226	char psize[38], inc_free[16], flag, swap[40];
227	const char *mount;
228	bool free_mount = false;
229
230	if (opt < 0 || (size_t)opt >= pset->num)
231		return;
232
233	inc_free[0] = 0;
234	if ((pset->infos[opt].flags & PUIFLAG_EXTEND) &&
235	     pset->cur_free_space > 0) {
236		size = pset->infos[opt].size + pset->cur_free_space;
237		snprintf(inc_free, sizeof inc_free, " (%" PRIu64 ")",
238		    size / sizemult);
239	}
240	size = pset->infos[opt].size;
241	if (pset->infos[opt].fs_type == FS_TMPFS) {
242		if (pset->infos[opt].size < 0)
243			snprintf(psize, sizeof psize, "%" PRIu64 "%%", -size);
244		else
245			snprintf(psize, sizeof psize, "%" PRIu64 " %s", size,
246			    msg_string(MSG_megname));
247	} else {
248		snprintf(psize, sizeof psize, "%" PRIu64 "%s",
249		    size / sizemult, inc_free);
250	}
251
252	if (pset->infos[opt].type == PT_swap) {
253		snprintf(swap, sizeof swap, "<%s>",
254		    msg_string(MSG_swap_display));
255		mount = swap;
256	} else if (pset->infos[opt].flags & PUIFLG_JUST_MOUNTPOINT) {
257		snprintf(swap, sizeof swap, "%s (%s)",
258		    pset->infos[opt].mount,
259		    getfslabelname(pset->infos[opt].fs_type,
260		    pset->infos[opt].fs_version));
261		mount = swap;
262	} else if (pset->infos[opt].mount[0]) {
263		if (pset->infos[opt].instflags & PUIINST_BOOT) {
264			snprintf(swap, sizeof swap, "%s <%s>",
265			    pset->infos[opt].mount, msg_string(MSG_ptn_boot));
266			mount = swap;
267		} else {
268			mount = pset->infos[opt].mount;
269		}
270#ifndef NO_CLONES
271	} else if (pset->infos[opt].flags & PUIFLG_CLONE_PARTS) {
272		snprintf(swap, sizeof swap, "%zu %s",
273		    pset->infos[opt].clone_src->num_sel,
274		    msg_string(MSG_clone_target_disp));
275		mount = swap;
276#endif
277	} else {
278		mount = NULL;
279		if (pset->infos[opt].parts->pscheme->other_partition_identifier
280		    && pset->infos[opt].cur_part_id != NO_PART)
281			mount = pset->infos[opt].parts->pscheme->
282			    other_partition_identifier(pset->infos[opt].parts,
283			    pset->infos[opt].cur_part_id);
284		if (mount == NULL)
285			mount = getfslabelname(pset->infos[opt].fs_type,
286			    pset->infos[opt].fs_version);
287		if (pset->infos[opt].instflags & PUIINST_BOOT) {
288			snprintf(swap, sizeof swap, "%s <%s>",
289			    mount, msg_string(MSG_ptn_boot));
290			mount = swap;
291		}
292		mount = str_arg_subst(msg_string(MSG_size_ptn_not_mounted),
293		    1, &mount);
294		free_mount = true;
295	}
296	flag = ' ';
297	if (pset->infos[opt].flags & PUIFLAG_EXTEND)
298		flag = '+';
299	else if (pset->infos[opt].flags & PUIFLG_IS_OUTER)
300		flag = '@';
301	else if (pset->infos[opt].cur_part_id != NO_PART)
302		flag = '=';
303	wprintw(m->mw, "%-35.35s %c %s", psize, flag, mount);
304	if (free_mount)
305		free(__UNCONST(mount));
306
307	if (opt == 0)
308		set_pset_exit_str(pset);
309}
310
311static int
312add_other_ptn_size(menudesc *menu, void *arg)
313{
314	struct partition_usage_set *pset = arg;
315	struct part_usage_info *p;
316	struct menu_ent *m;
317	char new_mp[MOUNTLEN], *err;
318	const char *args;
319
320	for (;;) {
321		msg_prompt_win(partman_go?MSG_askfsmountadv:MSG_askfsmount,
322		    -1, 18, 0, 0, NULL,  new_mp, sizeof(new_mp));
323		if (new_mp[0] == 0)
324			return 0;
325		if (new_mp[0] != '/') {
326			/* we need absolute mount paths */
327			memmove(new_mp+1, new_mp, sizeof(new_mp)-1);
328			new_mp[0] = '/';
329		}
330
331		/* duplicates? */
332		bool duplicate = false;
333		for (size_t i = 0; i < pset->num; i++) {
334			if (strcmp(pset->infos[i].mount,
335			    new_mp) == 0) {
336			    	args = new_mp;
337				err = str_arg_subst(
338				    msg_string(MSG_mp_already_exists),
339				    1, &args);
340				err_msg_win(err);
341				free(err);
342				duplicate = true;
343				break;
344			}
345		}
346		if (!duplicate)
347			break;
348	}
349
350	m = realloc(pset->menu_opts, (pset->num+5)*sizeof(*pset->menu_opts));
351	if (m == NULL)
352		return 0;
353	p = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos));
354	if (p == NULL)
355		return 0;
356
357	pset->infos = p;
358	pset->menu_opts = m;
359	menu->opts = m;
360	menu->numopts = pset->num+4;
361	m += pset->num;
362	p += pset->num;
363	memset(m, 0, sizeof(*m));
364	memset(p, 0, sizeof(*p));
365	p->parts = pset->parts;
366	p->cur_part_id = NO_PART;
367	p->type = PT_root;
368	p->fs_type = FS_BSDFFS;
369	p->fs_version = 3;
370	strncpy(p->mount, new_mp, sizeof(p->mount));
371
372	menu->cursel = pset->num;
373	pset->num++;
374	fill_ptn_menu(pset);
375
376	return -1;
377}
378
379#ifndef NO_CLONES
380static int
381inst_ext_clone(menudesc *menu, void *arg)
382{
383	struct selected_partitions selected;
384	struct clone_target_menu_data data;
385	struct partition_usage_set *pset = arg;
386	struct part_usage_info *p;
387	menu_ent *men;
388	int num_men, i;
389
390	if (!select_partitions(&selected, pm->parts))
391		return 0;
392
393	num_men = pset->num+1;
394	men = calloc(num_men, sizeof *men);
395	if (men == NULL)
396		return 0;
397	for (i = 0; i < num_men; i++)
398		men[i].opt_action = clone_target_select;
399	men[num_men-1].opt_name = MSG_clone_target_end;
400
401	memset(&data, 0, sizeof data);
402	data.usage = *pset;
403	data.res = -1;
404
405	data.usage.menu = new_menu(MSG_clone_target_hdr,
406	    men, num_men, 3, 2, 0, 65, MC_SCROLL,
407	    NULL, draw_size_menu_line, NULL, NULL, MSG_cancel);
408	process_menu(data.usage.menu, &data);
409	free_menu(data.usage.menu);
410	free(men);
411
412	if (data.res < 0)
413		goto err;
414
415	/* insert clone record */
416	men = realloc(pset->menu_opts, (pset->num+5)*sizeof(*pset->menu_opts));
417	if (men == NULL)
418		goto err;
419	pset->menu_opts = men;
420	menu->opts = men;
421	menu->numopts = pset->num+4;
422
423	p = realloc(pset->infos, (pset->num+1)*sizeof(*pset->infos));
424	if (p == NULL)
425		goto err;
426	pset->infos = p;
427
428	men += data.res;
429	p += data.res;
430	memmove(men+1, men, sizeof(*men)*((pset->num+4)-data.res));
431	memmove(p+1, p, sizeof(*p)*((pset->num)-data.res));
432	memset(men, 0, sizeof(*men));
433	memset(p, 0, sizeof(*p));
434	p->flags = PUIFLG_CLONE_PARTS;
435	p->cur_part_id = NO_PART;
436	p->clone_src = malloc(sizeof(selected));
437	if (p->clone_src != NULL) {
438		*p->clone_src = selected;
439		p->clone_ndx = ~0U;
440		p->size = selected_parts_size(&selected);
441		p->parts = pset->parts;
442	} else {
443		p->clone_ndx = 0;
444		free_selected_partitions(&selected);
445	}
446
447	menu->cursel = data.res == 0 ? 1 : 0;
448	pset->num++;
449	fill_ptn_menu(pset);
450
451	return -1;
452
453err:
454	free_selected_partitions(&selected);
455	return 0;
456}
457#endif
458
459static size_t
460fill_ptn_menu(struct partition_usage_set *pset)
461{
462	struct part_usage_info *p;
463	struct disk_part_info info;
464	menu_ent *m;
465	size_t i;
466	daddr_t free_space;
467
468#ifdef NO_CLONES
469#define	ADD_ITEMS	3
470#else
471#define	ADD_ITEMS	4
472#endif
473
474	memset(pset->menu_opts, 0, (pset->num+ADD_ITEMS)
475	    *sizeof(*pset->menu_opts));
476	for (m = pset->menu_opts, p = pset->infos, i = 0; i < pset->num;
477	    m++, p++, i++) {
478		if (p->flags & PUIFLG_CLONE_PARTS)
479			m->opt_flags = OPT_IGNORE|OPT_NOSHORT;
480		else
481		m->opt_action = set_ptn_size;
482	}
483
484	m->opt_name = size_separator;
485	m->opt_flags = OPT_IGNORE|OPT_NOSHORT;
486	m++;
487
488	m->opt_name = MSG_add_another_ptn;
489	m->opt_action = add_other_ptn_size;
490	m++;
491
492#ifndef NO_CLONES
493	m->opt_name = MSG_clone_from_elsewhere;
494	m->opt_action = inst_ext_clone;
495	m++;
496#endif
497
498	m->opt_name = MSG_askunits;
499	m->opt_menu = MENU_sizechoice;
500	m->opt_flags = OPT_SUB;
501	m++;
502
503	/* calculate free space */
504	free_space = pset->parts->free_space - pset->reserved_space;
505	for (i = 0; i < pset->parts->num_part; i++) {
506		if (!pset->parts->pscheme->get_part_info(pset->parts, i,
507		    &info))
508			continue;
509		if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK|
510		    PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
511			continue;
512		free_space += info.size;
513	}
514	for (i = 0; i < pset->num; i++) {
515		if (pset->infos[i].flags &
516		    (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT))
517			continue;
518		free_space -= pset->infos[i].size;
519	}
520	pset->cur_free_space = free_space;
521	set_pset_exit_str(pset);
522
523	if (pset->menu >= 0)
524		set_menu_numopts(pset->menu, m - pset->menu_opts);
525
526	return m - pset->menu_opts;
527}
528
529static part_id
530find_part_at(struct disk_partitions *parts, daddr_t start)
531{
532	size_t i;
533	struct disk_part_info info;
534
535	for (i = 0; i < parts->num_part; i++) {
536		if (!parts->pscheme->get_part_info(parts, i, &info))
537			continue;
538		if (info.start == start)
539			return i;
540	}
541
542	return NO_PART;
543}
544
545static daddr_t
546parse_ram_size(const char *str, bool *is_percent)
547{
548	daddr_t val;
549	char *cp;
550
551	val = strtoull(str, &cp, 10);
552	while (*cp && isspace((unsigned char)*cp))
553		cp++;
554
555	*is_percent = *cp == '%';
556	return val;
557}
558
559int
560set_ptn_size(menudesc *m, void *arg)
561{
562	struct partition_usage_set *pset = arg;
563	struct part_usage_info *p = &pset->infos[m->cursel];
564	char answer[16], dflt[16];
565	const char *err_msg;
566	size_t i, root = ~0U;
567	daddr_t size, old_size, new_size_val, mult;
568	int rv;
569	bool non_zero, extend, is_ram_size, is_percent = false;
570
571	if (pset->cur_free_space == 0 && p->size == 0 &&
572	    !(p->flags & PUIFLG_JUST_MOUNTPOINT))
573		/* Don't allow 'free_parts' to go negative */
574		return 0;
575
576	if (p->cur_part_id != NO_PART) {
577		rv = 0;
578		process_menu(MENU_ptnsize_replace_existing_partition, &rv);
579		if (rv == 0)
580			return 0;
581		if (!pset->parts->pscheme->delete_partition(pset->parts,
582		    p->cur_part_id, &err_msg)) {
583			if (err_msg)
584				err_msg_win(err_msg);
585			return 0;
586		}
587		p->cur_part_id = NO_PART;
588		/*
589		 * All other part ids are invalid now too - update them!
590		 */
591		for (i = 0; i < pset->num; i++) {
592			if (pset->infos[i].cur_part_id == NO_PART)
593				continue;
594			pset->infos[i].cur_part_id =
595			    find_part_at(pset->parts, pset->infos[i].cur_start);
596		}
597	}
598
599	is_ram_size = (p->flags & PUIFLG_JUST_MOUNTPOINT)
600	    && p->fs_type == FS_TMPFS;
601
602	size = p->size;
603	if (is_ram_size && size < 0) {
604		is_percent = true;
605		size = -size;
606	}
607	old_size = size;
608	if (size == 0)
609		size = p->def_size;
610	if (!is_ram_size)
611		size /= sizemult;
612
613	if (is_ram_size) {
614		snprintf(dflt, sizeof dflt, "%" PRIu64 "%s",
615		    size, is_percent ? "%" : "");
616	} else {
617		snprintf(dflt, sizeof dflt, "%" PRIu64 "%s",
618		    size, p->flags & PUIFLAG_EXTEND ? "+" : "");
619	}
620
621	for (;;) {
622		msg_fmt_prompt_win(MSG_askfssize, -1, 18, 0, 0,
623		    dflt, answer, sizeof answer, "%s%s", p->mount,
624		    is_ram_size ? msg_string(MSG_megname) : multname);
625
626		if (is_ram_size) {
627			new_size_val = parse_ram_size(answer, &is_percent);
628			if (is_percent &&
629			    (new_size_val < 0 || new_size_val > 100))
630				continue;
631			if (!is_percent && new_size_val < 0)
632				continue;
633			size = new_size_val;
634			extend = false;
635			break;
636		}
637		mult = sizemult;
638		new_size_val = parse_disk_pos(answer, &mult, pm->sectorsize,
639		    pm->dlcylsize, &extend);
640
641		if (strcmp(answer, dflt) == 0)
642			non_zero = p->def_size > 0;
643		else
644			non_zero = new_size_val > 0;
645
646		/* Some special cases when /usr is first given a size */
647		if (old_size == 0 && non_zero &&
648		    strcmp(p->mount, "/usr") == 0) {
649			for (i = 0; i < pset->num; i++) {
650				if (strcmp(pset->infos[i].mount, "/") == 0) {
651					root = i;
652					break;
653				}
654			}
655			/* Remove space for /usr from / */
656			if (root < pset->num &&
657			     pset->infos[root].cur_part_id == NO_PART &&
658			     pset->infos[root].size ==
659					pset->infos[root].def_size) {
660				/*
661				 * root partition does not yet exist and
662				 * has default size
663				 */
664				pset->infos[root].size -= p->def_size;
665				pset->cur_free_space += p->def_size;
666			}
667			/*
668			 * hack to add free space to /usr if
669			 * previously / got it
670			 */
671			if (pset->infos[root].flags & PUIFLAG_EXTEND)
672				extend = true;
673		}
674		if (new_size_val < 0)
675			continue;
676		size = new_size_val;
677		break;
678	}
679
680	daddr_t align = pset->parts->pscheme->get_part_alignment(pset->parts);
681	if (!is_ram_size) {
682		size = NUMSEC(size, mult, align);
683	}
684	if (p->flags & PUIFLAG_EXTEND)
685		p->flags &= ~PUIFLAG_EXTEND;
686	if (extend && (p->limit == 0 || p->limit > p->size)) {
687		for (size_t k = 0; k < pset->num; k++)
688			pset->infos[k].flags &= ~PUIFLAG_EXTEND;
689		p->flags |= PUIFLAG_EXTEND;
690		if (size == 0)
691			size = align;
692	}
693	if (p->limit != 0 && size > p->limit)
694		size = p->limit;
695	if ((p->flags & (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT)) == 0)
696		pset->cur_free_space += p->size - size;
697	p->size = is_percent ? -size : size;
698	set_pset_exit_str(pset);
699
700	return 0;
701}
702
703/*
704 * User interface to edit a "wanted" partition layout "pset" as first
705 * abstract phase (not concrete partitions).
706 * Make sure to have everything (at least theoretically) fit the
707 * available space.
708 * During editing we keep the part_usage_info and the menu_opts
709 * in pset in sync, that is: we always allocate just enough entries
710 * in pset->infos as we have usage infos in the list (pset->num),
711 * and two additional menu entries ("add a partition" and "select units").
712 * The menu exit string changes depending on content, and implies
713 * abort while the partition set is not valid (does not fit).
714 * Return true when the user wants to continue (by editing the concrete
715 * partitions), return false to abort.
716 */
717bool
718get_ptn_sizes(struct partition_usage_set *pset)
719{
720	size_t num;
721
722	wclear(stdscr);
723	wrefresh(stdscr);
724
725	if (pset->menu_opts == NULL)
726		pset->menu_opts = calloc(pset->num+4, sizeof(*pset->menu_opts));
727
728	pset->menu = -1;
729	num = fill_ptn_menu(pset);
730
731	pset->menu = new_menu(size_menu_title, pset->menu_opts, num,
732			3, -1, 12, 70,
733			MC_ALWAYS_SCROLL|MC_NOBOX|MC_NOCLEAR|MC_CONTINUOUS,
734			draw_size_menu_header, draw_size_menu_line, NULL,
735			NULL, size_menu_exit);
736
737	if (pset->menu < 0) {
738		free(pset->menu_opts);
739		pset->menu_opts = NULL;
740		return false;
741	}
742
743	pset->ok = true;
744	process_menu(pset->menu, pset);
745
746	free_menu(pset->menu);
747	free(pset->menu_opts);
748	pset->menu = -1;
749	pset->menu_opts = NULL;
750
751	return pset->ok;
752}
753
754static int
755set_keep_existing(menudesc *m, void *arg)
756{
757	((arg_rep_int*)arg)->rv = LY_KEEPEXISTING;
758	return 0;
759}
760
761static int
762set_switch_scheme(menudesc *m, void *arg)
763{
764	((arg_rep_int*)arg)->rv = LY_OTHERSCHEME;
765	return 0;
766}
767
768static int
769set_edit_part_sizes(menudesc *m, void *arg)
770{
771	((arg_rep_int*)arg)->rv = LY_SETSIZES;
772	return 0;
773}
774
775static int
776set_use_default_sizes(menudesc *m, void *arg)
777{
778	((arg_rep_int*)arg)->rv = LY_USEDEFAULT;
779	return 0;
780}
781
782static int
783set_use_empty_parts(menudesc *m, void *arg)
784{
785	((arg_rep_int*)arg)->rv = LY_USENONE;
786	return 0;
787}
788
789/*
790 * Check if there is a reasonable pre-existing partition for
791 * NetBSD.
792 */
793static bool
794check_existing_netbsd(struct disk_partitions *parts)
795{
796	size_t nbsd_parts;
797	struct disk_part_info info;
798
799	nbsd_parts = 0;
800	for (part_id p = 0; p < parts->num_part; p++) {
801		if (!parts->pscheme->get_part_info(parts, p, &info))
802			continue;
803		if (info.flags & (PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
804			continue;
805		if (info.nat_type && info.nat_type->generic_ptype == PT_root)
806			nbsd_parts++;
807	}
808
809	return nbsd_parts > 0;
810}
811
812/*
813 * Query a partition layout type (with available options depending on
814 * pre-existing partitions).
815 */
816static enum layout_type
817ask_layout(struct disk_partitions *parts, bool have_existing)
818{
819	arg_rep_int ai;
820	const char *args[2];
821	int menu;
822	size_t num_opts;
823	menu_ent options[5], *opt;
824
825	args[0] = msg_string(parts->pscheme->name);
826	args[1] = msg_string(parts->pscheme->short_name);
827	ai.args.argv = args;
828	ai.args.argc = 2;
829	ai.rv = LY_ERROR;
830
831	memset(options, 0, sizeof(options));
832	num_opts = 0;
833	opt = &options[0];
834
835	if (have_existing) {
836		opt->opt_name = MSG_Keep_existing_partitions;
837		opt->opt_flags = OPT_EXIT;
838		opt->opt_action = set_keep_existing;
839		opt++;
840		num_opts++;
841	}
842	opt->opt_name = MSG_Set_Sizes;
843	opt->opt_flags = OPT_EXIT;
844	opt->opt_action = set_edit_part_sizes;
845	opt++;
846	num_opts++;
847
848	opt->opt_name = MSG_Use_Default_Parts;
849	opt->opt_flags = OPT_EXIT;
850	opt->opt_action = set_use_default_sizes;
851	opt++;
852	num_opts++;
853
854	opt->opt_name = MSG_Use_Empty_Parts;
855	opt->opt_flags = OPT_EXIT;
856	opt->opt_action = set_use_empty_parts;
857	opt++;
858	num_opts++;
859
860	if (num_available_part_schemes > 1 &&
861	    parts->parent == NULL) {
862		opt->opt_name = MSG_Use_Different_Part_Scheme;
863		opt->opt_flags = OPT_EXIT;
864		opt->opt_action = set_switch_scheme;
865		opt++;
866		num_opts++;
867	}
868
869	menu = new_menu(MSG_Select_your_choice, options, num_opts,
870	    -1, -10, 0, 0, 0, NULL, NULL, NULL, NULL, MSG_cancel);
871	if (menu != -1) {
872		get_menudesc(menu)->expand_act = expand_all_option_texts;
873		process_menu(menu, &ai);
874		free_menu(menu);
875	}
876
877	return ai.rv;
878}
879
880static void
881merge_part_with_wanted(struct disk_partitions *parts, part_id pno,
882    const struct disk_part_info *info, struct partition_usage_set *wanted,
883    size_t wanted_num, bool is_outer)
884{
885	struct part_usage_info *infos;
886
887	/*
888	 * does this partition match something in the wanted set?
889	 */
890	for (size_t i = 0; i < wanted_num; i++) {
891		if (wanted->infos[i].type != info->nat_type->generic_ptype)
892			continue;
893		if (wanted->infos[i].type == PT_root &&
894		    info->last_mounted != NULL && info->last_mounted[0] != 0 &&
895		    strcmp(info->last_mounted, wanted->infos[i].mount) != 0)
896			continue;
897		if (wanted->infos[i].cur_part_id != NO_PART)
898			continue;
899		wanted->infos[i].cur_part_id = pno;
900		wanted->infos[i].parts = parts;
901		wanted->infos[i].size = info->size;
902		wanted->infos[i].cur_start = info->start;
903		wanted->infos[i].flags &= ~PUIFLAG_EXTEND;
904		if (wanted->infos[i].fs_type != FS_UNUSED &&
905		    wanted->infos[i].type != PT_swap &&
906		    info->last_mounted != NULL &&
907		    info->last_mounted[0] != 0)
908			wanted->infos[i].instflags |= PUIINST_MOUNT;
909		if (is_outer)
910			wanted->infos[i].flags |= PUIFLG_IS_OUTER;
911		else
912			wanted->infos[i].flags &= ~PUIFLG_IS_OUTER;
913		return;
914	}
915
916	/*
917	 * no match - if this is from the outer scheme, we are done.
918	 * otherwise it must be inserted into the wanted set.
919	 */
920	if (is_outer)
921		return;
922
923	/*
924	 * create a new entry for this
925	 */
926	infos = realloc(wanted->infos, sizeof(*infos)*(wanted->num+1));
927	if (infos == NULL)
928		return;
929	wanted->infos = infos;
930	infos += wanted->num;
931	wanted->num++;
932	memset(infos, 0, sizeof(*infos));
933	if (info->last_mounted != NULL && info->last_mounted[0] != 0)
934		strlcpy(infos->mount, info->last_mounted,
935		    sizeof(infos->mount));
936	infos->type = info->nat_type->generic_ptype;
937	infos->cur_part_id = pno;
938	infos->parts = parts;
939	infos->size = info->size;
940	infos->cur_start = info->start;
941	infos->fs_type = info->fs_type;
942	infos->fs_version = info->fs_sub_type;
943	if (is_outer)
944		infos->flags |= PUIFLG_IS_OUTER;
945}
946
947static bool
948have_x11_by_default(void)
949{
950	static const uint8_t def_sets[] = { MD_SETS_SELECTED };
951
952	for (size_t i = 0; i < __arraycount(def_sets); i++)
953		if (def_sets[i] >= SET_X11_FIRST &&
954		    def_sets[i] <= SET_X11_LAST)
955			return true;
956
957	return false;
958}
959
960static void
961fill_defaults(struct partition_usage_set *wanted, struct disk_partitions *parts,
962    daddr_t ptstart, daddr_t ptsize)
963{
964	size_t i, root = ~0U, usr = ~0U, swap = ~0U, def_usr = ~0U;
965	daddr_t free_space, dump_space, required;
966#if defined(DEFAULT_UFS2) && !defined(HAVE_UFS2_BOOT)
967	size_t boot = ~0U;
968#endif
969
970	memset(wanted, 0, sizeof(*wanted));
971	wanted->parts = parts;
972	if (ptstart > parts->disk_start)
973		wanted->reserved_space = ptstart - parts->disk_start;
974	if ((ptstart + ptsize) < (parts->disk_start+parts->disk_size))
975		wanted->reserved_space +=
976		    (parts->disk_start+parts->disk_size) -
977		    (ptstart + ptsize);
978	wanted->num = __arraycount(default_parts_init);
979	wanted->infos = calloc(wanted->num, sizeof(*wanted->infos));
980	if (wanted->infos == NULL) {
981		err_msg_win(err_outofmem);
982		return;
983	}
984
985	memcpy(wanted->infos, default_parts_init, sizeof(default_parts_init));
986
987#ifdef HAVE_TMPFS
988	if (get_ramsize() >= SMALL_RAM_SIZE) {
989		for (i = 0; i < wanted->num; i++) {
990			if (wanted->infos[i].type != PT_root ||
991			    wanted->infos[i].fs_type != FS_TMPFS)
992				continue;
993			/* default tmpfs to 1/4 RAM */
994			wanted->infos[i].size = -25;
995			wanted->infos[i].def_size = -25;
996			break;
997		}
998	}
999#endif
1000
1001#ifdef MD_PART_DEFAULTS
1002	MD_PART_DEFAULTS(pm, wanted->infos, wanted->num);
1003#endif
1004
1005	for (i = 0; i < wanted->num; i++) {
1006		wanted->infos[i].parts = parts;
1007		wanted->infos[i].cur_part_id = NO_PART;
1008		if (wanted->infos[i].type == PT_undef &&
1009		    wanted->infos[i].fs_type != FS_UNUSED) {
1010			const struct part_type_desc *pt =
1011			    parts->pscheme->get_fs_part_type(PT_undef,
1012			    wanted->infos[i].fs_type,
1013			    wanted->infos[i].fs_version);
1014			if (pt != NULL)
1015				wanted->infos[i].type = pt->generic_ptype;
1016		}
1017		if (wanted->parts->parent != NULL &&
1018		    (wanted->infos[i].fs_type == FS_MSDOS ||
1019		     wanted->infos[i].fs_type == FS_EX2FS))
1020			wanted->infos[i].flags |=
1021			    PUIFLG_ADD_INNER|PUIFLAG_ADD_OUTER;
1022
1023#if DEFSWAPSIZE == -1
1024		if (wanted->infos[i].type == PT_swap) {
1025#ifdef	MD_MAY_SWAP_TO
1026			if (MD_MAY_SWAP_TO(wanted->parts->disk))
1027#endif
1028				wanted->infos[i].size =
1029				    get_ramsize() * (MEG / 512);
1030		}
1031#endif
1032		if (wanted->infos[i].type == PT_swap && swap > wanted->num)
1033			swap = i;
1034#if defined(DEFAULT_UFS2) && !defined(HAVE_UFS2_BOOT)
1035		if (wanted->infos[i].instflags & PUIINST_BOOT)
1036			boot = i;
1037#endif
1038		if (wanted->infos[i].type == PT_root) {
1039			if (strcmp(wanted->infos[i].mount, "/") == 0) {
1040				root = i;
1041			} else if (
1042			    strcmp(wanted->infos[i].mount, "/usr") == 0) {
1043				if (wanted->infos[i].size > 0)
1044					usr = i;
1045				else
1046					def_usr = i;
1047			}
1048			if (wanted->infos[i].fs_type == FS_UNUSED)
1049				wanted->infos[i].fs_type = FS_BSDFFS;
1050			if (wanted->infos[i].fs_type == FS_BSDFFS) {
1051#ifdef DEFAULT_UFS2
1052#ifndef HAVE_UFS2_BOOT
1053				if (boot < wanted->num || i != root)
1054#endif
1055					wanted->infos[i].fs_version = 3;
1056#endif
1057			}
1058		}
1059	}
1060
1061	/*
1062	 * Now we have the defaults as if we were installing to an
1063	 * empty disk. Merge the partitions in target range that are already
1064	 * there (match with wanted) or are there additionally.
1065	 * The only thing outside of target range that we care for
1066	 * are FAT partitions, EXT2FS partitions, and a potential
1067	 * swap partition - we assume one is enough.
1068	 */
1069	size_t num = wanted->num;
1070	if (parts->parent) {
1071		for (part_id pno = 0; pno < parts->parent->num_part; pno++) {
1072			struct disk_part_info info;
1073
1074			if (!parts->parent->pscheme->get_part_info(
1075			    parts->parent, pno, &info))
1076				continue;
1077			if (info.nat_type->generic_ptype != PT_swap &&
1078			    info.fs_type != FS_MSDOS &&
1079			    info.fs_type != FS_EX2FS)
1080				continue;
1081			merge_part_with_wanted(parts->parent, pno, &info,
1082			    wanted, num, true);
1083			break;
1084		}
1085	}
1086	for (part_id pno = 0; pno < parts->num_part; pno++) {
1087		struct disk_part_info info;
1088
1089		if (!parts->pscheme->get_part_info(parts, pno, &info))
1090			continue;
1091
1092		if (info.flags & PTI_PSCHEME_INTERNAL)
1093			continue;
1094
1095		if (info.nat_type->generic_ptype != PT_swap &&
1096		    (info.start < ptstart ||
1097		    (info.start + info.size) > (ptstart+ptsize)))
1098			continue;
1099
1100		merge_part_with_wanted(parts, pno, &info,
1101		    wanted, num, false);
1102	}
1103
1104	daddr_t align = parts->pscheme->get_part_alignment(parts);
1105
1106	if (root < wanted->num && wanted->infos[root].cur_part_id == NO_PART) {
1107		daddr_t max_root_size = parts->disk_start + parts->disk_size;
1108		if (root_limit > 0) {
1109			/* Bah - bios can not read all the disk, limit root */
1110			max_root_size = root_limit - parts->disk_start;
1111		}
1112		wanted->infos[root].limit = max_root_size;
1113	}
1114
1115	if (have_x11_by_default()) {
1116		daddr_t xsize = XNEEDMB * (MEG / 512);
1117		if (usr < wanted->num) {
1118			if (wanted->infos[usr].cur_part_id == NO_PART) {
1119				wanted->infos[usr].size += xsize;
1120				wanted->infos[usr].def_size += xsize;
1121			}
1122		} else if (root < wanted->num &&
1123		    wanted->infos[root].cur_part_id == NO_PART &&
1124		    (wanted->infos[root].limit == 0 ||
1125		    (wanted->infos[root].size + xsize) <=
1126		    wanted->infos[root].limit)) {
1127			wanted->infos[root].size += xsize;
1128		}
1129	}
1130	if (wanted->infos[root].limit > 0 &&
1131	    wanted->infos[root].size > wanted->infos[root].limit) {
1132		if (usr < wanted->num) {
1133			/* move space from root to usr */
1134			daddr_t spill = wanted->infos[root].size -
1135			    wanted->infos[root].limit;
1136			spill = roundup(spill, align);
1137			wanted->infos[root].size =
1138			    wanted->infos[root].limit;
1139			wanted->infos[usr].size = spill;
1140		} else {
1141			wanted->infos[root].size =
1142			    wanted->infos[root].limit;
1143		}
1144	}
1145
1146	/*
1147	 * Preliminary calc additional space to allocate and how much
1148	 * we likely will have left over. Use that to do further
1149	 * adjustments, so we don't present the user inherently
1150	 * impossible defaults.
1151	 */
1152	free_space = parts->free_space - wanted->reserved_space;
1153	required = 0;
1154	if (root < wanted->num)
1155		required += wanted->infos[root].size;
1156	if (usr < wanted->num)
1157		required += wanted->infos[usr].size;
1158	else if (def_usr < wanted->num)
1159		required += wanted->infos[def_usr].def_size;
1160	free_space -= required;
1161	for (i = 0; i < wanted->num; i++) {
1162		if (i == root || i == usr)
1163			continue;	/* already accounted above */
1164		if (wanted->infos[i].cur_part_id != NO_PART)
1165			continue;
1166		if (wanted->infos[i].size == 0)
1167			continue;
1168		if (wanted->infos[i].flags
1169		    & (PUIFLG_IS_OUTER|PUIFLG_JUST_MOUNTPOINT))
1170			continue;
1171		free_space -= wanted->infos[i].size;
1172	}
1173	if (free_space < 0 && swap < wanted->num &&
1174	    get_ramsize() > TINY_RAM_SIZE) {
1175		/* steel from swap partition */
1176		daddr_t d = wanted->infos[swap].size;
1177		daddr_t inc = roundup(-free_space, align);
1178		if (inc > d)
1179			inc = d;
1180		free_space += inc;
1181		wanted->infos[swap].size -= inc;
1182	}
1183	if (root < wanted->num) {
1184		/* Add space for 2 system dumps to / (traditional) */
1185		dump_space = get_ramsize() * (MEG/512);
1186		dump_space = roundup(dump_space, align);
1187		if (free_space > dump_space*2)
1188			dump_space *= 2;
1189		if (free_space > dump_space) {
1190			wanted->infos[root].size += dump_space;
1191			free_space -= dump_space;
1192		}
1193	}
1194	if (wanted->infos[root].limit > 0 &&
1195	    (wanted->infos[root].cur_start + wanted->infos[root].size >
1196		wanted->infos[root].limit ||
1197	    (wanted->infos[root].flags & PUIFLAG_EXTEND &&
1198	    (wanted->infos[root].cur_start + wanted->infos[root].size
1199	     + free_space > wanted->infos[root].limit)))) {
1200		if (usr >= wanted->num && def_usr < wanted->num) {
1201			usr = def_usr;
1202			wanted->infos[usr].size = wanted->infos[root].size
1203			    - wanted->infos[root].limit;
1204			if (wanted->infos[usr].size <= 0)
1205				wanted->infos[usr].size = max(1,
1206				    wanted->infos[usr].def_size);
1207			wanted->infos[root].size =
1208			    wanted->infos[root].limit;
1209			if (wanted->infos[root].flags & PUIFLAG_EXTEND) {
1210				wanted->infos[root].flags &= ~PUIFLAG_EXTEND;
1211				wanted->infos[usr].flags |= PUIFLAG_EXTEND;
1212			}
1213		} else if (usr < wanted->num) {
1214			/* move space from root to usr */
1215			daddr_t spill = wanted->infos[root].size -
1216			    wanted->infos[root].limit;
1217			spill = roundup(spill, align);
1218			wanted->infos[root].size =
1219			    wanted->infos[root].limit;
1220			wanted->infos[usr].size = spill;
1221		} else {
1222			wanted->infos[root].size =
1223			    wanted->infos[root].limit;
1224		}
1225	}
1226	wanted->infos[root].def_size = wanted->infos[root].size;
1227}
1228
1229/*
1230 * We sort pset->infos to sync with pset->parts and
1231 * the cur_part_id, to allow using the same index into both
1232 * "array" in later phases. This may include inserting
1233 * dummy  entries (when we do not actually want the
1234 * partition, but it is forced upon us, like RAW_PART in
1235 * disklabel).
1236 */
1237static void
1238sort_and_sync_parts(struct partition_usage_set *pset)
1239{
1240	struct part_usage_info *infos;
1241	size_t i, j, no;
1242	part_id pno;
1243
1244	pset->cur_free_space = pset->parts->free_space - pset->reserved_space;
1245
1246	/* count non-empty entries that are not in pset->parts */
1247	no = pset->parts->num_part;
1248	for (i = 0; i < pset->num; i++) {
1249		if (pset->infos[i].size == 0)
1250			continue;
1251		if (pset->infos[i].cur_part_id != NO_PART)
1252			continue;
1253		no++;
1254	}
1255
1256	/* allocate new infos */
1257	infos = calloc(no, sizeof *infos);
1258	if (infos == NULL)
1259		return;
1260
1261	/* pre-initialize the first entries as dummy entries */
1262	for (i = 0; i < pset->parts->num_part; i++) {
1263		infos[i].cur_part_id = NO_PART;
1264		infos[i].cur_flags = PTI_PSCHEME_INTERNAL;
1265	}
1266	/*
1267	 * Now copy over everything from our old entries that points to
1268	 * a real partition.
1269	 */
1270	for (i = 0; i < pset->num; i++) {
1271		pno = pset->infos[i].cur_part_id;
1272		if (pno == NO_PART)
1273			continue;
1274		if (pset->parts != pset->infos[i].parts)
1275			continue;
1276		if (pset->infos[i].flags & PUIFLG_JUST_MOUNTPOINT)
1277			continue;
1278		if ((pset->infos[i].flags & (PUIFLG_IS_OUTER|PUIFLG_ADD_INNER))
1279		    == PUIFLG_IS_OUTER)
1280			continue;
1281		if (pno >= pset->parts->num_part)
1282			continue;
1283		memcpy(infos+pno, pset->infos+i, sizeof(*infos));
1284	}
1285	/* Fill in the infos for real partitions where we had no data */
1286	for (pno = 0; pno < pset->parts->num_part; pno++) {
1287		struct disk_part_info info;
1288
1289		if (infos[pno].cur_part_id != NO_PART)
1290			continue;
1291
1292		if (!pset->parts->pscheme->get_part_info(pset->parts, pno,
1293		    &info))
1294			continue;
1295
1296		infos[pno].parts = pset->parts;
1297		infos[pno].cur_part_id = pno;
1298		infos[pno].cur_flags = info.flags;
1299		infos[pno].size = info.size;
1300		infos[pno].type = info.nat_type->generic_ptype;
1301		infos[pno].cur_start = info.start;
1302		infos[pno].fs_type = info.fs_type;
1303		infos[pno].fs_version = info.fs_sub_type;
1304	}
1305	/* Add the non-partition entries after that */
1306	j = pset->parts->num_part;
1307	for (i = 0; i < pset->num; i++) {
1308		if (j >= no)
1309			break;
1310		if (pset->infos[i].size == 0)
1311			continue;
1312		if (pset->infos[i].cur_part_id != NO_PART)
1313			continue;
1314		memcpy(infos+j, pset->infos+i, sizeof(*infos));
1315		j++;
1316	}
1317
1318	/* done, replace infos */
1319	free(pset->infos);
1320	pset->num = no;
1321	pset->infos = infos;
1322}
1323
1324#ifndef NO_CLONES
1325/*
1326 * Convert clone entries with more than one source into
1327 * several entries with a single source each.
1328 */
1329static void
1330normalize_clones(struct part_usage_info **infos, size_t *num)
1331{
1332	size_t i, j, add_clones;
1333	struct part_usage_info *ui, *src, *target;
1334	struct disk_part_info info;
1335	struct selected_partition *clone;
1336
1337	for (add_clones = 0, i = 0; i < *num; i++) {
1338		if ((*infos)[i].clone_src != NULL &&
1339		    (*infos)[i].flags & PUIFLG_CLONE_PARTS &&
1340		    (*infos)[i].cur_part_id == NO_PART)
1341			add_clones += (*infos)[i].clone_src->num_sel-1;
1342	}
1343	if (add_clones == 0)
1344		return;
1345
1346	ui = calloc(*num+add_clones, sizeof(**infos));
1347	if (ui == NULL)
1348		return;	/* can not handle this well here, drop some clones */
1349
1350	/* walk the list and dedup clones */
1351	for (src = *infos, target = ui, i = 0; i < *num; i++) {
1352		if (src != target)
1353			*target = *src;
1354		if (target->clone_src != NULL &&
1355		    (target->flags & PUIFLG_CLONE_PARTS) &&
1356		    target->cur_part_id == NO_PART) {
1357			for (j = 0; j < src->clone_src->num_sel; j++) {
1358				if (j > 0) {
1359					target++;
1360					*target = *src;
1361				}
1362				target->clone_ndx = j;
1363				clone = &target->clone_src->selection[j];
1364				clone->parts->pscheme->get_part_info(
1365				    clone->parts, clone->id, &info);
1366				target->size = info.size;
1367			}
1368		}
1369		target++;
1370		src++;
1371	}
1372	*num += add_clones;
1373	assert((target-ui) >= 0 && (size_t)(target-ui) == *num);
1374	free(*infos);
1375	*infos = ui;
1376}
1377#endif
1378
1379static void
1380apply_settings_to_partitions(struct disk_partitions *parts,
1381    struct partition_usage_set *wanted, daddr_t start, daddr_t xsize)
1382{
1383	size_t i, exp_ndx = ~0U;
1384	daddr_t planned_space = 0, nsp, from, align;
1385	struct disk_part_info *infos;
1386#ifndef NO_CLONES
1387	struct disk_part_info cinfo, srcinfo;
1388	struct selected_partition *sp;
1389#endif
1390	struct disk_part_free_space space;
1391	struct disk_partitions *ps = NULL;
1392	part_id pno, new_part_id;
1393
1394#ifndef NO_CLONES
1395	normalize_clones(&wanted->infos, &wanted->num);
1396#endif
1397
1398	infos = calloc(wanted->num, sizeof(*infos));
1399	if (infos == NULL) {
1400		err_msg_win(err_outofmem);
1401		return;
1402	}
1403
1404	align = wanted->parts->pscheme->get_part_alignment(wanted->parts);
1405	/*
1406	 * Pass one: calculate space available for expanding
1407	 * the marked partition.
1408	 */
1409	if (parts->free_space != parts->disk_size)
1410		planned_space = align;	/* align first part */
1411	for (i = 0; i < wanted->num; i++) {
1412		if ((wanted->infos[i].flags & PUIFLAG_EXTEND) &&
1413		    exp_ndx == ~0U)
1414			exp_ndx = i;
1415		if (wanted->infos[i].flags & PUIFLG_JUST_MOUNTPOINT)
1416			continue;
1417		nsp = wanted->infos[i].size;
1418		if (wanted->infos[i].cur_part_id != NO_PART) {
1419			ps = wanted->infos[i].flags & PUIFLG_IS_OUTER ?
1420			    parts->parent : parts;
1421
1422			ps->pscheme->get_part_info(ps,
1423			     wanted->infos[i].cur_part_id, &infos[i]);
1424			if (!(wanted->infos[i].flags & PUIFLG_IS_OUTER))
1425				nsp -= infos[i].size;
1426		}
1427		if (nsp <= 0)
1428			continue;
1429		planned_space += roundup(nsp, align);
1430	}
1431
1432	/*
1433	 * Expand the pool partition (or shrink, if we overran),
1434	 * but check size limits.
1435	 */
1436	if (exp_ndx < wanted->num) {
1437		daddr_t free_space = parts->free_space - planned_space -
1438		    wanted->reserved_space;
1439		daddr_t new_size = wanted->infos[exp_ndx].size;
1440		if (free_space > 0)
1441			new_size += roundup(free_space,align);
1442
1443		if (wanted->infos[exp_ndx].limit > 0 &&
1444		    (new_size + wanted->infos[exp_ndx].cur_start)
1445		     > wanted->infos[exp_ndx].limit) {
1446			wanted->infos[exp_ndx].size =
1447			    wanted->infos[exp_ndx].limit
1448			    - wanted->infos[exp_ndx].cur_start;
1449		} else {
1450			wanted->infos[exp_ndx].size = new_size;
1451		}
1452	}
1453
1454	/*
1455	 * Now it gets tricky: we want the wanted partitions in order
1456	 * as defined, but any already existing partitions should not
1457	 * be moved. We allow them to change size though.
1458	 * To keep it simple, we just assign in order and skip blocked
1459	 * spaces. This may shuffle the order of the resulting partitions
1460	 * compared to the wanted list.
1461	 */
1462
1463	/* Adjust sizes of existing partitions */
1464	for (i = 0; i < wanted->num; i++) {
1465		ps = wanted->infos[i].flags & PUIFLG_IS_OUTER ?
1466		    parts->parent : parts;
1467		const struct part_usage_info *want = &wanted->infos[i];
1468
1469		if (want->cur_part_id == NO_PART)
1470			continue;
1471		if (i == exp_ndx)	/* the exp. part. can not exist yet */
1472			continue;
1473		daddr_t free_size = ps->pscheme->max_free_space_at(ps,
1474		    infos[i].start);
1475		if (free_size < wanted->infos[i].size)
1476			continue;
1477		if (infos[i].size != wanted->infos[i].size) {
1478			infos[i].size = wanted->infos[i].size;
1479			ps->pscheme->set_part_info(ps, want->cur_part_id,
1480			    &infos[i], NULL);
1481		}
1482	}
1483
1484	from = start > 0 ? start : -1;
1485	/*
1486	 * First add all outer partitions - we need to align those exactly
1487	 * with the inner counterpart later.
1488	 */
1489	if (parts->parent) {
1490		ps = parts->parent;
1491		daddr_t outer_align = ps->pscheme->get_part_alignment(ps);
1492
1493		for (i = 0; i < wanted->num; i++) {
1494			struct part_usage_info *want = &wanted->infos[i];
1495
1496			if (want->cur_part_id != NO_PART)
1497				continue;
1498			if (!(want->flags & PUIFLAG_ADD_OUTER))
1499				continue;
1500			if (want->size <= 0)
1501				continue;
1502
1503			size_t cnt = ps->pscheme->get_free_spaces(ps,
1504			    &space, 1, want->size-2*outer_align,
1505			    outer_align, from, -1);
1506
1507			if (cnt == 0)	/* no free space for this partition */
1508				continue;
1509
1510			infos[i].start = space.start;
1511			infos[i].size = min(want->size, space.size);
1512			infos[i].nat_type =
1513			    ps->pscheme->get_fs_part_type(
1514			        want->type, want->fs_type, want->fs_version);
1515			infos[i].last_mounted = want->mount;
1516			infos[i].fs_type = want->fs_type;
1517			infos[i].fs_sub_type = want->fs_version;
1518			infos[i].fs_opt1 = want->fs_opt1;
1519			infos[i].fs_opt2 = want->fs_opt2;
1520			infos[i].fs_opt3 = want->fs_opt3;
1521			new_part_id = ps->pscheme->add_partition(ps,
1522			    &infos[i], NULL);
1523			if (new_part_id == NO_PART)
1524				continue;	/* failed to add, skip */
1525
1526			ps->pscheme->get_part_info(ps,
1527			    new_part_id, &infos[i]);
1528			want->cur_part_id = new_part_id;
1529
1530			want->flags |= PUIFLG_ADD_INNER|PUIFLG_IS_OUTER;
1531			from = roundup(infos[i].start +
1532			    infos[i].size, outer_align);
1533		}
1534	}
1535
1536	/*
1537	 * Now add new inner partitions (and cloned partitions)
1538	 */
1539	for (i = 0; i < wanted->num; i++) {
1540
1541		daddr_t limit = wanted->parts->disk_size + wanted->parts->disk_start;
1542		if (from >= limit)
1543			break;
1544
1545		struct part_usage_info *want = &wanted->infos[i];
1546
1547		if (want->cur_part_id != NO_PART)
1548			continue;
1549		if (want->flags & (PUIFLG_JUST_MOUNTPOINT|PUIFLG_IS_OUTER))
1550			continue;
1551#ifndef NO_CLONES
1552		if ((want->flags & PUIFLG_CLONE_PARTS) &&
1553		    want->clone_src != NULL &&
1554		    want->clone_ndx < want->clone_src->num_sel) {
1555			sp = &want->clone_src->selection[want->clone_ndx];
1556			if (!sp->parts->pscheme->get_part_info(
1557			    sp->parts, sp->id, &srcinfo))
1558				continue;
1559			if (!wanted->parts->pscheme->
1560			    adapt_foreign_part_info(wanted->parts,
1561			    &cinfo, sp->parts->pscheme, &srcinfo))
1562				continue;
1563
1564			/* find space for cinfo and add a partition */
1565			size_t cnt = wanted->parts->pscheme->get_free_spaces(
1566			    wanted->parts, &space, 1, want->size-align, align,
1567			    from, -1);
1568			if (cnt == 0)
1569				cnt = wanted->parts->pscheme->get_free_spaces(
1570				    wanted->parts, &space, 1,
1571				    want->size-5*align, align, from, -1);
1572
1573			if (cnt == 0)
1574				continue; /* no free space for this clone */
1575
1576			infos[i] = cinfo;
1577			infos[i].start = space.start;
1578			new_part_id = wanted->parts->pscheme->add_partition(
1579			    wanted->parts, &infos[i], NULL);
1580		} else {
1581#else
1582		{
1583#endif
1584			if (want->size <= 0)
1585				continue;
1586			size_t cnt = wanted->parts->pscheme->get_free_spaces(
1587			    wanted->parts, &space, 1, want->size-align, align,
1588			    from, -1);
1589			if (cnt == 0)
1590				cnt = wanted->parts->pscheme->get_free_spaces(
1591				    wanted->parts, &space, 1,
1592				    want->size-5*align, align, from, -1);
1593
1594			if (cnt == 0)
1595				continue; /* no free space for this partition */
1596
1597			infos[i].start = space.start;
1598			infos[i].size = min(want->size, space.size);
1599			infos[i].nat_type =
1600			    wanted->parts->pscheme->get_fs_part_type(
1601			    want->type, want->fs_type, want->fs_version);
1602			infos[i].last_mounted = want->mount;
1603			infos[i].fs_type = want->fs_type;
1604			infos[i].fs_sub_type = want->fs_version;
1605			infos[i].fs_opt1 = want->fs_opt1;
1606			infos[i].fs_opt2 = want->fs_opt2;
1607			infos[i].fs_opt3 = want->fs_opt3;
1608			if (want->fs_type != FS_UNUSED &&
1609			    want->type != PT_swap) {
1610				want->instflags |= PUIINST_NEWFS;
1611				if (want->mount[0] != 0)
1612					want->instflags |= PUIINST_MOUNT;
1613			}
1614			new_part_id = wanted->parts->pscheme->add_partition(
1615			    wanted->parts, &infos[i], NULL);
1616		}
1617
1618		if (new_part_id == NO_PART)
1619			continue;	/* failed to add, skip */
1620
1621		wanted->parts->pscheme->get_part_info(
1622		    wanted->parts, new_part_id, &infos[i]);
1623		from = roundup(infos[i].start+infos[i].size, align);
1624	}
1625
1626
1627	/*
1628	* If there are any outer partitions that we need as inner ones
1629	 * too, add them to the inner partitioning scheme.
1630	 */
1631	for (i = 0; i < wanted->num; i++) {
1632		struct part_usage_info *want = &wanted->infos[i];
1633
1634		if (want->cur_part_id == NO_PART)
1635			continue;
1636		if (want->flags & PUIFLG_JUST_MOUNTPOINT)
1637			continue;
1638		if (want->size <= 0)
1639			continue;
1640
1641		if ((want->flags & (PUIFLG_ADD_INNER|PUIFLG_IS_OUTER)) !=
1642		    (PUIFLG_ADD_INNER|PUIFLG_IS_OUTER))
1643			continue;
1644
1645		new_part_id = NO_PART;
1646		for (part_id j = 0; new_part_id == NO_PART &&
1647		    j < wanted->parts->num_part; j++) {
1648			struct disk_part_info test;
1649
1650			if (!wanted->parts->pscheme->get_part_info(
1651			    wanted->parts, j, &test))
1652				continue;
1653			if (test.start == want->cur_start &&
1654			    test.size == want->size)
1655				new_part_id = j;
1656		}
1657
1658		if (new_part_id == NO_PART) {
1659			infos[i].start = want->cur_start;
1660			infos[i].size = want->size;
1661			infos[i].nat_type = wanted->parts->pscheme->
1662			    get_fs_part_type(want->type, want->fs_type,
1663			    want->fs_version);
1664			infos[i].last_mounted = want->mount;
1665			infos[i].fs_type = want->fs_type;
1666			infos[i].fs_sub_type = want->fs_version;
1667			infos[i].fs_opt1 = want->fs_opt1;
1668			infos[i].fs_opt2 = want->fs_opt2;
1669			infos[i].fs_opt3 = want->fs_opt3;
1670
1671			if (wanted->parts->pscheme->add_outer_partition
1672			    != NULL)
1673				new_part_id = wanted->parts->pscheme->
1674				    add_outer_partition(
1675				    wanted->parts, &infos[i], NULL);
1676			else
1677				new_part_id = wanted->parts->pscheme->
1678				    add_partition(
1679				    wanted->parts, &infos[i], NULL);
1680
1681			if (new_part_id == NO_PART)
1682				continue;	/* failed to add, skip */
1683		}
1684
1685		wanted->parts->pscheme->get_part_info(
1686		    wanted->parts, new_part_id, &infos[i]);
1687		want->parts = wanted->parts;
1688		if (want->fs_type != FS_UNUSED &&
1689		    want->type != PT_swap) {
1690			want->instflags |= PUIINST_NEWFS;
1691			if (want->mount[0] != 0)
1692				want->instflags |= PUIINST_MOUNT;
1693		}
1694	}
1695
1696	/*
1697	 * Note: all part_ids are invalid now, as we have added things!
1698	 */
1699	for (i = 0; i < wanted->num; i++)
1700		wanted->infos[i].cur_part_id = NO_PART;
1701	for (pno = 0; pno < parts->num_part; pno++) {
1702		struct disk_part_info t;
1703
1704		if (!parts->pscheme->get_part_info(parts, pno, &t))
1705			continue;
1706		if (t.flags & PTI_SPECIAL_PARTS)
1707			continue;
1708
1709		for (i = 0; i < wanted->num; i++) {
1710			if (wanted->infos[i].cur_part_id != NO_PART)
1711				continue;
1712			if (wanted->infos[i].size <= 0)
1713				continue;
1714			if (t.start == infos[i].start) {
1715				wanted->infos[i].cur_part_id = pno;
1716				wanted->infos[i].cur_start = infos[i].start;
1717				wanted->infos[i].cur_flags = infos[i].flags;
1718				break;
1719			}
1720		}
1721	}
1722	free(infos);
1723
1724	/* sort, and sync part ids and wanted->infos[] indices */
1725	sort_and_sync_parts(wanted);
1726}
1727
1728static void
1729replace_by_default(struct disk_partitions *parts,
1730    daddr_t start, daddr_t size, struct partition_usage_set *wanted)
1731{
1732
1733	if (start == 0 && size == parts->disk_size)
1734		parts->pscheme->delete_all_partitions(parts);
1735	else if (parts->pscheme->delete_partitions_in_range != NULL)
1736		parts->pscheme->delete_partitions_in_range(parts, start, size);
1737	else
1738		assert(parts->num_part == 0);
1739
1740	fill_defaults(wanted, parts, start, size);
1741	apply_settings_to_partitions(parts, wanted, start, size);
1742}
1743
1744static bool
1745edit_with_defaults(struct disk_partitions *parts,
1746    daddr_t start, daddr_t size, struct partition_usage_set *wanted)
1747{
1748	bool ok;
1749
1750	fill_defaults(wanted, parts, start, size);
1751	ok = get_ptn_sizes(wanted);
1752	if (ok)
1753		apply_settings_to_partitions(parts, wanted, start, size);
1754	return ok;
1755}
1756
1757/*
1758 * md back-end code for menu-driven BSD disklabel editor.
1759 * returns 0 on failure, 1 on success, -1 for restart.
1760 * fills the install target with a list for newfs/fstab.
1761 */
1762int
1763make_bsd_partitions(struct install_partition_desc *install)
1764{
1765	struct disk_partitions *parts = pm->parts;
1766	const struct disk_partitioning_scheme *pscheme;
1767	struct partition_usage_set wanted;
1768	daddr_t p_start, p_size;
1769	enum layout_type layoutkind = LY_SETSIZES;
1770	bool have_existing;
1771
1772	if (pm && pm->no_part && parts == NULL)
1773		return 1;
1774	if (parts == NULL) {
1775		pscheme = select_part_scheme(pm, NULL, !pm->no_mbr, NULL);
1776		if (pscheme == NULL)
1777			return 0;
1778		parts = pscheme->create_new_for_disk(pm->diskdev,
1779		    0, pm->dlsize, true, NULL);
1780		if (parts == NULL)
1781			return 0;
1782		pm->parts = parts;
1783	} else {
1784		pscheme = parts->pscheme;
1785	}
1786
1787	if (pscheme->secondary_partitions) {
1788		struct disk_partitions *p;
1789
1790		p = pscheme->secondary_partitions(parts, pm->ptstart, false);
1791		if (p) {
1792			parts = p;
1793			pscheme = parts->pscheme;
1794		}
1795	}
1796
1797	have_existing = check_existing_netbsd(parts);
1798
1799	/*
1800	 * Make sure the cylinder size multiplier/divisor and disk size are
1801	 * valid
1802	 */
1803	if (pm->current_cylsize == 0)
1804		pm->current_cylsize = pm->dlcylsize;
1805	if (pm->ptsize == 0)
1806		pm->ptsize = pm->dlsize;
1807
1808	/* Ask for layout type -- standard or special */
1809	if (partman_go == 0) {
1810		char bsd_size[6], min_size[6], x_size[6];
1811
1812		humanize_number(bsd_size, sizeof(bsd_size),
1813		    (uint64_t)pm->ptsize*pm->sectorsize,
1814		    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1815		humanize_number(min_size, sizeof(min_size),
1816		    (uint64_t)(DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE)*MEG,
1817		    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1818		humanize_number(x_size, sizeof(x_size),
1819		    (uint64_t)(DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE
1820		    + XNEEDMB)*MEG,
1821		    "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1822
1823		msg_display_subst(
1824		    have_existing ? MSG_layout_prologue_existing
1825		    : MSG_layout_prologue_none, 6, pm->diskdev,
1826		    msg_string(parts->pscheme->name),
1827		    msg_string(parts->pscheme->short_name),
1828		    bsd_size, min_size, x_size);
1829		msg_display_add_subst(MSG_layout_main, 6,
1830		    pm->diskdev,
1831		    msg_string(parts->pscheme->name),
1832		    msg_string(parts->pscheme->short_name),
1833		    bsd_size, min_size, x_size);
1834		msg_display_add("\n\n");
1835		layoutkind = ask_layout(parts, have_existing);
1836		if (layoutkind == LY_ERROR)
1837			return 0;
1838	}
1839
1840	if (layoutkind == LY_USEDEFAULT || layoutkind == LY_SETSIZES) {
1841		/* calc available disk area for the NetBSD partitions */
1842		p_start = pm->ptstart;
1843		p_size = pm->ptsize;
1844		if (parts->parent != NULL &&
1845		    parts->parent->pscheme->guess_install_target != NULL)
1846			parts->parent->pscheme->guess_install_target(
1847			    parts->parent, &p_start, &p_size);
1848	}
1849	if (layoutkind == LY_OTHERSCHEME) {
1850		parts->pscheme->destroy_part_scheme(parts);
1851		return -1;
1852	} else if (layoutkind == LY_USENONE) {
1853		struct disk_part_free_space space;
1854		size_t cnt;
1855
1856		empty_usage_set_from_parts(&wanted, parts);
1857		cnt = parts->pscheme->get_free_spaces(parts, &space, 1,
1858		0, parts->pscheme->get_part_alignment(parts), 0, -1);
1859		p_start = p_size = 0;
1860		if (cnt == 1) {
1861			p_start = space.start;
1862			p_size = space.size;
1863			wanted.cur_free_space = space.size;
1864		}
1865	} else if (layoutkind == LY_USEDEFAULT) {
1866		replace_by_default(parts, p_start, p_size,
1867		    &wanted);
1868	} else if (layoutkind == LY_SETSIZES) {
1869		if (!edit_with_defaults(parts, p_start, p_size,
1870		    &wanted)) {
1871			free_usage_set(&wanted);
1872			return 0;
1873		}
1874	} else {
1875		usage_set_from_parts(&wanted, parts);
1876	}
1877
1878	/*
1879	 * Make sure the target root partition is properly marked,
1880	 * check for existing EFI boot partition.
1881	 */
1882	bool have_inst_target = false;
1883#ifdef HAVE_EFI_BOOT
1884	daddr_t target_start = -1;
1885#endif
1886	for (size_t i = 0; i < wanted.num; i++) {
1887		if (wanted.infos[i].cur_flags & PTI_INSTALL_TARGET) {
1888			have_inst_target = true;
1889#ifdef HAVE_EFI_BOOT
1890			target_start = wanted.infos[i].cur_start;
1891#endif
1892			break;
1893		 }
1894	}
1895	if (!have_inst_target) {
1896		for (size_t i = 0; i < wanted.num; i++) {
1897			struct disk_part_info info;
1898
1899			if (wanted.infos[i].type != PT_root ||
1900			    strcmp(wanted.infos[i].mount, "/") != 0)
1901				continue;
1902			wanted.infos[i].cur_flags |= PTI_INSTALL_TARGET;
1903
1904			if (!wanted.parts->pscheme->get_part_info(wanted.parts,
1905			    wanted.infos[i].cur_part_id, &info))
1906				break;
1907			info.flags |= PTI_INSTALL_TARGET;
1908			wanted.parts->pscheme->set_part_info(wanted.parts,
1909			    wanted.infos[i].cur_part_id, &info, NULL);
1910#ifdef HAVE_EFI_BOOT
1911			target_start = wanted.infos[i].cur_start;
1912#endif
1913			break;
1914		}
1915	}
1916#ifdef HAVE_EFI_BOOT
1917	size_t boot_part = ~0U;
1918	for (part_id i = 0; i < wanted.num; i++) {
1919		if ((wanted.infos[i].cur_flags & PTI_BOOT) != 0 ||
1920		    wanted.infos[i].type ==  PT_EFI_SYSTEM) {
1921			boot_part = i;
1922			break;
1923		}
1924	}
1925	if (boot_part == ~0U) {
1926		for (part_id i = 0; i < wanted.num; i++) {
1927			/*
1928			 * heuristic to recognize existing MBR FAT
1929			 * partitions as EFI without looking for
1930			 * details
1931			 */
1932			if ((wanted.infos[i].type != PT_FAT &&
1933			    wanted.infos[i].type != PT_EFI_SYSTEM) ||
1934			    wanted.infos[i].fs_type != FS_MSDOS)
1935				continue;
1936			daddr_t ps = wanted.infos[i].cur_start;
1937			daddr_t pe = ps + wanted.infos[i].size;
1938			if (target_start >= 0 &&
1939			   (ps >= target_start || pe >= target_start))
1940				continue;
1941			boot_part = i;
1942			break;
1943		}
1944	}
1945	if (boot_part != ~0U) {
1946		struct disk_part_info info;
1947
1948		if (wanted.parts->pscheme->get_part_info(wanted.parts,
1949		    wanted.infos[boot_part].cur_part_id, &info)) {
1950			info.flags |= PTI_BOOT;
1951			wanted.parts->pscheme->set_part_info(wanted.parts,
1952			    wanted.infos[boot_part].cur_part_id, &info, NULL);
1953		}
1954		wanted.infos[boot_part].instflags |= PUIINST_BOOT;
1955	}
1956#endif
1957
1958	/*
1959	 * OK, we have a partition table. Give the user the chance to
1960	 * edit it and verify it's OK, or abort altogether.
1961	 */
1962 	for (;;) {
1963		int rv = edit_and_check_label(pm, &wanted, true);
1964		if (rv == 0) {
1965			msg_display(MSG_abort_part);
1966			free_usage_set(&wanted);
1967			return 0;
1968		}
1969		/* update install infos */
1970		install->num = wanted.num;
1971		install->infos = wanted.infos;
1972		install->write_back = wanted.write_back;
1973		install->num_write_back = wanted.num_write_back;
1974		/* and check them */
1975		if (check_partitions(install))
1976			break;
1977	}
1978
1979	/* we moved infos from wanted to install target */
1980	wanted.infos = NULL;
1981	wanted.write_back = NULL;
1982	free_usage_set(&wanted);
1983
1984	/* Everything looks OK. */
1985	return 1;
1986}
1987
1988#ifndef MD_NEED_BOOTBLOCK
1989#define MD_NEED_BOOTBLOCK(A)	true
1990#endif
1991
1992/*
1993 * check that there is at least a / somewhere.
1994 */
1995bool
1996check_partitions(struct install_partition_desc *install)
1997{
1998#ifdef HAVE_BOOTXX_xFS
1999	int rv = 1;
2000	char *bootxx;
2001#endif
2002#ifndef HAVE_UFS2_BOOT
2003	size_t i;
2004#endif
2005
2006#ifdef HAVE_BOOTXX_xFS
2007	if (MD_NEED_BOOTBLOCK(install)) {
2008		/* check if we have boot code for the root partition type */
2009		bootxx = bootxx_name(install);
2010		if (bootxx != NULL) {
2011			rv = access(bootxx, R_OK);
2012			free(bootxx);
2013		} else
2014			rv = -1;
2015		if (rv != 0) {
2016			hit_enter_to_continue(NULL, MSG_No_Bootcode);
2017			return false;
2018		}
2019	}
2020#endif
2021#ifndef HAVE_UFS2_BOOT
2022	if (MD_NEED_BOOTBLOCK(install)) {
2023		for (i = 0; i < install->num; i++) {
2024			if (install->infos[i].type != PT_root)
2025				continue;
2026			if (strcmp(install->infos[i].mount, "/") != 0)
2027				continue;
2028			if (install->infos[i].fs_type != FS_BSDFFS)
2029				continue;
2030			if (install->infos[i].fs_version < 2)
2031				continue;
2032			hit_enter_to_continue(NULL, MSG_cannot_ufs2_root);
2033			return false;
2034		}
2035	}
2036#endif
2037
2038	return md_check_partitions(install);
2039}
2040