xref: /freebsd/usr.sbin/bsdinstall/partedit/gpart_ops.c (revision 5dcd9c10612684d1c823670cbb5b4715028784e7)
1 /*-
2  * Copyright (c) 2011 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <errno.h>
31 #include <libutil.h>
32 #include <inttypes.h>
33 
34 #include <libgeom.h>
35 #include <dialog.h>
36 #include <dlg_keys.h>
37 
38 #include "partedit.h"
39 
40 #define GPART_FLAGS "x" /* Do not commit changes by default */
41 
42 static void
43 gpart_show_error(const char *title, const char *explanation, const char *errstr)
44 {
45 	char *errmsg;
46 	char message[512];
47 	int error;
48 
49 	if (explanation == NULL)
50 		explanation = "";
51 
52 	error = strtol(errstr, &errmsg, 0);
53 	if (errmsg != errstr) {
54 		while (errmsg[0] == ' ')
55 			errmsg++;
56 		if (errmsg[0] != '\0')
57 			sprintf(message, "%s%s. %s", explanation,
58 			    strerror(error), errmsg);
59 		else
60 			sprintf(message, "%s%s", explanation, strerror(error));
61 	} else {
62 		sprintf(message, "%s%s", explanation, errmsg);
63 	}
64 
65 	dialog_msgbox(title, message, 0, 0, TRUE);
66 }
67 
68 static int
69 scheme_supports_labels(const char *scheme)
70 {
71 	if (strcmp(scheme, "APM") == 0)
72 		return (1);
73 	if (strcmp(scheme, "GPT") == 0)
74 		return (1);
75 	if (strcmp(scheme, "PC98") == 0)
76 		return (1);
77 
78 	return (0);
79 }
80 
81 int
82 gpart_partition(const char *lg_name, const char *scheme)
83 {
84 	int cancel, choice;
85 	struct gctl_req *r;
86 	const char *errstr;
87 
88 	DIALOG_LISTITEM items[] = {
89 		{"APM", "Apple Partition Map",
90 		    "Bootable on PowerPC Apple Hardware", 0 },
91 		{"BSD", "BSD Labels",
92 		    "Bootable on most x86 systems", 0 },
93 		{"GPT", "GUID Partition Table",
94 		    "Bootable on most x86 systems", 0 },
95 		{"MBR", "DOS Partitions",
96 		    "Bootable on most x86 systems", 0 },
97 		{"PC98", "NEC PC9801 Partition Table",
98 		    "Bootable on NEC PC9801 systems", 0 },
99 		{"VTOC8", "Sun VTOC8 Partition Table",
100 		    "Bootable on Sun SPARC systems", 0 },
101 	};
102 
103 schememenu:
104 	if (scheme == NULL) {
105 		dialog_vars.default_item = __DECONST(char *, default_scheme());
106 		cancel = dlg_menu("Partition Scheme",
107 		    "Select a partition scheme for this volume:", 0, 0, 0,
108 		    sizeof(items) / sizeof(items[0]), items, &choice, NULL);
109 		dialog_vars.default_item = NULL;
110 
111 		if (cancel)
112 			return (-1);
113 
114 		if (!is_scheme_bootable(items[choice].name)) {
115 			char message[512];
116 			sprintf(message, "This partition scheme (%s) is not "
117 			    "bootable on this platform. Are you sure you want "
118 			    "to proceed?", items[choice].name);
119 			dialog_vars.defaultno = TRUE;
120 			cancel = dialog_yesno("Warning", message, 0, 0);
121 			dialog_vars.defaultno = FALSE;
122 			if (cancel) /* cancel */
123 				goto schememenu;
124 		}
125 
126 		scheme = items[choice].name;
127 	}
128 
129 	r = gctl_get_handle();
130 	gctl_ro_param(r, "class", -1, "PART");
131 	gctl_ro_param(r, "arg0", -1, lg_name);
132 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
133 	gctl_ro_param(r, "scheme", -1, scheme);
134 	gctl_ro_param(r, "verb", -1, "create");
135 
136 	errstr = gctl_issue(r);
137 	if (errstr != NULL && errstr[0] != '\0') {
138 		gpart_show_error("Error", NULL, errstr);
139 		gctl_free(r);
140 		scheme = NULL;
141 		goto schememenu;
142 	}
143 	gctl_free(r);
144 
145 	if (bootcode_path(scheme) != NULL)
146 		get_part_metadata(lg_name, 1)->bootcode = 1;
147 	return (0);
148 }
149 
150 static void
151 gpart_activate(struct gprovider *pp)
152 {
153 	struct gconfig *gc;
154 	struct gctl_req *r;
155 	const char *errstr, *scheme;
156 	const char *attribute = NULL;
157 	intmax_t idx;
158 
159 	/*
160 	 * Some partition schemes need this partition to be marked 'active'
161 	 * for it to be bootable.
162 	 */
163 	LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
164 		if (strcmp(gc->lg_name, "scheme") == 0) {
165 			scheme = gc->lg_val;
166 			break;
167 		}
168 	}
169 
170 	if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0 ||
171 	    strcmp(scheme, "PC98") == 0)
172 		attribute = "active";
173 	else
174 		return;
175 
176 	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
177 		if (strcmp(gc->lg_name, "index") == 0) {
178 			idx = atoi(gc->lg_val);
179 			break;
180 		}
181 	}
182 
183 	r = gctl_get_handle();
184 	gctl_ro_param(r, "class", -1, "PART");
185 	gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
186 	gctl_ro_param(r, "verb", -1, "set");
187 	gctl_ro_param(r, "attrib", -1, attribute);
188 	gctl_ro_param(r, "index", sizeof(idx), &idx);
189 
190 	errstr = gctl_issue(r);
191 	if (errstr != NULL && errstr[0] != '\0')
192 		gpart_show_error("Error", "Error marking partition active:",
193 		    errstr);
194 	gctl_free(r);
195 }
196 
197 static void
198 gpart_bootcode(struct ggeom *gp)
199 {
200 	const char *bootcode;
201 	struct gconfig *gc;
202 	struct gctl_req *r;
203 	const char *errstr, *scheme;
204 	uint8_t *boot;
205 	size_t bootsize, bytes;
206 	int bootfd;
207 
208 	/*
209 	 * Write default bootcode to the newly partitioned disk, if that
210 	 * applies on this platform.
211 	 */
212 	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
213 		if (strcmp(gc->lg_name, "scheme") == 0) {
214 			scheme = gc->lg_val;
215 			break;
216 		}
217 	}
218 
219 	bootcode = bootcode_path(scheme);
220 	if (bootcode == NULL)
221 		return;
222 
223 	bootfd = open(bootcode, O_RDONLY);
224 	if (bootfd <= 0) {
225 		dialog_msgbox("Bootcode Error", strerror(errno), 0, 0,
226 		    TRUE);
227 		return;
228 	}
229 
230 	bootsize = lseek(bootfd, 0, SEEK_END);
231 	boot = malloc(bootsize);
232 	lseek(bootfd, 0, SEEK_SET);
233 	bytes = 0;
234 	while (bytes < bootsize)
235 		bytes += read(bootfd, boot + bytes, bootsize - bytes);
236 	close(bootfd);
237 
238 	r = gctl_get_handle();
239 	gctl_ro_param(r, "class", -1, "PART");
240 	gctl_ro_param(r, "arg0", -1, gp->lg_name);
241 	gctl_ro_param(r, "verb", -1, "bootcode");
242 	gctl_ro_param(r, "bootcode", bootsize, boot);
243 
244 	errstr = gctl_issue(r);
245 	if (errstr != NULL && errstr[0] != '\0')
246 		gpart_show_error("Bootcode Error", NULL, errstr);
247 	gctl_free(r);
248 	free(boot);
249 }
250 
251 static void
252 gpart_partcode(struct gprovider *pp)
253 {
254 	struct gconfig *gc;
255 	const char *scheme;
256 	const char *indexstr;
257 	char message[255], command[255];
258 
259 	LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
260 		if (strcmp(gc->lg_name, "scheme") == 0) {
261 			scheme = gc->lg_val;
262 			break;
263 		}
264 	}
265 
266 	/* Make sure this partition scheme needs partcode on this platform */
267 	if (partcode_path(scheme) == NULL)
268 		return;
269 
270 	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
271 		if (strcmp(gc->lg_name, "index") == 0) {
272 			indexstr = gc->lg_val;
273 			break;
274 		}
275 	}
276 
277 	/* Shell out to gpart for partcode for now */
278 	sprintf(command, "gpart bootcode -p %s -i %s %s",
279 	    partcode_path(scheme), indexstr, pp->lg_geom->lg_name);
280 	if (system(command) != 0) {
281 		sprintf(message, "Error installing partcode on partition %s",
282 		    pp->lg_name);
283 		dialog_msgbox("Error", message, 0, 0, TRUE);
284 	}
285 }
286 
287 void
288 gpart_destroy(struct ggeom *lg_geom, int force)
289 {
290 	struct gprovider *pp;
291 	struct gctl_req *r;
292 	const char *errstr;
293 
294 	/* Begin with the hosing: delete all partitions */
295 	LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider)
296 		gpart_delete(pp);
297 
298 	/* Now destroy the geom itself */
299 	r = gctl_get_handle();
300 	gctl_ro_param(r, "class", -1, "PART");
301 	gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
302 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
303 	gctl_ro_param(r, "verb", -1, "destroy");
304 	errstr = gctl_issue(r);
305 	if (errstr != NULL && errstr[0] != '\0')
306 		gpart_show_error("Error", NULL, errstr);
307 	gctl_free(r);
308 
309 	/* If asked, commit the change */
310 	if (force) {
311 		r = gctl_get_handle();
312 		gctl_ro_param(r, "class", -1, "PART");
313 		gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
314 		gctl_ro_param(r, "verb", -1, "commit");
315 		errstr = gctl_issue(r);
316 		if (errstr != NULL && errstr[0] != '\0')
317 			gpart_show_error("Error", NULL, errstr);
318 		gctl_free(r);
319 	}
320 
321 	/* And any metadata associated with the partition scheme itself */
322 	delete_part_metadata(lg_geom->lg_name);
323 }
324 
325 void
326 gpart_edit(struct gprovider *pp)
327 {
328 	struct gctl_req *r;
329 	struct gconfig *gc;
330 	struct gconsumer *cp;
331 	struct ggeom *geom;
332 	const char *errstr, *oldtype, *scheme;
333 	struct partition_metadata *md;
334 	char sizestr[32];
335 	intmax_t idx;
336 	int hadlabel, choice, junk, nitems;
337 	unsigned i;
338 
339 	DIALOG_FORMITEM items[] = {
340 		{0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0,
341 		    FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)",
342 		    FALSE},
343 		{0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0,
344 		    FALSE, "Partition size. Append K, M, G for kilobytes, "
345 		    "megabytes or gigabytes.", FALSE},
346 		{0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
347 		    FALSE, "Path at which to mount this partition (leave blank "
348 		    "for swap, set to / for root filesystem)", FALSE},
349 		{0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
350 		    "Partition name. Not all partition schemes support this.",
351 		    FALSE},
352 	};
353 
354 	/*
355 	 * Find the PART geom we are manipulating. This may be a consumer of
356 	 * this provider, or its parent. Check the consumer case first.
357 	 */
358 	geom = NULL;
359 	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
360 		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
361 			char message[512];
362 			/*
363 			 * The PART object is a consumer, so the user wants to
364 			 * edit the partition table. gpart doesn't really
365 			 * support this, so we have to hose the whole table
366 			 * first.
367 			 */
368 
369 			sprintf(message, "Changing the partition scheme on "
370 			    "this disk (%s) requires deleting all existing "
371 			    "partitions on this drive. This will PERMANENTLY "
372 			    "ERASE any data stored here. Are you sure you want "
373 			    "to proceed?", cp->lg_geom->lg_name);
374 			dialog_vars.defaultno = TRUE;
375 			choice = dialog_yesno("Warning", message, 0, 0);
376 			dialog_vars.defaultno = FALSE;
377 
378 			if (choice == 1) /* cancel */
379 				return;
380 
381 			/* Destroy the geom and all sub-partitions */
382 			gpart_destroy(cp->lg_geom, 0);
383 
384 			/* Now re-partition and return */
385 			gpart_partition(cp->lg_geom->lg_name, NULL);
386 			return;
387 		}
388 
389 	if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
390 		geom = pp->lg_geom;
391 
392 	if (geom == NULL) {
393 		/* Disk not partitioned, so partition it */
394 		gpart_partition(pp->lg_geom->lg_name, NULL);
395 		return;
396 	}
397 
398 	LIST_FOREACH(gc, &geom->lg_config, lg_config) {
399 		if (strcmp(gc->lg_name, "scheme") == 0) {
400 			scheme = gc->lg_val;
401 			break;
402 		}
403 	}
404 
405 	nitems = scheme_supports_labels(scheme) ? 4 : 3;
406 
407 	/* Edit editable parameters of a partition */
408 	hadlabel = 0;
409 	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
410 		if (strcmp(gc->lg_name, "type") == 0) {
411 			oldtype = gc->lg_val;
412 			items[0].text = gc->lg_val;
413 		}
414 		if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) {
415 			hadlabel = 1;
416 			items[3].text = gc->lg_val;
417 		}
418 		if (strcmp(gc->lg_name, "index") == 0)
419 			idx = atoi(gc->lg_val);
420 	}
421 
422 	TAILQ_FOREACH(md, &part_metadata, metadata) {
423 		if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) {
424 			if (md->fstab != NULL)
425 				items[2].text = md->fstab->fs_file;
426 			break;
427 		}
428 	}
429 
430 	humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE,
431 	    HN_NOSPACE | HN_DECIMAL);
432 	items[1].text = sizestr;
433 
434 editpart:
435 	choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk);
436 
437 	if (choice) /* Cancel pressed */
438 		return;
439 
440 	/* Check if the label has a / in it */
441 	if (strchr(items[3].text, '/') != NULL) {
442 		dialog_msgbox("Error", "Label contains a /, which is not an "
443 		    "allowed character.", 0, 0, TRUE);
444 		goto editpart;
445 	}
446 
447 	if (strncmp(items[0].text, "freebsd-", 8) != 0 &&
448 	    items[0].text[0] != '\0') {
449 		char message[512];
450 
451 		sprintf(message, "Cannot mount unknown file system %s!\n",
452 		    items[0].text);
453 		dialog_msgbox("Error", message, 0, 0, TRUE);
454 		goto editpart;
455 	}
456 
457 	r = gctl_get_handle();
458 	gctl_ro_param(r, "class", -1, "PART");
459 	gctl_ro_param(r, "arg0", -1, geom->lg_name);
460 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
461 	gctl_ro_param(r, "verb", -1, "modify");
462 	gctl_ro_param(r, "index", sizeof(idx), &idx);
463 	if (hadlabel || items[3].text[0] != '\0')
464 		gctl_ro_param(r, "label", -1, items[3].text);
465 	gctl_ro_param(r, "type", -1, items[0].text);
466 	errstr = gctl_issue(r);
467 	if (errstr != NULL && errstr[0] != '\0') {
468 		gpart_show_error("Error", NULL, errstr);
469 		gctl_free(r);
470 		goto editpart;
471 	}
472 	gctl_free(r);
473 
474 	set_default_part_metadata(pp->lg_name, scheme, items[0].text,
475 	    items[2].text, strcmp(oldtype, items[0].text) != 0);
476 
477 	for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++)
478 		if (items[i].text_free)
479 			free(items[i].text);
480 }
481 
482 void
483 set_default_part_metadata(const char *name, const char *scheme,
484     const char *type, const char *mountpoint, int newfs)
485 {
486 	struct partition_metadata *md;
487 
488 	/* Set part metadata */
489 	md = get_part_metadata(name, 1);
490 
491 	if (newfs) {
492 		if (md->newfs != NULL) {
493 			free(md->newfs);
494 			md->newfs = NULL;
495 		}
496 
497 		if (strcmp(type, "freebsd-ufs") == 0) {
498 			md->newfs = malloc(255);
499 			sprintf(md->newfs, "newfs /dev/%s", name);
500 		}
501 	}
502 
503 	if (strcmp(type, "freebsd-swap") == 0)
504 		mountpoint = "none";
505 	if (strcmp(type, "freebsd-boot") == 0)
506 		md->bootcode = 1;
507 
508 	/* VTOC8 needs partcode in UFS partitions */
509 	if (strcmp(scheme, "VTOC8") == 0 && strcmp(type, "freebsd-ufs") == 0)
510 		md->bootcode = 1;
511 
512 	if (mountpoint == NULL || mountpoint[0] == '\0') {
513 		if (md->fstab != NULL) {
514 			free(md->fstab->fs_spec);
515 			free(md->fstab->fs_file);
516 			free(md->fstab->fs_vfstype);
517 			free(md->fstab->fs_mntops);
518 			free(md->fstab->fs_type);
519 			free(md->fstab);
520 			md->fstab = NULL;
521 		}
522 	} else {
523 		if (md->fstab == NULL) {
524 			md->fstab = malloc(sizeof(struct fstab));
525 		} else {
526 			free(md->fstab->fs_spec);
527 			free(md->fstab->fs_file);
528 			free(md->fstab->fs_vfstype);
529 			free(md->fstab->fs_mntops);
530 			free(md->fstab->fs_type);
531 		}
532 		md->fstab->fs_spec = malloc(strlen(name) + 6);
533 		sprintf(md->fstab->fs_spec, "/dev/%s", name);
534 		md->fstab->fs_file = strdup(mountpoint);
535 		/* Get VFS from text after freebsd-, if possible */
536 		if (strncmp("freebsd-", type, 8))
537 			md->fstab->fs_vfstype = strdup(&type[8]);
538 		else
539 			md->fstab->fs_vfstype = strdup(type); /* Guess */
540 		md->fstab->fs_vfstype = strdup(&type[8]);
541 		if (strcmp(type, "freebsd-swap") == 0) {
542 			md->fstab->fs_type = strdup(FSTAB_SW);
543 			md->fstab->fs_freq = 0;
544 			md->fstab->fs_passno = 0;
545 		} else {
546 			md->fstab->fs_type = strdup(FSTAB_RW);
547 			if (strcmp(mountpoint, "/") == 0) {
548 				md->fstab->fs_freq = 1;
549 				md->fstab->fs_passno = 1;
550 			} else {
551 				md->fstab->fs_freq = 2;
552 				md->fstab->fs_passno = 2;
553 			}
554 		}
555 		md->fstab->fs_mntops = strdup(md->fstab->fs_type);
556 	}
557 }
558 
559 static
560 int part_compare(const void *xa, const void *xb)
561 {
562 	struct gprovider **a = (struct gprovider **)xa;
563 	struct gprovider **b = (struct gprovider **)xb;
564 	intmax_t astart, bstart;
565 	struct gconfig *gc;
566 
567 	astart = bstart = 0;
568 	LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
569 		if (strcmp(gc->lg_name, "start") == 0) {
570 			astart = strtoimax(gc->lg_val, NULL, 0);
571 			break;
572 		}
573 	LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
574 		if (strcmp(gc->lg_name, "start") == 0) {
575 			bstart = strtoimax(gc->lg_val, NULL, 0);
576 			break;
577 		}
578 
579 	if (astart < bstart)
580 		return -1;
581 	else if (astart > bstart)
582 		return 1;
583 	else
584 		return 0;
585 }
586 
587 intmax_t
588 gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
589 {
590 	struct gconfig *gc;
591 	struct gprovider *pp, **providers;
592 	intmax_t lastend;
593 	intmax_t start, end;
594 	intmax_t maxsize, maxstart;
595 	intmax_t partstart, partend;
596 	int i, nparts;
597 
598 	/* Now get the maximum free size and free start */
599 	start = end = 0;
600 	LIST_FOREACH(gc, &geom->lg_config, lg_config) {
601 		if (strcmp(gc->lg_name, "first") == 0)
602 			start = strtoimax(gc->lg_val, NULL, 0);
603 		if (strcmp(gc->lg_name, "last") == 0)
604 			end = strtoimax(gc->lg_val, NULL, 0);
605 	}
606 
607 	i = nparts = 0;
608 	LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
609 		nparts++;
610 	providers = calloc(nparts, sizeof(providers[0]));
611 	LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
612 		providers[i++] = pp;
613 	qsort(providers, nparts, sizeof(providers[0]), part_compare);
614 
615 	lastend = start - 1;
616 	maxsize = 0;
617 	for (i = 0; i < nparts; i++) {
618 		pp = providers[i];
619 
620 		LIST_FOREACH(gc, &pp->lg_config, lg_config) {
621 			if (strcmp(gc->lg_name, "start") == 0)
622 				partstart = strtoimax(gc->lg_val, NULL, 0);
623 			if (strcmp(gc->lg_name, "end") == 0)
624 				partend = strtoimax(gc->lg_val, NULL, 0);
625 		}
626 
627 		if (partstart - lastend > maxsize) {
628 			maxsize = partstart - lastend - 1;
629 			maxstart = lastend + 1;
630 		}
631 
632 		lastend = partend;
633 	}
634 
635 	if (end - lastend > maxsize) {
636 		maxsize = end - lastend - 1;
637 		maxstart = lastend + 1;
638 	}
639 
640 	pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
641 
642 	/* Compute beginning of new partition and maximum available space */
643 	if (pp->lg_stripesize > 0 &&
644 	    (maxstart*pp->lg_sectorsize % pp->lg_stripesize) != 0) {
645 		intmax_t offset = (pp->lg_stripesize -
646 		    ((maxstart*pp->lg_sectorsize) % pp->lg_stripesize)) /
647 		    pp->lg_sectorsize;
648 		maxstart += offset;
649 		maxsize -= offset;
650 	}
651 
652 	if (npartstart != NULL)
653 		*npartstart = maxstart;
654 
655 	return (maxsize);
656 }
657 
658 void
659 gpart_create(struct gprovider *pp, char *default_type, char *default_size,
660      char *default_mountpoint, char **partname, int interactive)
661 {
662 	struct gctl_req *r;
663 	struct gconfig *gc;
664 	struct gconsumer *cp;
665 	struct ggeom *geom;
666 	const char *errstr, *scheme;
667 	char sizestr[32], startstr[32], output[64];
668 	intmax_t maxsize, size, sector, firstfree, stripe;
669 	uint64_t bytes;
670 	int nitems, choice, junk;
671 	unsigned i;
672 
673 	DIALOG_FORMITEM items[] = {
674 		{0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0,
675 		    FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-swap)",
676 		    FALSE},
677 		{0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0,
678 		    FALSE, "Partition size. Append K, M, G for kilobytes, "
679 		    "megabytes or gigabytes.", FALSE},
680 		{0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
681 		    FALSE, "Path at which to mount partition (blank for "
682 		    "swap, / for root filesystem)", FALSE},
683 		{0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
684 		    "Partition name. Not all partition schemes support this.",
685 		    FALSE},
686 	};
687 
688 	if (partname != NULL)
689 		*partname = NULL;
690 
691 	/* Record sector and stripe sizes */
692 	sector = pp->lg_sectorsize;
693 	stripe = pp->lg_stripesize;
694 
695 	/*
696 	 * Find the PART geom we are manipulating. This may be a consumer of
697 	 * this provider, or its parent. Check the consumer case first.
698 	 */
699 	geom = NULL;
700 	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
701 		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
702 			geom = cp->lg_geom;
703 			break;
704 		}
705 
706 	if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
707 		geom = pp->lg_geom;
708 
709 	/* Now get the partition scheme */
710 	scheme = NULL;
711 	if (geom != NULL) {
712 		LIST_FOREACH(gc, &geom->lg_config, lg_config)
713 			if (strcmp(gc->lg_name, "scheme") == 0)
714 				scheme = gc->lg_val;
715 	}
716 
717 	if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
718 		if (gpart_partition(pp->lg_geom->lg_name, NULL) == 0)
719 			dialog_msgbox("",
720 			    "The partition table has been successfully created."
721 			    " Please press Create again to create partitions.",
722 			    0, 0, TRUE);
723 
724 		return;
725 	}
726 
727 	/*
728 	 * If we still don't have a geom, either the user has
729 	 * canceled partitioning or there has been an error which has already
730 	 * been displayed, so bail.
731 	 */
732 	if (geom == NULL)
733 		return;
734 
735 	maxsize = size = gpart_max_free(geom, &firstfree);
736 	if (size <= 0) {
737 		dialog_msgbox("Error", "No free space left on device.", 0, 0,
738 		    TRUE);
739 		return;
740 	}
741 
742 	humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
743 	    HN_NOSPACE | HN_DECIMAL);
744 	items[1].text = sizestr;
745 
746 	/* Special-case the MBR default type for nested partitions */
747 	if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "PC98") == 0)
748 		items[0].text = "freebsd";
749 
750 	nitems = scheme_supports_labels(scheme) ? 4 : 3;
751 
752 	if (default_type != NULL)
753 		items[0].text = default_type;
754 	if (default_size != NULL)
755 		items[1].text = default_size;
756 	if (default_mountpoint != NULL)
757 		items[2].text = default_mountpoint;
758 
759 addpartform:
760 	if (interactive) {
761 		choice = dlg_form("Add Partition", "", 0, 0, 0, nitems,
762 		    items, &junk);
763 		if (choice) /* Cancel pressed */
764 			return;
765 	}
766 
767 	size = maxsize;
768 	if (strlen(items[1].text) > 0) {
769 		if (expand_number(items[1].text, &bytes) != 0) {
770 			char error[512];
771 
772 			sprintf(error, "Invalid size: %s\n", strerror(errno));
773 			dialog_msgbox("Error", error, 0, 0, TRUE);
774 			goto addpartform;
775 		}
776 		size = MIN((intmax_t)(bytes/sector), maxsize);
777 	}
778 
779 	/* Check if the label has a / in it */
780 	if (strchr(items[3].text, '/') != NULL) {
781 		dialog_msgbox("Error", "Label contains a /, which is not an "
782 		    "allowed character.", 0, 0, TRUE);
783 		goto addpartform;
784 	}
785 
786 	/* Warn if no mountpoint set */
787 	if (strcmp(items[0].text, "freebsd-ufs") == 0 &&
788 	    items[2].text[0] != '/') {
789 		dialog_vars.defaultno = TRUE;
790 		choice = dialog_yesno("Warning",
791 		    "This partition does not have a valid mountpoint "
792 		    "(for the partition from which you intend to boot the "
793 		    "operating system, the mountpoint should be /). Are you "
794 		    "sure you want to continue?"
795 		, 0, 0);
796 		dialog_vars.defaultno = FALSE;
797 		if (choice == 1) /* cancel */
798 			goto addpartform;
799 	}
800 
801 	/* If this is the root partition, check that this scheme is bootable */
802 	if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) {
803 		char message[512];
804 		sprintf(message, "This partition scheme (%s) is not bootable "
805 		    "on this platform. Are you sure you want to proceed?",
806 		    scheme);
807 		dialog_vars.defaultno = TRUE;
808 		choice = dialog_yesno("Warning", message, 0, 0);
809 		dialog_vars.defaultno = FALSE;
810 		if (choice == 1) /* cancel */
811 			goto addpartform;
812 	}
813 
814 	/*
815 	 * If this is the root partition, and we need a boot partition, ask
816 	 * the user to add one.
817 	 */
818 	if (strcmp(items[2].text, "/") == 0 && bootpart_size(scheme) > 0) {
819 		if (interactive)
820 			choice = dialog_yesno("Boot Partition",
821 			    "This partition scheme requires a boot partition "
822 			    "for the disk to be bootable. Would you like to "
823 			    "make one now?", 0, 0);
824 		else
825 			choice = 0;
826 
827 		if (choice == 0) { /* yes */
828 			r = gctl_get_handle();
829 			gctl_ro_param(r, "class", -1, "PART");
830 			gctl_ro_param(r, "arg0", -1, geom->lg_name);
831 			gctl_ro_param(r, "flags", -1, GPART_FLAGS);
832 			gctl_ro_param(r, "verb", -1, "add");
833 			gctl_ro_param(r, "type", -1, "freebsd-boot");
834 			snprintf(sizestr, sizeof(sizestr), "%jd",
835 			    bootpart_size(scheme) / sector);
836 			gctl_ro_param(r, "size", -1, sizestr);
837 			snprintf(startstr, sizeof(startstr), "%jd", firstfree);
838 			gctl_ro_param(r, "start", -1, startstr);
839 			gctl_rw_param(r, "output", sizeof(output), output);
840 			errstr = gctl_issue(r);
841 			if (errstr != NULL && errstr[0] != '\0')
842 				gpart_show_error("Error", NULL, errstr);
843 			gctl_free(r);
844 
845 			get_part_metadata(strtok(output, " "), 1)->bootcode = 1;
846 
847 			/* Now adjust the part we are really adding forward */
848 			firstfree += bootpart_size(scheme) / sector;
849 			size -= (bootpart_size(scheme) + stripe)/sector;
850 			if (stripe > 0 && (firstfree*sector % stripe) != 0)
851 				firstfree += (stripe - ((firstfree*sector) %
852 				    stripe)) / sector;
853 		}
854 	}
855 
856 	r = gctl_get_handle();
857 	gctl_ro_param(r, "class", -1, "PART");
858 	gctl_ro_param(r, "arg0", -1, geom->lg_name);
859 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
860 	gctl_ro_param(r, "verb", -1, "add");
861 
862 	gctl_ro_param(r, "type", -1, items[0].text);
863 	snprintf(sizestr, sizeof(sizestr), "%jd", size);
864 	gctl_ro_param(r, "size", -1, sizestr);
865 	snprintf(startstr, sizeof(startstr), "%jd", firstfree);
866 	gctl_ro_param(r, "start", -1, startstr);
867 	if (items[3].text[0] != '\0')
868 		gctl_ro_param(r, "label", -1, items[3].text);
869 	gctl_rw_param(r, "output", sizeof(output), output);
870 
871 	errstr = gctl_issue(r);
872 	if (errstr != NULL && errstr[0] != '\0') {
873 		gpart_show_error("Error", NULL, errstr);
874 		gctl_free(r);
875 		goto addpartform;
876 	}
877 
878 	if (strcmp(items[0].text, "freebsd-boot") == 0)
879 		get_part_metadata(strtok(output, " "), 1)->bootcode = 1;
880 	else if (strcmp(items[0].text, "freebsd") == 0)
881 		gpart_partition(strtok(output, " "), "BSD");
882 	else
883 		set_default_part_metadata(strtok(output, " "), scheme,
884 		    items[0].text, items[2].text, 1);
885 
886 	for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++)
887 		if (items[i].text_free)
888 			free(items[i].text);
889 	gctl_free(r);
890 
891 	if (partname != NULL)
892 		*partname = strdup(strtok(output, " "));
893 }
894 
895 void
896 gpart_delete(struct gprovider *pp)
897 {
898 	struct gconfig *gc;
899 	struct ggeom *geom;
900 	struct gconsumer *cp;
901 	struct gctl_req *r;
902 	const char *errstr;
903 	intmax_t idx;
904 	int choice, is_partition;
905 
906 	/* Is it a partition? */
907 	is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
908 
909 	/* Find out if this is the root of a gpart geom */
910 	geom = NULL;
911 	LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
912 		if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
913 			geom = cp->lg_geom;
914 			break;
915 		}
916 
917 	/* Destroy all consumers */
918 	if (geom != NULL) {
919 		if (is_partition) {
920 			char message[512];
921 			/*
922 			 * We have to actually really delete the sub-partition
923 			 * tree so that the consumers will go away and the
924 			 * partition can be deleted. Warn the user.
925 			 */
926 
927 			sprintf(message, "Deleting this partition (%s) "
928 			    "requires deleting all existing sub-partitions. "
929 			    "This will PERMANENTLY ERASE any data stored here "
930 			    "and CANNOT BE REVERTED. Are you sure you want to "
931 			    "proceed?", cp->lg_geom->lg_name);
932 			dialog_vars.defaultno = TRUE;
933 			choice = dialog_yesno("Warning", message, 0, 0);
934 			dialog_vars.defaultno = FALSE;
935 
936 			if (choice == 1) /* cancel */
937 				return;
938 		}
939 
940 		gpart_destroy(geom, is_partition);
941 	}
942 
943 	/*
944 	 * If this is not a partition, see if that is a problem, complain if
945 	 * necessary, and return always, since we need not do anything further,
946 	 * error or no.
947 	 */
948 	if (!is_partition) {
949 		if (geom == NULL)
950 			dialog_msgbox("Error",
951 			    "Only partitions can be deleted.", 0, 0, TRUE);
952 		return;
953 	}
954 
955 	r = gctl_get_handle();
956 	gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
957 	gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
958 	gctl_ro_param(r, "flags", -1, GPART_FLAGS);
959 	gctl_ro_param(r, "verb", -1, "delete");
960 
961 	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
962 		if (strcmp(gc->lg_name, "index") == 0) {
963 			idx = atoi(gc->lg_val);
964 			gctl_ro_param(r, "index", sizeof(idx), &idx);
965 			break;
966 		}
967 	}
968 
969 	errstr = gctl_issue(r);
970 	if (errstr != NULL && errstr[0] != '\0') {
971 		gpart_show_error("Error", NULL, errstr);
972 		gctl_free(r);
973 		return;
974 	}
975 
976 	gctl_free(r);
977 
978 	delete_part_metadata(pp->lg_name);
979 }
980 
981 void
982 gpart_revert_all(struct gmesh *mesh)
983 {
984 	struct gclass *classp;
985 	struct gconfig *gc;
986 	struct ggeom *gp;
987 	struct gctl_req *r;
988 	const char *errstr;
989 	const char *modified;
990 
991 	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
992 		if (strcmp(classp->lg_name, "PART") == 0)
993 			break;
994 	}
995 
996 	if (strcmp(classp->lg_name, "PART") != 0) {
997 		dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
998 		return;
999 	}
1000 
1001 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1002 		modified = "true"; /* XXX: If we don't know (kernel too old),
1003 				    * assume there are modifications. */
1004 		LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1005 			if (strcmp(gc->lg_name, "modified") == 0) {
1006 				modified = gc->lg_val;
1007 				break;
1008 			}
1009 		}
1010 
1011 		if (strcmp(modified, "false") == 0)
1012 			continue;
1013 
1014 		r = gctl_get_handle();
1015 		gctl_ro_param(r, "class", -1, "PART");
1016 		gctl_ro_param(r, "arg0", -1, gp->lg_name);
1017 		gctl_ro_param(r, "verb", -1, "undo");
1018 
1019 		errstr = gctl_issue(r);
1020 		if (errstr != NULL && errstr[0] != '\0')
1021 			gpart_show_error("Error", NULL, errstr);
1022 		gctl_free(r);
1023 	}
1024 }
1025 
1026 void
1027 gpart_commit(struct gmesh *mesh)
1028 {
1029 	struct partition_metadata *md;
1030 	struct gclass *classp;
1031 	struct ggeom *gp;
1032 	struct gconfig *gc;
1033 	struct gconsumer *cp;
1034 	struct gprovider *pp;
1035 	struct gctl_req *r;
1036 	const char *errstr;
1037 	const char *modified;
1038 
1039 	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1040 		if (strcmp(classp->lg_name, "PART") == 0)
1041 			break;
1042 	}
1043 
1044 	if (strcmp(classp->lg_name, "PART") != 0) {
1045 		dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1046 		return;
1047 	}
1048 
1049 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1050 		modified = "true"; /* XXX: If we don't know (kernel too old),
1051 				    * assume there are modifications. */
1052 		LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1053 			if (strcmp(gc->lg_name, "modified") == 0) {
1054 				modified = gc->lg_val;
1055 				break;
1056 			}
1057 		}
1058 
1059 		if (strcmp(modified, "false") == 0)
1060 			continue;
1061 
1062 		/* Add bootcode if necessary, before the commit */
1063 		md = get_part_metadata(gp->lg_name, 0);
1064 		if (md != NULL && md->bootcode)
1065 			gpart_bootcode(gp);
1066 
1067 		/* Now install partcode on its partitions, if necessary */
1068 		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1069 			md = get_part_metadata(pp->lg_name, 0);
1070 			if (md == NULL || !md->bootcode)
1071 				continue;
1072 
1073 			/* Mark this partition active if that's required */
1074 			gpart_activate(pp);
1075 
1076 			/* Check if the partition has sub-partitions */
1077 			LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1078 				if (strcmp(cp->lg_geom->lg_class->lg_name,
1079 				    "PART") == 0)
1080 					break;
1081 
1082 			if (cp == NULL) /* No sub-partitions */
1083 				gpart_partcode(pp);
1084 		}
1085 
1086 		r = gctl_get_handle();
1087 		gctl_ro_param(r, "class", -1, "PART");
1088 		gctl_ro_param(r, "arg0", -1, gp->lg_name);
1089 		gctl_ro_param(r, "verb", -1, "commit");
1090 
1091 		errstr = gctl_issue(r);
1092 		if (errstr != NULL && errstr[0] != '\0')
1093 			gpart_show_error("Error", NULL, errstr);
1094 		gctl_free(r);
1095 	}
1096 }
1097 
1098