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