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.form.value_without_ok = true;
657 	conf.title = "Edit Partition";
658 	choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items);
659 
660 	if (choice == BSDDIALOG_CANCEL)
661 		goto endedit;
662 
663 	/* If this is the root partition, check that this fs is bootable */
664 	if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme,
665 	    items[0].value)) {
666 		char message[512];
667 
668 		snprintf(message, sizeof(message),
669 		    "This file system (%s) is not bootable "
670 		    "on this system. Are you sure you want to proceed?",
671 		    items[0].value);
672 		conf.button.default_cancel = true;
673 		conf.title = "Warning";
674 		choice = bsddialog_yesno(&conf, message, 0, 0);
675 		conf.button.default_cancel = false;
676 		if (choice == BSDDIALOG_CANCEL)
677 			goto editpart;
678 	}
679 
680 	/* Check if the label has a / in it */
681 	if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) {
682 		conf.title = "Error";
683 		bsddialog_msgbox(&conf, "Label contains a /, which is not an "
684 		    "allowed character.", 0, 0);
685 		goto editpart;
686 	}
687 
688 	r = gctl_get_handle();
689 	gctl_ro_param(r, "class", -1, "PART");
690 	gctl_ro_param(r, "arg0", -1, geom->lg_name);
691 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
692 	gctl_ro_param(r, "verb", -1, "modify");
693 	gctl_ro_param(r, "index", sizeof(idx), &idx);
694 	if (items[3].value != NULL && (hadlabel || items[3].value[0] != '\0'))
695 		gctl_ro_param(r, "label", -1, items[3].value);
696 	gctl_ro_param(r, "type", -1, items[0].value);
697 	errstr = gctl_issue(r);
698 	if (errstr != NULL && errstr[0] != '\0') {
699 		gpart_show_error("Error", NULL, errstr);
700 		gctl_free(r);
701 		goto editpart;
702 	}
703 	gctl_free(r);
704 
705 	newfs = newfs_command(items[0].value, 1);
706 	set_default_part_metadata(pp->lg_name, scheme, items[0].value,
707 	    items[2].value, (strcmp(oldtype, items[0].value) != 0) ?
708 	    newfs : NULL);
709 	free(newfs);
710 
711 endedit:
712 	if (strcmp(oldtype, items[0].value) != 0 && cp != NULL)
713 		gpart_destroy(cp->lg_geom);
714 	if (strcmp(oldtype, items[0].value) != 0 && strcmp(items[0].value,
715 	    "freebsd") == 0)
716 		gpart_partition(pp->lg_name, "BSD");
717 
718 	for (i = 0; i < nitems(items); i++)
719 		if (items[i].value != NULL)
720 			free(items[i].value);
721 }
722 
723 void
set_default_part_metadata(const char * name,const char * scheme,const char * type,const char * mountpoint,const char * newfs)724 set_default_part_metadata(const char *name, const char *scheme,
725     const char *type, const char *mountpoint, const char *newfs)
726 {
727 	struct partition_metadata *md;
728 	char *zpool_name = NULL;
729 	const char *default_bootmount = NULL;
730 	int i;
731 
732 	/* Set part metadata */
733 	md = get_part_metadata(name, 1);
734 
735 	if (newfs) {
736 		if (md->newfs != NULL) {
737 			free(md->newfs);
738 			md->newfs = NULL;
739 		}
740 
741 		if (newfs != NULL && newfs[0] != '\0') {
742 			if (strcmp("freebsd-zfs", type) == 0) {
743 				zpool_name = strdup((strlen(mountpoint) == 1) ?
744 				    "root" : &mountpoint[1]);
745 				for (i = 0; zpool_name[i] != 0; i++)
746 					if (!isalnum(zpool_name[i]))
747 						zpool_name[i] = '_';
748 				asprintf(&md->newfs, "%s %s /dev/%s", newfs,
749 				    zpool_name, name);
750 			} else {
751 				asprintf(&md->newfs, "%s /dev/%s", newfs, name);
752 			}
753 		}
754 	}
755 
756 	if (strcmp(type, "freebsd-swap") == 0)
757 		mountpoint = "none";
758 	if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) {
759 		if (default_bootmount == NULL)
760 			md->bootcode = 1;
761 		else if (mountpoint == NULL || strlen(mountpoint) == 0)
762 			mountpoint = default_bootmount;
763 	}
764 
765 	if (mountpoint == NULL || mountpoint[0] == '\0') {
766 		if (md->fstab != NULL) {
767 			free(md->fstab->fs_spec);
768 			free(md->fstab->fs_file);
769 			free(md->fstab->fs_vfstype);
770 			free(md->fstab->fs_mntops);
771 			free(md->fstab->fs_type);
772 			free(md->fstab);
773 			md->fstab = NULL;
774 		}
775 	} else {
776 		if (md->fstab == NULL) {
777 			md->fstab = malloc(sizeof(struct fstab));
778 		} else {
779 			free(md->fstab->fs_spec);
780 			free(md->fstab->fs_file);
781 			free(md->fstab->fs_vfstype);
782 			free(md->fstab->fs_mntops);
783 			free(md->fstab->fs_type);
784 		}
785 		if (strcmp("freebsd-zfs", type) == 0) {
786 			md->fstab->fs_spec = strdup(zpool_name);
787 		} else {
788 			asprintf(&md->fstab->fs_spec, "/dev/%s", name);
789 		}
790 		md->fstab->fs_file = strdup(mountpoint);
791 		/* Get VFS from text after freebsd-, if possible */
792 		if (strncmp("freebsd-", type, 8) == 0)
793 			md->fstab->fs_vfstype = strdup(&type[8]);
794 		else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0
795 	     	    || strcmp("ms-basic-data", type) == 0)
796 			md->fstab->fs_vfstype = strdup("msdosfs");
797 		else
798 			md->fstab->fs_vfstype = strdup(type); /* Guess */
799 		if (strcmp(type, "freebsd-swap") == 0) {
800 			md->fstab->fs_type = strdup(FSTAB_SW);
801 			md->fstab->fs_freq = 0;
802 			md->fstab->fs_passno = 0;
803 		} else if (strcmp(type, "freebsd-zfs") == 0) {
804 			md->fstab->fs_type = strdup(FSTAB_RW);
805 			md->fstab->fs_freq = 0;
806 			md->fstab->fs_passno = 0;
807 		} else {
808 			md->fstab->fs_type = strdup(FSTAB_RW);
809 			if (strcmp(mountpoint, "/") == 0) {
810 				md->fstab->fs_freq = 1;
811 				md->fstab->fs_passno = 1;
812 			} else {
813 				md->fstab->fs_freq = 2;
814 				md->fstab->fs_passno = 2;
815 			}
816 		}
817 		md->fstab->fs_mntops = strdup(md->fstab->fs_type);
818 	}
819 
820 	if (zpool_name != NULL)
821 		free(zpool_name);
822 }
823 
824 static
part_compare(const void * xa,const void * xb)825 int part_compare(const void *xa, const void *xb)
826 {
827 	struct gprovider **a = (struct gprovider **)xa;
828 	struct gprovider **b = (struct gprovider **)xb;
829 	intmax_t astart, bstart;
830 	struct gconfig *gc;
831 
832 	astart = bstart = 0;
833 	LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
834 		if (strcmp(gc->lg_name, "start") == 0) {
835 			astart = strtoimax(gc->lg_val, NULL, 0);
836 			break;
837 		}
838 	LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
839 		if (strcmp(gc->lg_name, "start") == 0) {
840 			bstart = strtoimax(gc->lg_val, NULL, 0);
841 			break;
842 		}
843 
844 	if (astart < bstart)
845 		return -1;
846 	else if (astart > bstart)
847 		return 1;
848 	else
849 		return 0;
850 }
851 
852 intmax_t
gpart_max_free(struct ggeom * geom,intmax_t * npartstart)853 gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
854 {
855 	struct gconfig *gc;
856 	struct gprovider *pp, **providers;
857 	intmax_t sectorsize, stripesize, offset;
858 	intmax_t lastend;
859 	intmax_t start, end;
860 	intmax_t maxsize, maxstart;
861 	intmax_t partstart, partend;
862 	int i, nparts;
863 
864 	/* Now get the maximum free size and free start */
865 	start = end = 0;
866 	LIST_FOREACH(gc, &geom->lg_config, lg_config) {
867 		if (strcmp(gc->lg_name, "first") == 0)
868 			start = strtoimax(gc->lg_val, NULL, 0);
869 		if (strcmp(gc->lg_name, "last") == 0)
870 			end = strtoimax(gc->lg_val, NULL, 0);
871 	}
872 
873 	i = nparts = 0;
874 	LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
875 		nparts++;
876 	providers = calloc(nparts, sizeof(providers[0]));
877 	LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
878 		providers[i++] = pp;
879 	qsort(providers, nparts, sizeof(providers[0]), part_compare);
880 
881 	lastend = start - 1;
882 	maxsize = 0;
883 	for (i = 0; i < nparts; i++) {
884 		pp = providers[i];
885 
886 		LIST_FOREACH(gc, &pp->lg_config, lg_config) {
887 			if (strcmp(gc->lg_name, "start") == 0)
888 				partstart = strtoimax(gc->lg_val, NULL, 0);
889 			if (strcmp(gc->lg_name, "end") == 0)
890 				partend = strtoimax(gc->lg_val, NULL, 0);
891 		}
892 
893 		if (partstart - lastend > maxsize) {
894 			maxsize = partstart - lastend - 1;
895 			maxstart = lastend + 1;
896 		}
897 
898 		lastend = partend;
899 	}
900 
901 	if (end - lastend > maxsize) {
902 		maxsize = end - lastend;
903 		maxstart = lastend + 1;
904 	}
905 
906 	pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
907 
908 	/*
909 	 * Round the start and size of the largest available space up to
910 	 * the nearest multiple of the adjusted stripe size.
911 	 *
912 	 * The adjusted stripe size is the least common multiple of the
913 	 * actual stripe size, or the sector size if no stripe size was
914 	 * reported, and 4096.  The reason for this is that contemporary
915 	 * disks often have 4096-byte physical sectors but report 512
916 	 * bytes instead for compatibility with older / broken operating
917 	 * systems and BIOSes.  For the same reasons, virtualized storage
918 	 * may also report a 512-byte stripe size, or none at all.
919 	 */
920 	sectorsize = pp->lg_sectorsize;
921 	if ((stripesize = pp->lg_stripesize) == 0)
922 		stripesize = sectorsize;
923 	while (stripesize % 4096 != 0)
924 		stripesize *= 2;
925 	if ((offset = maxstart * sectorsize % stripesize) != 0) {
926 		offset = (stripesize - offset) / sectorsize;
927 		maxstart += offset;
928 		maxsize -= offset;
929 	}
930 
931 	if (npartstart != NULL)
932 		*npartstart = maxstart;
933 
934 	return (maxsize);
935 }
936 
937 static size_t
add_boot_partition(struct ggeom * geom,struct gprovider * pp,const char * scheme,int interactive)938 add_boot_partition(struct ggeom *geom, struct gprovider *pp,
939     const char *scheme, int interactive)
940 {
941 	struct gconfig *gc;
942 	struct gprovider *ppi;
943 	int choice;
944 	struct bsddialog_conf conf;
945 
946 	/* Check for existing freebsd-boot partition */
947 	LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) {
948 		struct partition_metadata *md;
949 		const char *bootmount = NULL;
950 
951 		LIST_FOREACH(gc, &ppi->lg_config, lg_config)
952 			if (strcmp(gc->lg_name, "type") == 0)
953 				break;
954 		if (gc == NULL)
955 			continue;
956 		if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0)
957 			continue;
958 
959 		/*
960 		 * If the boot partition is not mountable and needs partcode,
961 		 * but doesn't have it, it doesn't satisfy our requirements.
962 		 */
963 		md = get_part_metadata(ppi->lg_name, 0);
964 		if (bootmount == NULL && (md == NULL || !md->bootcode))
965 			continue;
966 
967 		/* If it is mountable, but mounted somewhere else, remount */
968 		if (bootmount != NULL && md != NULL && md->fstab != NULL
969 		    && strlen(md->fstab->fs_file) > 0
970 		    && strcmp(md->fstab->fs_file, bootmount) != 0)
971 			continue;
972 
973 		/* If it is mountable, but mountpoint is not set, mount it */
974 		if (bootmount != NULL && md == NULL)
975 			set_default_part_metadata(ppi->lg_name, scheme,
976 			    gc->lg_val, bootmount, NULL);
977 
978 		/* Looks good at this point, no added data needed */
979 		return (0);
980 	}
981 
982 	if (interactive) {
983 		bsddialog_initconf(&conf);
984 		conf.title = "Boot Partition";
985 		choice = bsddialog_yesno(&conf,
986 		    "This partition scheme requires a boot partition "
987 		    "for the disk to be bootable. Would you like to "
988 		    "make one now?", 0, 0);
989 	} else {
990 		choice = BSDDIALOG_YES;
991 	}
992 
993 	if (choice == BSDDIALOG_YES) {
994 		struct partition_metadata *md;
995 		const char *bootmount = NULL;
996 		char *bootpartname = NULL;
997 		char sizestr[7];
998 
999 		humanize_number(sizestr, 7,
1000 		    bootpart_size(scheme), "B", HN_AUTOSCALE,
1001 		    HN_NOSPACE | HN_DECIMAL);
1002 
1003 		gpart_create(pp, bootpart_type(scheme, &bootmount),
1004 		    sizestr, bootmount, &bootpartname, 0);
1005 
1006 		if (bootpartname == NULL) /* Error reported to user already */
1007 			return 0;
1008 
1009 		/* If the part is not mountable, make sure newfs isn't set */
1010 		if (bootmount == NULL) {
1011 			md = get_part_metadata(bootpartname, 0);
1012 			if (md != NULL && md->newfs != NULL) {
1013 				free(md->newfs);
1014 				md->newfs = NULL;
1015 			}
1016 		}
1017 
1018 		free(bootpartname);
1019 
1020 		return (bootpart_size(scheme));
1021 	}
1022 
1023 	return (0);
1024 }
1025 
1026 void
gpart_create(struct gprovider * pp,const char * default_type,const char * default_size,const char * default_mountpoint,char ** partname,int interactive)1027 gpart_create(struct gprovider *pp, const char *default_type,
1028     const char *default_size, const char *default_mountpoint,
1029     char **partname, int interactive)
1030 {
1031 	struct gctl_req *r;
1032 	struct gconfig *gc;
1033 	struct gconsumer *cp;
1034 	struct ggeom *geom;
1035 	const char *errstr, *scheme;
1036 	char sizestr[32], startstr[32], output[64], *newpartname;
1037 	char *newfs, options_fstype[64];
1038 	intmax_t maxsize, size, sector, firstfree, stripe;
1039 	uint64_t bytes;
1040 	int nitems, choice, junk;
1041 	unsigned i;
1042 	bool init_allocated;
1043 	struct bsddialog_conf conf;
1044 
1045 	struct bsddialog_formitem items[] = {
1046 		{"Type:", 1, 1, "freebsd-ufs", 1, 12, 12, 15, NULL, 0,
1047 		    "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
1048 		    "freebsd-swap)"},
1049 		{"Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0,
1050 		    "Partition size. Append K, M, G for kilobytes, "
1051 		    "megabytes or gigabytes."},
1052 		{"Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0,
1053 		    "Path at which to mount partition (blank for "
1054 		    "swap, / for root filesystem)"},
1055 		{"Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0,
1056 		    "Partition name. Not all partition schemes support this."},
1057 	};
1058 
1059 	bsddialog_initconf(&conf);
1060 
1061 	if (partname != NULL)
1062 		*partname = NULL;
1063 
1064 	/* Record sector and stripe sizes */
1065 	sector = pp->lg_sectorsize;
1066 	stripe = pp->lg_stripesize;
1067 
1068 	/*
1069 	 * Find the PART geom we are manipulating. This may be a consumer of
1070 	 * this provider, or its parent. Check the consumer case first.
1071 	 */
1072 	geom = NULL;
1073 	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1074 		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1075 			geom = cp->lg_geom;
1076 			break;
1077 		}
1078 
1079 	if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
1080 		geom = pp->lg_geom;
1081 
1082 	/* Now get the partition scheme */
1083 	scheme = NULL;
1084 	if (geom != NULL) {
1085 		LIST_FOREACH(gc, &geom->lg_config, lg_config)
1086 			if (strcmp(gc->lg_name, "scheme") == 0)
1087 				scheme = gc->lg_val;
1088 	}
1089 
1090 	if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
1091 		if (gpart_partition(pp->lg_name, NULL) == 0) {
1092 			bsddialog_msgbox(&conf,
1093 			    "The partition table has been successfully created."
1094 			    " Please press Create again to create partitions.",
1095 			    0, 0);
1096 		}
1097 
1098 		return;
1099 	}
1100 
1101 	/*
1102 	 * If we still don't have a geom, either the user has
1103 	 * canceled partitioning or there has been an error which has already
1104 	 * been displayed, so bail.
1105 	 */
1106 	if (geom == NULL)
1107 		return;
1108 
1109 	maxsize = size = gpart_max_free(geom, &firstfree);
1110 	if (size <= 0) {
1111 		conf .title = "Error";
1112 		bsddialog_msgbox(&conf, "No free space left on device.", 0, 0);
1113 		return;
1114 	}
1115 
1116 	humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
1117 	    HN_NOSPACE | HN_DECIMAL);
1118 	items[1].init = sizestr;
1119 
1120 	/* Special-case the MBR default type for nested partitions */
1121 	if (strcmp(scheme, "MBR") == 0) {
1122 		items[0].init = "freebsd";
1123 		items[0].bottomdesc = "Filesystem type (e.g. freebsd, fat32)";
1124 	}
1125 
1126 	nitems = scheme_supports_labels(scheme) ? 4 : 3;
1127 
1128 	if (default_type != NULL)
1129 		items[0].init = (char *)default_type;
1130 	if (default_size != NULL)
1131 		items[1].init = (char *)default_size;
1132 	if (default_mountpoint != NULL)
1133 		items[2].init = (char *)default_mountpoint;
1134 
1135 	/* Default options */
1136 	strncpy(options_fstype, items[0].init,
1137 	    sizeof(options_fstype));
1138 	newfs = newfs_command(options_fstype, 1);
1139 
1140 	init_allocated = false;
1141 addpartform:
1142 	if (interactive) {
1143 		conf.button.with_extra = true;
1144 		conf.button.extra_label = "Options";
1145 		conf.form.value_without_ok = true;
1146 		conf.title = "Add Partition";
1147 		choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items);
1148 		conf.button.with_extra = false;
1149 		conf.button.extra_label = NULL;
1150 		conf.form.value_without_ok = false;
1151 		switch (choice) {
1152 		case BSDDIALOG_OK:
1153 			break;
1154 		case BSDDIALOG_CANCEL:
1155 			return;
1156 		case BSDDIALOG_EXTRA: /* Options */
1157 			free(newfs);
1158 			strncpy(options_fstype, items[0].value,
1159 			    sizeof(options_fstype));
1160 			newfs = newfs_command(options_fstype, 0);
1161 			for (i = 0; i < nitems(items); i++) {
1162 				if (init_allocated)
1163 					free((char*)items[i].init);
1164 				items[i].init = items[i].value;
1165 			}
1166 			init_allocated = true;
1167 			goto addpartform;
1168 		}
1169 	} else { /* auto partitioning */
1170 		items[0].value = strdup(items[0].init);
1171 		items[1].value = strdup(items[1].init);
1172 		items[2].value = strdup(items[2].init);
1173 		if (nitems > 3)
1174 			items[3].value = strdup(items[3].init);
1175 	}
1176 
1177 	/*
1178 	 * If the user changed the fs type after specifying options, undo
1179 	 * their choices in favor of the new filesystem's defaults.
1180 	 */
1181 	if (strcmp(options_fstype, items[0].value) != 0) {
1182 		free(newfs);
1183 		strncpy(options_fstype, items[0].value, sizeof(options_fstype));
1184 		newfs = newfs_command(options_fstype, 1);
1185 	}
1186 
1187 	size = maxsize;
1188 	if (strlen(items[1].value) > 0) {
1189 		if (expand_number(items[1].value, &bytes) != 0) {
1190 			char error[512];
1191 
1192 			snprintf(error, sizeof(error), "Invalid size: %s\n",
1193 			    strerror(errno));
1194 			conf.title = "Error";
1195 			bsddialog_msgbox(&conf, error, 0, 0);
1196 			goto addpartform;
1197 		}
1198 		size = MIN((intmax_t)(bytes/sector), maxsize);
1199 	}
1200 
1201 	/* Check if the label has a / in it */
1202 	if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) {
1203 		conf.title = "Error";
1204 		bsddialog_msgbox(&conf, "Label contains a /, which is not an "
1205 		    "allowed character.", 0, 0);
1206 		goto addpartform;
1207 	}
1208 
1209 	/* Warn if no mountpoint set */
1210 	if (strcmp(items[0].value, "freebsd-ufs") == 0 &&
1211 	    items[2].value[0] != '/') {
1212 		choice = 0;
1213 		if (interactive) {
1214 			conf.button.default_cancel = true;
1215 			conf.title = "Warning";
1216 			choice = bsddialog_yesno(&conf,
1217 			    "This partition does not have a valid mountpoint "
1218 			    "(for the partition from which you intend to boot the "
1219 			    "operating system, the mountpoint should be /). Are you "
1220 			    "sure you want to continue?"
1221 			, 0, 0);
1222 			conf.button.default_cancel = false;
1223 		}
1224 		if (choice == BSDDIALOG_CANCEL)
1225 			goto addpartform;
1226 	}
1227 
1228 	/*
1229 	 * Error if this scheme needs nested partitions, this is one, and
1230 	 * a mountpoint was set.
1231 	 */
1232 	if (strcmp(items[0].value, "freebsd") == 0 &&
1233 	    strlen(items[2].value) > 0) {
1234 		conf.title = "Error";
1235 		bsddialog_msgbox(&conf, "Partitions of type \"freebsd\" are "
1236 		    "nested BSD-type partition schemes and cannot have "
1237 		    "mountpoints. After creating one, select it and press "
1238 		    "Create again to add the actual file systems.", 0, 0);
1239 		goto addpartform;
1240 	}
1241 
1242 	/* If this is the root partition, check that this scheme is bootable */
1243 	if (strcmp(items[2].value, "/") == 0 && !is_scheme_bootable(scheme)) {
1244 		char message[512];
1245 
1246 		snprintf(message, sizeof(message),
1247 		    "This partition scheme (%s) is not bootable "
1248 		    "on this platform. Are you sure you want to proceed?",
1249 		    scheme);
1250 		conf.button.default_cancel = true;
1251 		conf.title = "Warning";
1252 		choice = bsddialog_yesno(&conf, message, 0, 0);
1253 		conf.button.default_cancel = false;
1254 		if (choice == BSDDIALOG_CANCEL)
1255 			goto addpartform;
1256 	}
1257 
1258 	/* If this is the root partition, check that this fs is bootable */
1259 	if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme,
1260 	    items[0].value)) {
1261 		char message[512];
1262 
1263 		snprintf(message, sizeof(message),
1264 		    "This file system (%s) is not bootable "
1265 		    "on this system. Are you sure you want to proceed?",
1266 		    items[0].value);
1267 		conf.button.default_cancel = true;
1268 		conf.title = "Warning";
1269 		choice = bsddialog_yesno(&conf, message, 0, 0);
1270 		conf.button.default_cancel = false;
1271 		if (choice == BSDDIALOG_CANCEL)
1272 			goto addpartform;
1273 	}
1274 
1275 	/*
1276 	 * If this is the root partition, and we need a boot partition, ask
1277 	 * the user to add one.
1278 	 */
1279 
1280 	if ((strcmp(items[0].value, "freebsd") == 0 ||
1281 	    strcmp(items[2].value, "/") == 0) && bootpart_size(scheme) > 0) {
1282 		size_t bytes = add_boot_partition(geom, pp, scheme,
1283 		    interactive);
1284 
1285 		/* Now adjust the part we are really adding forward */
1286 		if (bytes > 0) {
1287 			firstfree += bytes / sector;
1288 			size -= (bytes + stripe)/sector;
1289 			if (stripe > 0 && (firstfree*sector % stripe) != 0)
1290 				firstfree += (stripe - ((firstfree*sector) %
1291 				    stripe)) / sector;
1292 		}
1293 	}
1294 
1295 	output[0] = '\0';
1296 
1297 	r = gctl_get_handle();
1298 	gctl_ro_param(r, "class", -1, "PART");
1299 	gctl_ro_param(r, "arg0", -1, geom->lg_name);
1300 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1301 	gctl_ro_param(r, "verb", -1, "add");
1302 
1303 	gctl_ro_param(r, "type", -1, items[0].value);
1304 	snprintf(sizestr, sizeof(sizestr), "%jd", size);
1305 	gctl_ro_param(r, "size", -1, sizestr);
1306 	snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1307 	gctl_ro_param(r, "start", -1, startstr);
1308 	if (items[3].value != NULL && items[3].value[0] != '\0')
1309 		gctl_ro_param(r, "label", -1, items[3].value);
1310 	gctl_add_param(r, "output", sizeof(output), output,
1311 	    GCTL_PARAM_WR | GCTL_PARAM_ASCII);
1312 	errstr = gctl_issue(r);
1313 	if (errstr != NULL && errstr[0] != '\0') {
1314 		gpart_show_error("Error", NULL, errstr);
1315 		gctl_free(r);
1316 		goto addpartform;
1317 	}
1318 	newpartname = strtok(output, " ");
1319 	gctl_free(r);
1320 
1321 	/*
1322 	 * Try to destroy any geom that gpart picked up already here from
1323 	 * dirty blocks.
1324 	 */
1325 	r = gctl_get_handle();
1326 	gctl_ro_param(r, "class", -1, "PART");
1327 	gctl_ro_param(r, "arg0", -1, newpartname);
1328 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1329 	junk = 1;
1330 	gctl_ro_param(r, "force", sizeof(junk), &junk);
1331 	gctl_ro_param(r, "verb", -1, "destroy");
1332 	gctl_issue(r); /* Error usually expected and non-fatal */
1333 	gctl_free(r);
1334 
1335 
1336 	if (strcmp(items[0].value, "freebsd") == 0)
1337 		gpart_partition(newpartname, "BSD");
1338 	else
1339 		set_default_part_metadata(newpartname, scheme,
1340 		    items[0].value, items[2].value, newfs);
1341 	free(newfs);
1342 
1343 	for (i = 0; i < nitems(items); i++) {
1344 		if (items[i].value != NULL) {
1345 			free(items[i].value);
1346 			if (init_allocated && items[i].init != NULL)
1347 				free((char*)items[i].init);
1348 		}
1349 	}
1350 
1351 	if (partname != NULL)
1352 		*partname = strdup(newpartname);
1353 }
1354 
1355 void
gpart_delete(struct gprovider * pp)1356 gpart_delete(struct gprovider *pp)
1357 {
1358 	struct gconfig *gc;
1359 	struct ggeom *geom;
1360 	struct gconsumer *cp;
1361 	struct gctl_req *r;
1362 	const char *errstr;
1363 	intmax_t idx;
1364 	int is_partition;
1365 	struct bsddialog_conf conf;
1366 
1367 	/* Is it a partition? */
1368 	is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
1369 
1370 	/* Find out if this is the root of a gpart geom */
1371 	geom = NULL;
1372 	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1373 		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1374 			geom = cp->lg_geom;
1375 			break;
1376 		}
1377 
1378 	/* If so, destroy all children */
1379 	if (geom != NULL) {
1380 		gpart_destroy(geom);
1381 
1382 		/* If this is a partition, revert it, so it can be deleted */
1383 		if (is_partition) {
1384 			r = gctl_get_handle();
1385 			gctl_ro_param(r, "class", -1, "PART");
1386 			gctl_ro_param(r, "arg0", -1, geom->lg_name);
1387 			gctl_ro_param(r, "verb", -1, "undo");
1388 			gctl_issue(r); /* Ignore non-fatal errors */
1389 			gctl_free(r);
1390 		}
1391 	}
1392 
1393 	/*
1394 	 * If this is not a partition, see if that is a problem, complain if
1395 	 * necessary, and return always, since we need not do anything further,
1396 	 * error or no.
1397 	 */
1398 	if (!is_partition) {
1399 		if (geom == NULL) {
1400 			bsddialog_initconf(&conf);
1401 			conf.title = "Error";
1402 			bsddialog_msgbox(&conf,
1403 			    "Only partitions can be deleted.", 0, 0);
1404 		}
1405 		return;
1406 	}
1407 
1408 	r = gctl_get_handle();
1409 	gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
1410 	gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
1411 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1412 	gctl_ro_param(r, "verb", -1, "delete");
1413 
1414 	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
1415 		if (strcmp(gc->lg_name, "index") == 0) {
1416 			idx = atoi(gc->lg_val);
1417 			gctl_ro_param(r, "index", sizeof(idx), &idx);
1418 			break;
1419 		}
1420 	}
1421 
1422 	errstr = gctl_issue(r);
1423 	if (errstr != NULL && errstr[0] != '\0') {
1424 		gpart_show_error("Error", NULL, errstr);
1425 		gctl_free(r);
1426 		return;
1427 	}
1428 
1429 	gctl_free(r);
1430 
1431 	delete_part_metadata(pp->lg_name);
1432 }
1433 
1434 void
gpart_revert_all(struct gmesh * mesh)1435 gpart_revert_all(struct gmesh *mesh)
1436 {
1437 	struct gclass *classp;
1438 	struct gconfig *gc;
1439 	struct ggeom *gp;
1440 	struct gctl_req *r;
1441 	const char *modified;
1442 	struct bsddialog_conf conf;
1443 
1444 	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1445 		if (strcmp(classp->lg_name, "PART") == 0)
1446 			break;
1447 	}
1448 
1449 	if (strcmp(classp->lg_name, "PART") != 0) {
1450 		bsddialog_initconf(&conf);
1451 		conf.title = "Error";
1452 		bsddialog_msgbox(&conf, "gpart not found!", 0, 0);
1453 		return;
1454 	}
1455 
1456 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1457 		modified = "true"; /* XXX: If we don't know (kernel too old),
1458 				    * assume there are modifications. */
1459 		LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1460 			if (strcmp(gc->lg_name, "modified") == 0) {
1461 				modified = gc->lg_val;
1462 				break;
1463 			}
1464 		}
1465 
1466 		if (strcmp(modified, "false") == 0)
1467 			continue;
1468 
1469 		r = gctl_get_handle();
1470 		gctl_ro_param(r, "class", -1, "PART");
1471 		gctl_ro_param(r, "arg0", -1, gp->lg_name);
1472 		gctl_ro_param(r, "verb", -1, "undo");
1473 
1474 		gctl_issue(r);
1475 		gctl_free(r);
1476 	}
1477 }
1478 
1479 void
gpart_commit(struct gmesh * mesh)1480 gpart_commit(struct gmesh *mesh)
1481 {
1482 	struct partition_metadata *md;
1483 	struct gclass *classp;
1484 	struct ggeom *gp;
1485 	struct gconfig *gc;
1486 	struct gconsumer *cp;
1487 	struct gprovider *pp;
1488 	struct gctl_req *r;
1489 	const char *errstr;
1490 	const char *modified;
1491 	const char *rootfs;
1492 	struct bsddialog_conf conf;
1493 
1494 	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1495 		if (strcmp(classp->lg_name, "PART") == 0)
1496 			break;
1497 	}
1498 
1499 	/* Figure out what filesystem / uses */
1500 	rootfs = "ufs"; /* Assume ufs if nothing else present */
1501 	TAILQ_FOREACH(md, &part_metadata, metadata) {
1502 		if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) {
1503 			rootfs = md->fstab->fs_vfstype;
1504 			break;
1505 		}
1506 	}
1507 
1508 	if (strcmp(classp->lg_name, "PART") != 0) {
1509 		bsddialog_initconf(&conf);
1510 		conf.title = "Error";
1511 		bsddialog_msgbox(&conf, "gpart not found!", 0, 0);
1512 		return;
1513 	}
1514 
1515 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1516 		modified = "true"; /* XXX: If we don't know (kernel too old),
1517 				    * assume there are modifications. */
1518 		LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1519 			if (strcmp(gc->lg_name, "modified") == 0) {
1520 				modified = gc->lg_val;
1521 				break;
1522 			}
1523 		}
1524 
1525 		if (strcmp(modified, "false") == 0)
1526 			continue;
1527 
1528 		/* Add bootcode if necessary, before the commit */
1529 		md = get_part_metadata(gp->lg_name, 0);
1530 		if (md != NULL && md->bootcode)
1531 			gpart_bootcode(gp);
1532 
1533 		/* Now install partcode on its partitions, if necessary */
1534 		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1535 			md = get_part_metadata(pp->lg_name, 0);
1536 			if (md == NULL || !md->bootcode)
1537 				continue;
1538 
1539 			/* Mark this partition active if that's required */
1540 			gpart_activate(pp);
1541 
1542 			/* Check if the partition has sub-partitions */
1543 			LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1544 				if (strcmp(cp->lg_geom->lg_class->lg_name,
1545 				    "PART") == 0)
1546 					break;
1547 
1548 			if (cp == NULL) /* No sub-partitions */
1549 				gpart_partcode(pp, rootfs);
1550 		}
1551 
1552 		r = gctl_get_handle();
1553 		gctl_ro_param(r, "class", -1, "PART");
1554 		gctl_ro_param(r, "arg0", -1, gp->lg_name);
1555 		gctl_ro_param(r, "verb", -1, "commit");
1556 
1557 		errstr = gctl_issue(r);
1558 		if (errstr != NULL && errstr[0] != '\0')
1559 			gpart_show_error("Error", NULL, errstr);
1560 		gctl_free(r);
1561 	}
1562 }
1563 
1564