gpart_ops.c revision 330449
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2011 Nathan Whitehorn
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: stable/11/usr.sbin/bsdinstall/partedit/gpart_ops.c 330449 2018-03-05 07:26:05Z eadler $
29 */
30
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <errno.h>
34#include <libutil.h>
35#include <inttypes.h>
36
37#include <libgeom.h>
38#include <dialog.h>
39#include <dlg_keys.h>
40
41#include "partedit.h"
42
43#define GPART_FLAGS "x" /* Do not commit changes by default */
44
45static void
46gpart_show_error(const char *title, const char *explanation, const char *errstr)
47{
48	char *errmsg;
49	char message[512];
50	int error;
51
52	if (explanation == NULL)
53		explanation = "";
54
55	error = strtol(errstr, &errmsg, 0);
56	if (errmsg != errstr) {
57		while (errmsg[0] == ' ')
58			errmsg++;
59		if (errmsg[0] != '\0')
60			sprintf(message, "%s%s. %s", explanation,
61			    strerror(error), errmsg);
62		else
63			sprintf(message, "%s%s", explanation, strerror(error));
64	} else {
65		sprintf(message, "%s%s", explanation, errmsg);
66	}
67
68	dialog_msgbox(title, message, 0, 0, TRUE);
69}
70
71static int
72scheme_supports_labels(const char *scheme)
73{
74	if (strcmp(scheme, "APM") == 0)
75		return (1);
76	if (strcmp(scheme, "GPT") == 0)
77		return (1);
78	if (strcmp(scheme, "PC98") == 0)
79		return (1);
80
81	return (0);
82}
83
84static void
85newfs_command(const char *fstype, char *command, int use_default)
86{
87	if (strcmp(fstype, "freebsd-ufs") == 0) {
88		int i;
89		DIALOG_LISTITEM items[] = {
90			{"UFS1", "UFS Version 1",
91			    "Use version 1 of the UFS file system instead "
92			    "of version 2 (not recommended)", 0 },
93			{"SU", "Softupdates",
94			    "Enable softupdates (default)", 1 },
95			{"SUJ", "Softupdates journaling",
96			    "Enable file system journaling (default - "
97			    "turn off for SSDs)", 1 },
98			{"TRIM", "Enable SSD TRIM support",
99			    "Enable TRIM support, useful on solid-state drives",
100			    0 },
101		};
102
103		if (!use_default) {
104			int choice;
105			choice = dlg_checklist("UFS Options", "", 0, 0, 0,
106			    nitems(items), items, NULL,
107			    FLAG_CHECK, &i);
108			if (choice == 1) /* Cancel */
109				return;
110		}
111
112		strcpy(command, "newfs ");
113		for (i = 0; i < (int)nitems(items); i++) {
114			if (items[i].state == 0)
115				continue;
116			if (strcmp(items[i].name, "UFS1") == 0)
117				strcat(command, "-O1 ");
118			else if (strcmp(items[i].name, "SU") == 0)
119				strcat(command, "-U ");
120			else if (strcmp(items[i].name, "SUJ") == 0)
121				strcat(command, "-j ");
122			else if (strcmp(items[i].name, "TRIM") == 0)
123				strcat(command, "-t ");
124		}
125	} else if (strcmp(fstype, "freebsd-zfs") == 0) {
126		int i;
127		DIALOG_LISTITEM items[] = {
128			{"fletcher4", "checksum algorithm: fletcher4",
129			    "Use fletcher4 for data integrity checking. "
130			    "(default)", 1 },
131			{"fletcher2", "checksum algorithm: fletcher2",
132			    "Use fletcher2 for data integrity checking. "
133			    "(not recommended)", 0 },
134			{"sha256", "checksum algorithm: sha256",
135			    "Use sha256 for data integrity checking. "
136			    "(not recommended)", 0 },
137			{"atime", "Update atimes for files",
138			    "Disable atime update", 0 },
139		};
140
141		if (!use_default) {
142			int choice;
143			choice = dlg_checklist("ZFS Options", "", 0, 0, 0,
144			    nitems(items), items, NULL,
145			    FLAG_CHECK, &i);
146			if (choice == 1) /* Cancel */
147				return;
148		}
149
150		strcpy(command, "zpool create -f -m none ");
151		if (getenv("BSDINSTALL_TMPBOOT") != NULL) {
152			char zfsboot_path[MAXPATHLEN];
153			snprintf(zfsboot_path, sizeof(zfsboot_path), "%s/zfs",
154			    getenv("BSDINSTALL_TMPBOOT"));
155			mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP |
156			    S_IROTH | S_IXOTH);
157			sprintf(command, "%s -o cachefile=%s/zpool.cache ",
158			    command, zfsboot_path);
159		}
160		for (i = 0; i < (int)nitems(items); i++) {
161			if (items[i].state == 0)
162				continue;
163			if (strcmp(items[i].name, "fletcher4") == 0)
164				strcat(command, "-O checksum=fletcher4 ");
165			else if (strcmp(items[i].name, "fletcher2") == 0)
166				strcat(command, "-O checksum=fletcher2 ");
167			else if (strcmp(items[i].name, "sha256") == 0)
168				strcat(command, "-O checksum=sha256 ");
169			else if (strcmp(items[i].name, "atime") == 0)
170				strcat(command, "-O atime=off ");
171		}
172	} else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0) {
173		int i;
174		DIALOG_LISTITEM items[] = {
175			{"FAT32", "FAT Type 32",
176			    "Create a FAT32 filesystem (default)", 1 },
177			{"FAT16", "FAT Type 16",
178			    "Create a FAT16 filesystem", 0 },
179			{"FAT12", "FAT Type 12",
180			    "Create a FAT12 filesystem", 0 },
181		};
182
183		if (!use_default) {
184			int choice;
185			choice = dlg_checklist("FAT Options", "", 0, 0, 0,
186			    nitems(items), items, NULL,
187			    FLAG_RADIO, &i);
188			if (choice == 1) /* Cancel */
189				return;
190		}
191
192		strcpy(command, "newfs_msdos ");
193		for (i = 0; i < (int)nitems(items); i++) {
194			if (items[i].state == 0)
195				continue;
196			if (strcmp(items[i].name, "FAT32") == 0)
197				strcat(command, "-F 32 ");
198			else if (strcmp(items[i].name, "FAT16") == 0)
199				strcat(command, "-F 16 ");
200			else if (strcmp(items[i].name, "FAT12") == 0)
201				strcat(command, "-F 12 ");
202		}
203	} else {
204		if (!use_default)
205			dialog_msgbox("Error", "No configurable options exist "
206			    "for this filesystem.", 0, 0, TRUE);
207		command[0] = '\0';
208	}
209}
210
211const char *
212choose_part_type(const char *def_scheme)
213{
214	int cancel, choice;
215	const char *scheme = NULL;
216
217	DIALOG_LISTITEM items[] = {
218		{"APM", "Apple Partition Map",
219		    "Bootable on PowerPC Apple Hardware", 0 },
220		{"BSD", "BSD Labels",
221		    "Bootable on most x86 systems", 0 },
222		{"GPT", "GUID Partition Table",
223		    "Bootable on most x86 systems and EFI aware ARM64", 0 },
224		{"MBR", "DOS Partitions",
225		    "Bootable on most x86 systems", 0 },
226		{"PC98", "NEC PC9801 Partition Table",
227		    "Bootable on NEC PC9801 systems", 0 },
228		{"VTOC8", "Sun VTOC8 Partition Table",
229		    "Bootable on Sun SPARC systems", 0 },
230	};
231
232parttypemenu:
233	dialog_vars.default_item = __DECONST(char *, def_scheme);
234	cancel = dlg_menu("Partition Scheme",
235	    "Select a partition scheme for this volume:", 0, 0, 0,
236	    nitems(items), items, &choice, NULL);
237	dialog_vars.default_item = NULL;
238
239	if (cancel)
240		return NULL;
241
242	if (!is_scheme_bootable(items[choice].name)) {
243		char message[512];
244		sprintf(message, "This partition scheme (%s) is not "
245		    "bootable on this platform. Are you sure you want "
246		    "to proceed?", items[choice].name);
247		dialog_vars.defaultno = TRUE;
248		cancel = dialog_yesno("Warning", message, 0, 0);
249		dialog_vars.defaultno = FALSE;
250		if (cancel) /* cancel */
251			goto parttypemenu;
252	}
253
254	scheme = items[choice].name;
255
256	return scheme;
257}
258
259int
260gpart_partition(const char *lg_name, const char *scheme)
261{
262	int cancel;
263	struct gctl_req *r;
264	const char *errstr;
265
266schememenu:
267	if (scheme == NULL) {
268		scheme = choose_part_type(default_scheme());
269
270		if (scheme == NULL)
271			return (-1);
272
273		if (!is_scheme_bootable(scheme)) {
274			char message[512];
275			sprintf(message, "This partition scheme (%s) is not "
276			    "bootable on this platform. Are you sure you want "
277			    "to proceed?", scheme);
278			dialog_vars.defaultno = TRUE;
279			cancel = dialog_yesno("Warning", message, 0, 0);
280			dialog_vars.defaultno = FALSE;
281			if (cancel) { /* cancel */
282				/* Reset scheme so user can choose another */
283				scheme = NULL;
284				goto schememenu;
285			}
286		}
287	}
288
289	r = gctl_get_handle();
290	gctl_ro_param(r, "class", -1, "PART");
291	gctl_ro_param(r, "arg0", -1, lg_name);
292	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
293	gctl_ro_param(r, "scheme", -1, scheme);
294	gctl_ro_param(r, "verb", -1, "create");
295
296	errstr = gctl_issue(r);
297	if (errstr != NULL && errstr[0] != '\0') {
298		gpart_show_error("Error", NULL, errstr);
299		gctl_free(r);
300		scheme = NULL;
301		goto schememenu;
302	}
303	gctl_free(r);
304
305	if (bootcode_path(scheme) != NULL)
306		get_part_metadata(lg_name, 1)->bootcode = 1;
307	return (0);
308}
309
310static void
311gpart_activate(struct gprovider *pp)
312{
313	struct gconfig *gc;
314	struct gctl_req *r;
315	const char *errstr, *scheme;
316	const char *attribute = NULL;
317	intmax_t idx;
318
319	/*
320	 * Some partition schemes need this partition to be marked 'active'
321	 * for it to be bootable.
322	 */
323	LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
324		if (strcmp(gc->lg_name, "scheme") == 0) {
325			scheme = gc->lg_val;
326			break;
327		}
328	}
329
330	if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0 ||
331	    strcmp(scheme, "PC98") == 0)
332		attribute = "active";
333	else
334		return;
335
336	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
337		if (strcmp(gc->lg_name, "index") == 0) {
338			idx = atoi(gc->lg_val);
339			break;
340		}
341	}
342
343	r = gctl_get_handle();
344	gctl_ro_param(r, "class", -1, "PART");
345	gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
346	gctl_ro_param(r, "verb", -1, "set");
347	gctl_ro_param(r, "attrib", -1, attribute);
348	gctl_ro_param(r, "index", sizeof(idx), &idx);
349
350	errstr = gctl_issue(r);
351	if (errstr != NULL && errstr[0] != '\0')
352		gpart_show_error("Error", "Error marking partition active:",
353		    errstr);
354	gctl_free(r);
355}
356
357void
358gpart_set_root(const char *lg_name, const char *attribute)
359{
360	struct gctl_req *r;
361	const char *errstr;
362
363	r = gctl_get_handle();
364	gctl_ro_param(r, "class", -1, "PART");
365	gctl_ro_param(r, "arg0", -1, lg_name);
366	gctl_ro_param(r, "flags", -1, "C");
367	gctl_ro_param(r, "verb", -1, "set");
368	gctl_ro_param(r, "attrib", -1, attribute);
369
370	errstr = gctl_issue(r);
371	if (errstr != NULL && errstr[0] != '\0')
372		gpart_show_error("Error", "Error setting parameter on disk:",
373		    errstr);
374	gctl_free(r);
375}
376
377static void
378gpart_bootcode(struct ggeom *gp)
379{
380	const char *bootcode;
381	struct gconfig *gc;
382	struct gctl_req *r;
383	const char *errstr, *scheme;
384	uint8_t *boot;
385	size_t bootsize, bytes;
386	int bootfd;
387
388	/*
389	 * Write default bootcode to the newly partitioned disk, if that
390	 * applies on this platform.
391	 */
392	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
393		if (strcmp(gc->lg_name, "scheme") == 0) {
394			scheme = gc->lg_val;
395			break;
396		}
397	}
398
399	bootcode = bootcode_path(scheme);
400	if (bootcode == NULL)
401		return;
402
403	bootfd = open(bootcode, O_RDONLY);
404	if (bootfd < 0) {
405		dialog_msgbox("Bootcode Error", strerror(errno), 0, 0,
406		    TRUE);
407		return;
408	}
409
410	bootsize = lseek(bootfd, 0, SEEK_END);
411	boot = malloc(bootsize);
412	lseek(bootfd, 0, SEEK_SET);
413	bytes = 0;
414	while (bytes < bootsize)
415		bytes += read(bootfd, boot + bytes, bootsize - bytes);
416	close(bootfd);
417
418	r = gctl_get_handle();
419	gctl_ro_param(r, "class", -1, "PART");
420	gctl_ro_param(r, "arg0", -1, gp->lg_name);
421	gctl_ro_param(r, "verb", -1, "bootcode");
422	gctl_ro_param(r, "bootcode", bootsize, boot);
423
424	errstr = gctl_issue(r);
425	if (errstr != NULL && errstr[0] != '\0')
426		gpart_show_error("Bootcode Error", NULL, errstr);
427	gctl_free(r);
428	free(boot);
429}
430
431static void
432gpart_partcode(struct gprovider *pp, const char *fstype)
433{
434	struct gconfig *gc;
435	const char *scheme;
436	const char *indexstr;
437	char message[255], command[255];
438
439	LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
440		if (strcmp(gc->lg_name, "scheme") == 0) {
441			scheme = gc->lg_val;
442			break;
443		}
444	}
445
446	/* Make sure this partition scheme needs partcode on this platform */
447	if (partcode_path(scheme, fstype) == NULL)
448		return;
449
450	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
451		if (strcmp(gc->lg_name, "index") == 0) {
452			indexstr = gc->lg_val;
453			break;
454		}
455	}
456
457	/* Shell out to gpart for partcode for now */
458	sprintf(command, "gpart bootcode -p %s -i %s %s",
459	    partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name);
460	if (system(command) != 0) {
461		sprintf(message, "Error installing partcode on partition %s",
462		    pp->lg_name);
463		dialog_msgbox("Error", message, 0, 0, TRUE);
464	}
465}
466
467void
468gpart_destroy(struct ggeom *lg_geom)
469{
470	struct gctl_req *r;
471	struct gprovider *pp;
472	const char *errstr;
473	int force = 1;
474
475	/* Delete all child metadata */
476	LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider)
477		gpart_delete(pp);
478
479	/* Revert any local changes to get this geom into a pristine state */
480	r = gctl_get_handle();
481	gctl_ro_param(r, "class", -1, "PART");
482	gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
483	gctl_ro_param(r, "verb", -1, "undo");
484	gctl_issue(r); /* Ignore errors -- these are non-fatal */
485	gctl_free(r);
486
487	/* Now destroy the geom itself */
488	r = gctl_get_handle();
489	gctl_ro_param(r, "class", -1, "PART");
490	gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
491	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
492	gctl_ro_param(r, "force", sizeof(force), &force);
493	gctl_ro_param(r, "verb", -1, "destroy");
494	errstr = gctl_issue(r);
495	if (errstr != NULL && errstr[0] != '\0') {
496		/*
497		 * Check if we reverted away the existence of the geom
498		 * altogether. Show all other errors to the user.
499		 */
500		if (strtol(errstr, NULL, 0) != EINVAL)
501			gpart_show_error("Error", NULL, errstr);
502	}
503	gctl_free(r);
504
505	/* And any metadata associated with the partition scheme itself */
506	delete_part_metadata(lg_geom->lg_name);
507}
508
509void
510gpart_edit(struct gprovider *pp)
511{
512	struct gctl_req *r;
513	struct gconfig *gc;
514	struct gconsumer *cp;
515	struct ggeom *geom;
516	const char *errstr, *oldtype, *scheme;
517	struct partition_metadata *md;
518	char sizestr[32];
519	char newfs[255];
520	intmax_t idx;
521	int hadlabel, choice, junk, nitems;
522	unsigned i;
523
524	DIALOG_FORMITEM items[] = {
525		{0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0,
526		    FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
527		    "freebsd-swap)", FALSE},
528		{0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0,
529		    FALSE, "Partition size. Append K, M, G for kilobytes, "
530		    "megabytes or gigabytes.", FALSE},
531		{0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
532		    FALSE, "Path at which to mount this partition (leave blank "
533		    "for swap, set to / for root filesystem)", FALSE},
534		{0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
535		    "Partition name. Not all partition schemes support this.",
536		    FALSE},
537	};
538
539	/*
540	 * Find the PART geom we are manipulating. This may be a consumer of
541	 * this provider, or its parent. Check the consumer case first.
542	 */
543	geom = NULL;
544	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
545		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
546			/* Check for zombie geoms, treating them as blank */
547			scheme = NULL;
548			LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) {
549				if (strcmp(gc->lg_name, "scheme") == 0) {
550					scheme = gc->lg_val;
551					break;
552				}
553			}
554			if (scheme == NULL || strcmp(scheme, "(none)") == 0) {
555				gpart_partition(cp->lg_geom->lg_name, NULL);
556				return;
557			}
558
559			/* If this is a nested partition, edit as usual */
560			if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
561				break;
562
563			/* Destroy the geom and all sub-partitions */
564			gpart_destroy(cp->lg_geom);
565
566			/* Now re-partition and return */
567			gpart_partition(cp->lg_geom->lg_name, NULL);
568			return;
569		}
570
571	if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
572		geom = pp->lg_geom;
573
574	if (geom == NULL) {
575		/* Disk not partitioned, so partition it */
576		gpart_partition(pp->lg_name, NULL);
577		return;
578	}
579
580	LIST_FOREACH(gc, &geom->lg_config, lg_config) {
581		if (strcmp(gc->lg_name, "scheme") == 0) {
582			scheme = gc->lg_val;
583			break;
584		}
585	}
586
587	nitems = scheme_supports_labels(scheme) ? 4 : 3;
588
589	/* Edit editable parameters of a partition */
590	hadlabel = 0;
591	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
592		if (strcmp(gc->lg_name, "type") == 0) {
593			oldtype = gc->lg_val;
594			items[0].text = gc->lg_val;
595		}
596		if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) {
597			hadlabel = 1;
598			items[3].text = gc->lg_val;
599		}
600		if (strcmp(gc->lg_name, "index") == 0)
601			idx = atoi(gc->lg_val);
602	}
603
604	TAILQ_FOREACH(md, &part_metadata, metadata) {
605		if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) {
606			if (md->fstab != NULL)
607				items[2].text = md->fstab->fs_file;
608			break;
609		}
610	}
611
612	humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE,
613	    HN_NOSPACE | HN_DECIMAL);
614	items[1].text = sizestr;
615
616editpart:
617	choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk);
618
619	if (choice) /* Cancel pressed */
620		goto endedit;
621
622	/* Check if the label has a / in it */
623	if (strchr(items[3].text, '/') != NULL) {
624		dialog_msgbox("Error", "Label contains a /, which is not an "
625		    "allowed character.", 0, 0, TRUE);
626		goto editpart;
627	}
628
629	r = gctl_get_handle();
630	gctl_ro_param(r, "class", -1, "PART");
631	gctl_ro_param(r, "arg0", -1, geom->lg_name);
632	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
633	gctl_ro_param(r, "verb", -1, "modify");
634	gctl_ro_param(r, "index", sizeof(idx), &idx);
635	if (hadlabel || items[3].text[0] != '\0')
636		gctl_ro_param(r, "label", -1, items[3].text);
637	gctl_ro_param(r, "type", -1, items[0].text);
638	errstr = gctl_issue(r);
639	if (errstr != NULL && errstr[0] != '\0') {
640		gpart_show_error("Error", NULL, errstr);
641		gctl_free(r);
642		goto editpart;
643	}
644	gctl_free(r);
645
646	newfs_command(items[0].text, newfs, 1);
647	set_default_part_metadata(pp->lg_name, scheme, items[0].text,
648	    items[2].text, (strcmp(oldtype, items[0].text) != 0) ?
649	    newfs : NULL);
650
651endedit:
652	if (strcmp(oldtype, items[0].text) != 0 && cp != NULL)
653		gpart_destroy(cp->lg_geom);
654	if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text,
655	    "freebsd") == 0)
656		gpart_partition(pp->lg_name, "BSD");
657
658	for (i = 0; i < nitems(items); i++)
659		if (items[i].text_free)
660			free(items[i].text);
661}
662
663void
664set_default_part_metadata(const char *name, const char *scheme,
665    const char *type, const char *mountpoint, const char *newfs)
666{
667	struct partition_metadata *md;
668	char *zpool_name = NULL;
669	int i;
670
671	/* Set part metadata */
672	md = get_part_metadata(name, 1);
673
674	if (newfs) {
675		if (md->newfs != NULL) {
676			free(md->newfs);
677			md->newfs = NULL;
678		}
679
680		if (newfs != NULL && newfs[0] != '\0') {
681			md->newfs = malloc(strlen(newfs) + strlen(" /dev/") +
682			    strlen(mountpoint) + 5 + strlen(name) + 1);
683			if (strcmp("freebsd-zfs", type) == 0) {
684				zpool_name = strdup((strlen(mountpoint) == 1) ?
685				    "root" : &mountpoint[1]);
686				for (i = 0; zpool_name[i] != 0; i++)
687					if (!isalnum(zpool_name[i]))
688						zpool_name[i] = '_';
689				sprintf(md->newfs, "%s %s /dev/%s", newfs,
690				    zpool_name, name);
691			} else {
692				sprintf(md->newfs, "%s /dev/%s", newfs, name);
693			}
694		}
695	}
696
697	if (strcmp(type, "freebsd-swap") == 0)
698		mountpoint = "none";
699	if (strcmp(type, bootpart_type(scheme)) == 0)
700		md->bootcode = 1;
701
702	/* VTOC8 needs partcode at the start of partitions */
703	if (strcmp(scheme, "VTOC8") == 0 && (strcmp(type, "freebsd-ufs") == 0
704	    || strcmp(type, "freebsd-zfs") == 0))
705		md->bootcode = 1;
706
707	if (mountpoint == NULL || mountpoint[0] == '\0') {
708		if (md->fstab != NULL) {
709			free(md->fstab->fs_spec);
710			free(md->fstab->fs_file);
711			free(md->fstab->fs_vfstype);
712			free(md->fstab->fs_mntops);
713			free(md->fstab->fs_type);
714			free(md->fstab);
715			md->fstab = NULL;
716		}
717	} else {
718		if (md->fstab == NULL) {
719			md->fstab = malloc(sizeof(struct fstab));
720		} else {
721			free(md->fstab->fs_spec);
722			free(md->fstab->fs_file);
723			free(md->fstab->fs_vfstype);
724			free(md->fstab->fs_mntops);
725			free(md->fstab->fs_type);
726		}
727		if (strcmp("freebsd-zfs", type) == 0) {
728			md->fstab->fs_spec = strdup(zpool_name);
729		} else {
730			md->fstab->fs_spec = malloc(strlen(name) +
731			    strlen("/dev/") + 1);
732			sprintf(md->fstab->fs_spec, "/dev/%s", name);
733		}
734		md->fstab->fs_file = strdup(mountpoint);
735		/* Get VFS from text after freebsd-, if possible */
736		if (strncmp("freebsd-", type, 8) == 0)
737			md->fstab->fs_vfstype = strdup(&type[8]);
738		else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0)
739			md->fstab->fs_vfstype = strdup("msdosfs");
740		else
741			md->fstab->fs_vfstype = strdup(type); /* Guess */
742		if (strcmp(type, "freebsd-swap") == 0) {
743			md->fstab->fs_type = strdup(FSTAB_SW);
744			md->fstab->fs_freq = 0;
745			md->fstab->fs_passno = 0;
746		} else if (strcmp(type, "freebsd-zfs") == 0) {
747			md->fstab->fs_type = strdup(FSTAB_RW);
748			md->fstab->fs_freq = 0;
749			md->fstab->fs_passno = 0;
750		} else {
751			md->fstab->fs_type = strdup(FSTAB_RW);
752			if (strcmp(mountpoint, "/") == 0) {
753				md->fstab->fs_freq = 1;
754				md->fstab->fs_passno = 1;
755			} else {
756				md->fstab->fs_freq = 2;
757				md->fstab->fs_passno = 2;
758			}
759		}
760		md->fstab->fs_mntops = strdup(md->fstab->fs_type);
761	}
762
763	if (zpool_name != NULL)
764		free(zpool_name);
765}
766
767static
768int part_compare(const void *xa, const void *xb)
769{
770	struct gprovider **a = (struct gprovider **)xa;
771	struct gprovider **b = (struct gprovider **)xb;
772	intmax_t astart, bstart;
773	struct gconfig *gc;
774
775	astart = bstart = 0;
776	LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
777		if (strcmp(gc->lg_name, "start") == 0) {
778			astart = strtoimax(gc->lg_val, NULL, 0);
779			break;
780		}
781	LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
782		if (strcmp(gc->lg_name, "start") == 0) {
783			bstart = strtoimax(gc->lg_val, NULL, 0);
784			break;
785		}
786
787	if (astart < bstart)
788		return -1;
789	else if (astart > bstart)
790		return 1;
791	else
792		return 0;
793}
794
795intmax_t
796gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
797{
798	struct gconfig *gc;
799	struct gprovider *pp, **providers;
800	intmax_t sectorsize, stripesize, offset;
801	intmax_t lastend;
802	intmax_t start, end;
803	intmax_t maxsize, maxstart;
804	intmax_t partstart, partend;
805	int i, nparts;
806
807	/* Now get the maximum free size and free start */
808	start = end = 0;
809	LIST_FOREACH(gc, &geom->lg_config, lg_config) {
810		if (strcmp(gc->lg_name, "first") == 0)
811			start = strtoimax(gc->lg_val, NULL, 0);
812		if (strcmp(gc->lg_name, "last") == 0)
813			end = strtoimax(gc->lg_val, NULL, 0);
814	}
815
816	i = nparts = 0;
817	LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
818		nparts++;
819	providers = calloc(nparts, sizeof(providers[0]));
820	LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
821		providers[i++] = pp;
822	qsort(providers, nparts, sizeof(providers[0]), part_compare);
823
824	lastend = start - 1;
825	maxsize = 0;
826	for (i = 0; i < nparts; i++) {
827		pp = providers[i];
828
829		LIST_FOREACH(gc, &pp->lg_config, lg_config) {
830			if (strcmp(gc->lg_name, "start") == 0)
831				partstart = strtoimax(gc->lg_val, NULL, 0);
832			if (strcmp(gc->lg_name, "end") == 0)
833				partend = strtoimax(gc->lg_val, NULL, 0);
834		}
835
836		if (partstart - lastend > maxsize) {
837			maxsize = partstart - lastend - 1;
838			maxstart = lastend + 1;
839		}
840
841		lastend = partend;
842	}
843
844	if (end - lastend > maxsize) {
845		maxsize = end - lastend - 1;
846		maxstart = lastend + 1;
847	}
848
849	pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
850
851	/*
852	 * Round the start and size of the largest available space up to
853	 * the nearest multiple of the adjusted stripe size.
854	 *
855	 * The adjusted stripe size is the least common multiple of the
856	 * actual stripe size, or the sector size if no stripe size was
857	 * reported, and 4096.  The reason for this is that contemporary
858	 * disks often have 4096-byte physical sectors but report 512
859	 * bytes instead for compatibility with older / broken operating
860	 * systems and BIOSes.  For the same reasons, virtualized storage
861	 * may also report a 512-byte stripe size, or none at all.
862	 */
863	sectorsize = pp->lg_sectorsize;
864	if ((stripesize = pp->lg_stripesize) == 0)
865		stripesize = sectorsize;
866	while (stripesize % 4096 != 0)
867		stripesize *= 2;
868	if ((offset = maxstart * sectorsize % stripesize) != 0) {
869		offset = (stripesize - offset) / sectorsize;
870		maxstart += offset;
871		maxsize -= offset;
872	}
873
874	if (npartstart != NULL)
875		*npartstart = maxstart;
876
877	return (maxsize);
878}
879
880void
881gpart_create(struct gprovider *pp, char *default_type, char *default_size,
882     char *default_mountpoint, char **partname, int interactive)
883{
884	struct gctl_req *r;
885	struct gconfig *gc;
886	struct gconsumer *cp;
887	struct ggeom *geom;
888	const char *errstr, *scheme;
889	char sizestr[32], startstr[32], output[64], *newpartname;
890	char newfs[255], options_fstype[64];
891	intmax_t maxsize, size, sector, firstfree, stripe;
892	uint64_t bytes;
893	int nitems, choice, junk;
894	unsigned i;
895
896	DIALOG_FORMITEM items[] = {
897		{0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0,
898		    FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
899		    "freebsd-swap)", FALSE},
900		{0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0,
901		    FALSE, "Partition size. Append K, M, G for kilobytes, "
902		    "megabytes or gigabytes.", FALSE},
903		{0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
904		    FALSE, "Path at which to mount partition (blank for "
905		    "swap, / for root filesystem)", FALSE},
906		{0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
907		    "Partition name. Not all partition schemes support this.",
908		    FALSE},
909	};
910
911	if (partname != NULL)
912		*partname = NULL;
913
914	/* Record sector and stripe sizes */
915	sector = pp->lg_sectorsize;
916	stripe = pp->lg_stripesize;
917
918	/*
919	 * Find the PART geom we are manipulating. This may be a consumer of
920	 * this provider, or its parent. Check the consumer case first.
921	 */
922	geom = NULL;
923	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
924		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
925			geom = cp->lg_geom;
926			break;
927		}
928
929	if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
930		geom = pp->lg_geom;
931
932	/* Now get the partition scheme */
933	scheme = NULL;
934	if (geom != NULL) {
935		LIST_FOREACH(gc, &geom->lg_config, lg_config)
936			if (strcmp(gc->lg_name, "scheme") == 0)
937				scheme = gc->lg_val;
938	}
939
940	if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
941		if (gpart_partition(pp->lg_name, NULL) == 0)
942			dialog_msgbox("",
943			    "The partition table has been successfully created."
944			    " Please press Create again to create partitions.",
945			    0, 0, TRUE);
946
947		return;
948	}
949
950	/*
951	 * If we still don't have a geom, either the user has
952	 * canceled partitioning or there has been an error which has already
953	 * been displayed, so bail.
954	 */
955	if (geom == NULL)
956		return;
957
958	maxsize = size = gpart_max_free(geom, &firstfree);
959	if (size <= 0) {
960		dialog_msgbox("Error", "No free space left on device.", 0, 0,
961		    TRUE);
962		return;
963	}
964
965	humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
966	    HN_NOSPACE | HN_DECIMAL);
967	items[1].text = sizestr;
968
969	/* Special-case the MBR default type for nested partitions */
970	if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "PC98") == 0) {
971		items[0].text = "freebsd";
972		items[0].help = "Filesystem type (e.g. freebsd, fat32)";
973	}
974
975	nitems = scheme_supports_labels(scheme) ? 4 : 3;
976
977	if (default_type != NULL)
978		items[0].text = default_type;
979	if (default_size != NULL)
980		items[1].text = default_size;
981	if (default_mountpoint != NULL)
982		items[2].text = default_mountpoint;
983
984	/* Default options */
985	strncpy(options_fstype, items[0].text,
986	    sizeof(options_fstype));
987	newfs_command(options_fstype, newfs, 1);
988addpartform:
989	if (interactive) {
990		dialog_vars.extra_label = "Options";
991		dialog_vars.extra_button = TRUE;
992		choice = dlg_form("Add Partition", "", 0, 0, 0, nitems,
993		    items, &junk);
994		dialog_vars.extra_button = FALSE;
995		switch (choice) {
996		case 0: /* OK */
997			break;
998		case 1: /* Cancel */
999			return;
1000		case 3: /* Options */
1001			strncpy(options_fstype, items[0].text,
1002			    sizeof(options_fstype));
1003			newfs_command(options_fstype, newfs, 0);
1004			goto addpartform;
1005		}
1006	}
1007
1008	/*
1009	 * If the user changed the fs type after specifying options, undo
1010	 * their choices in favor of the new filesystem's defaults.
1011	 */
1012	if (strcmp(options_fstype, items[0].text) != 0) {
1013		strncpy(options_fstype, items[0].text, sizeof(options_fstype));
1014		newfs_command(options_fstype, newfs, 1);
1015	}
1016
1017	size = maxsize;
1018	if (strlen(items[1].text) > 0) {
1019		if (expand_number(items[1].text, &bytes) != 0) {
1020			char error[512];
1021
1022			sprintf(error, "Invalid size: %s\n", strerror(errno));
1023			dialog_msgbox("Error", error, 0, 0, TRUE);
1024			goto addpartform;
1025		}
1026		size = MIN((intmax_t)(bytes/sector), maxsize);
1027	}
1028
1029	/* Check if the label has a / in it */
1030	if (strchr(items[3].text, '/') != NULL) {
1031		dialog_msgbox("Error", "Label contains a /, which is not an "
1032		    "allowed character.", 0, 0, TRUE);
1033		goto addpartform;
1034	}
1035
1036	/* Warn if no mountpoint set */
1037	if (strcmp(items[0].text, "freebsd-ufs") == 0 &&
1038	    items[2].text[0] != '/') {
1039		choice = 0;
1040		if (interactive) {
1041			dialog_vars.defaultno = TRUE;
1042			choice = dialog_yesno("Warning",
1043			    "This partition does not have a valid mountpoint "
1044			    "(for the partition from which you intend to boot the "
1045			    "operating system, the mountpoint should be /). Are you "
1046			    "sure you want to continue?"
1047			, 0, 0);
1048			dialog_vars.defaultno = FALSE;
1049		}
1050		if (choice == 1) /* cancel */
1051			goto addpartform;
1052	}
1053
1054	/*
1055	 * Error if this scheme needs nested partitions, this is one, and
1056	 * a mountpoint was set.
1057	 */
1058	if (strcmp(items[0].text, "freebsd") == 0 &&
1059	    strlen(items[2].text) > 0) {
1060		dialog_msgbox("Error", "Partitions of type \"freebsd\" are "
1061		    "nested BSD-type partition schemes and cannot have "
1062		    "mountpoints. After creating one, select it and press "
1063		    "Create again to add the actual file systems.", 0, 0, TRUE);
1064		goto addpartform;
1065	}
1066
1067	/* If this is the root partition, check that this scheme is bootable */
1068	if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) {
1069		char message[512];
1070		sprintf(message, "This partition scheme (%s) is not bootable "
1071		    "on this platform. Are you sure you want to proceed?",
1072		    scheme);
1073		dialog_vars.defaultno = TRUE;
1074		choice = dialog_yesno("Warning", message, 0, 0);
1075		dialog_vars.defaultno = FALSE;
1076		if (choice == 1) /* cancel */
1077			goto addpartform;
1078	}
1079
1080	/* If this is the root partition, check that this fs is bootable */
1081	if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme,
1082	    items[0].text)) {
1083		char message[512];
1084		sprintf(message, "This file system (%s) is not bootable "
1085		    "on this system. Are you sure you want to proceed?",
1086		    items[0].text);
1087		dialog_vars.defaultno = TRUE;
1088		choice = dialog_yesno("Warning", message, 0, 0);
1089		dialog_vars.defaultno = FALSE;
1090		if (choice == 1) /* cancel */
1091			goto addpartform;
1092	}
1093
1094	/*
1095	 * If this is the root partition, and we need a boot partition, ask
1096	 * the user to add one.
1097	 */
1098
1099	/* Check for existing freebsd-boot partition */
1100	LIST_FOREACH(pp, &geom->lg_provider, lg_provider) {
1101		struct partition_metadata *md;
1102		md = get_part_metadata(pp->lg_name, 0);
1103		if (md == NULL || !md->bootcode)
1104			continue;
1105		LIST_FOREACH(gc, &pp->lg_config, lg_config)
1106			if (strcmp(gc->lg_name, "type") == 0)
1107				break;
1108		if (gc != NULL && strcmp(gc->lg_val,
1109		    bootpart_type(scheme)) == 0)
1110			break;
1111	}
1112
1113	/* If there isn't one, and we need one, ask */
1114	if ((strcmp(items[0].text, "freebsd") == 0 ||
1115	    strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0 &&
1116	    pp == NULL) {
1117		if (interactive)
1118			choice = dialog_yesno("Boot Partition",
1119			    "This partition scheme requires a boot partition "
1120			    "for the disk to be bootable. Would you like to "
1121			    "make one now?", 0, 0);
1122		else
1123			choice = 0;
1124
1125		if (choice == 0) { /* yes */
1126			r = gctl_get_handle();
1127			gctl_ro_param(r, "class", -1, "PART");
1128			gctl_ro_param(r, "arg0", -1, geom->lg_name);
1129			gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1130			gctl_ro_param(r, "verb", -1, "add");
1131			gctl_ro_param(r, "type", -1, bootpart_type(scheme));
1132			snprintf(sizestr, sizeof(sizestr), "%jd",
1133			    bootpart_size(scheme) / sector);
1134			gctl_ro_param(r, "size", -1, sizestr);
1135			snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1136			gctl_ro_param(r, "start", -1, startstr);
1137			gctl_rw_param(r, "output", sizeof(output), output);
1138			errstr = gctl_issue(r);
1139			if (errstr != NULL && errstr[0] != '\0')
1140				gpart_show_error("Error", NULL, errstr);
1141			gctl_free(r);
1142
1143			get_part_metadata(strtok(output, " "), 1)->bootcode = 1;
1144
1145			/* Now adjust the part we are really adding forward */
1146			firstfree += bootpart_size(scheme) / sector;
1147			size -= (bootpart_size(scheme) + stripe)/sector;
1148			if (stripe > 0 && (firstfree*sector % stripe) != 0)
1149				firstfree += (stripe - ((firstfree*sector) %
1150				    stripe)) / sector;
1151		}
1152	}
1153
1154	r = gctl_get_handle();
1155	gctl_ro_param(r, "class", -1, "PART");
1156	gctl_ro_param(r, "arg0", -1, geom->lg_name);
1157	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1158	gctl_ro_param(r, "verb", -1, "add");
1159
1160	gctl_ro_param(r, "type", -1, items[0].text);
1161	snprintf(sizestr, sizeof(sizestr), "%jd", size);
1162	gctl_ro_param(r, "size", -1, sizestr);
1163	snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1164	gctl_ro_param(r, "start", -1, startstr);
1165	if (items[3].text[0] != '\0')
1166		gctl_ro_param(r, "label", -1, items[3].text);
1167	gctl_rw_param(r, "output", sizeof(output), output);
1168	errstr = gctl_issue(r);
1169	if (errstr != NULL && errstr[0] != '\0') {
1170		gpart_show_error("Error", NULL, errstr);
1171		gctl_free(r);
1172		goto addpartform;
1173	}
1174	newpartname = strtok(output, " ");
1175	gctl_free(r);
1176
1177	/*
1178	 * Try to destroy any geom that gpart picked up already here from
1179	 * dirty blocks.
1180	 */
1181	r = gctl_get_handle();
1182	gctl_ro_param(r, "class", -1, "PART");
1183	gctl_ro_param(r, "arg0", -1, newpartname);
1184	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1185	junk = 1;
1186	gctl_ro_param(r, "force", sizeof(junk), &junk);
1187	gctl_ro_param(r, "verb", -1, "destroy");
1188	gctl_issue(r); /* Error usually expected and non-fatal */
1189	gctl_free(r);
1190
1191	if (strcmp(items[0].text, bootpart_type(scheme)) == 0)
1192		get_part_metadata(newpartname, 1)->bootcode = 1;
1193	else if (strcmp(items[0].text, "freebsd") == 0)
1194		gpart_partition(newpartname, "BSD");
1195	else
1196		set_default_part_metadata(newpartname, scheme,
1197		    items[0].text, items[2].text, newfs);
1198
1199	for (i = 0; i < nitems(items); i++)
1200		if (items[i].text_free)
1201			free(items[i].text);
1202
1203	if (partname != NULL)
1204		*partname = strdup(newpartname);
1205}
1206
1207void
1208gpart_delete(struct gprovider *pp)
1209{
1210	struct gconfig *gc;
1211	struct ggeom *geom;
1212	struct gconsumer *cp;
1213	struct gctl_req *r;
1214	const char *errstr;
1215	intmax_t idx;
1216	int is_partition;
1217
1218	/* Is it a partition? */
1219	is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
1220
1221	/* Find out if this is the root of a gpart geom */
1222	geom = NULL;
1223	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1224		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1225			geom = cp->lg_geom;
1226			break;
1227		}
1228
1229	/* If so, destroy all children */
1230	if (geom != NULL) {
1231		gpart_destroy(geom);
1232
1233		/* If this is a partition, revert it, so it can be deleted */
1234		if (is_partition) {
1235			r = gctl_get_handle();
1236			gctl_ro_param(r, "class", -1, "PART");
1237			gctl_ro_param(r, "arg0", -1, geom->lg_name);
1238			gctl_ro_param(r, "verb", -1, "undo");
1239			gctl_issue(r); /* Ignore non-fatal errors */
1240			gctl_free(r);
1241		}
1242	}
1243
1244	/*
1245	 * If this is not a partition, see if that is a problem, complain if
1246	 * necessary, and return always, since we need not do anything further,
1247	 * error or no.
1248	 */
1249	if (!is_partition) {
1250		if (geom == NULL)
1251			dialog_msgbox("Error",
1252			    "Only partitions can be deleted.", 0, 0, TRUE);
1253		return;
1254	}
1255
1256	r = gctl_get_handle();
1257	gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
1258	gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
1259	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1260	gctl_ro_param(r, "verb", -1, "delete");
1261
1262	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
1263		if (strcmp(gc->lg_name, "index") == 0) {
1264			idx = atoi(gc->lg_val);
1265			gctl_ro_param(r, "index", sizeof(idx), &idx);
1266			break;
1267		}
1268	}
1269
1270	errstr = gctl_issue(r);
1271	if (errstr != NULL && errstr[0] != '\0') {
1272		gpart_show_error("Error", NULL, errstr);
1273		gctl_free(r);
1274		return;
1275	}
1276
1277	gctl_free(r);
1278
1279	delete_part_metadata(pp->lg_name);
1280}
1281
1282void
1283gpart_revert_all(struct gmesh *mesh)
1284{
1285	struct gclass *classp;
1286	struct gconfig *gc;
1287	struct ggeom *gp;
1288	struct gctl_req *r;
1289	const char *modified;
1290
1291	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1292		if (strcmp(classp->lg_name, "PART") == 0)
1293			break;
1294	}
1295
1296	if (strcmp(classp->lg_name, "PART") != 0) {
1297		dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1298		return;
1299	}
1300
1301	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1302		modified = "true"; /* XXX: If we don't know (kernel too old),
1303				    * assume there are modifications. */
1304		LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1305			if (strcmp(gc->lg_name, "modified") == 0) {
1306				modified = gc->lg_val;
1307				break;
1308			}
1309		}
1310
1311		if (strcmp(modified, "false") == 0)
1312			continue;
1313
1314		r = gctl_get_handle();
1315		gctl_ro_param(r, "class", -1, "PART");
1316		gctl_ro_param(r, "arg0", -1, gp->lg_name);
1317		gctl_ro_param(r, "verb", -1, "undo");
1318
1319		gctl_issue(r);
1320		gctl_free(r);
1321	}
1322}
1323
1324void
1325gpart_commit(struct gmesh *mesh)
1326{
1327	struct partition_metadata *md;
1328	struct gclass *classp;
1329	struct ggeom *gp;
1330	struct gconfig *gc;
1331	struct gconsumer *cp;
1332	struct gprovider *pp;
1333	struct gctl_req *r;
1334	const char *errstr;
1335	const char *modified;
1336	const char *rootfs;
1337
1338	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1339		if (strcmp(classp->lg_name, "PART") == 0)
1340			break;
1341	}
1342
1343	/* Figure out what filesystem / uses */
1344	rootfs = "ufs"; /* Assume ufs if nothing else present */
1345	TAILQ_FOREACH(md, &part_metadata, metadata) {
1346		if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) {
1347			rootfs = md->fstab->fs_vfstype;
1348			break;
1349		}
1350	}
1351
1352	if (strcmp(classp->lg_name, "PART") != 0) {
1353		dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1354		return;
1355	}
1356
1357	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1358		modified = "true"; /* XXX: If we don't know (kernel too old),
1359				    * assume there are modifications. */
1360		LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1361			if (strcmp(gc->lg_name, "modified") == 0) {
1362				modified = gc->lg_val;
1363				break;
1364			}
1365		}
1366
1367		if (strcmp(modified, "false") == 0)
1368			continue;
1369
1370		/* Add bootcode if necessary, before the commit */
1371		md = get_part_metadata(gp->lg_name, 0);
1372		if (md != NULL && md->bootcode)
1373			gpart_bootcode(gp);
1374
1375		/* Now install partcode on its partitions, if necessary */
1376		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1377			md = get_part_metadata(pp->lg_name, 0);
1378			if (md == NULL || !md->bootcode)
1379				continue;
1380
1381			/* Mark this partition active if that's required */
1382			gpart_activate(pp);
1383
1384			/* Check if the partition has sub-partitions */
1385			LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1386				if (strcmp(cp->lg_geom->lg_class->lg_name,
1387				    "PART") == 0)
1388					break;
1389
1390			if (cp == NULL) /* No sub-partitions */
1391				gpart_partcode(pp, rootfs);
1392		}
1393
1394		r = gctl_get_handle();
1395		gctl_ro_param(r, "class", -1, "PART");
1396		gctl_ro_param(r, "arg0", -1, gp->lg_name);
1397		gctl_ro_param(r, "verb", -1, "commit");
1398
1399		errstr = gctl_issue(r);
1400		if (errstr != NULL && errstr[0] != '\0')
1401			gpart_show_error("Error", NULL, errstr);
1402		gctl_free(r);
1403	}
1404}
1405
1406