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