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