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