2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2011 Nathan Whitehorn
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/param.h>
43 #define GPART_FLAGS "x" /* Do not commit changes by default */
46 gpart_show_error(const char *title, const char *explanation, const char *errstr)
52 if (explanation == NULL)
55 error = strtol(errstr, &errmsg, 0);
56 if (errmsg != errstr) {
57 while (errmsg[0] == ' ')
59 if (errmsg[0] != '\0')
60 sprintf(message, "%s%s. %s", explanation,
61 strerror(error), errmsg);
63 sprintf(message, "%s%s", explanation, strerror(error));
65 sprintf(message, "%s%s", explanation, errmsg);
68 dialog_msgbox(title, message, 0, 0, TRUE);
72 scheme_supports_labels(const char *scheme)
74 if (strcmp(scheme, "APM") == 0)
76 if (strcmp(scheme, "GPT") == 0)
83 newfs_command(const char *fstype, char *command, int use_default)
85 if (strcmp(fstype, "freebsd-ufs") == 0) {
87 DIALOG_LISTITEM items[] = {
88 {"UFS1", "UFS Version 1",
89 "Use version 1 of the UFS file system instead "
90 "of version 2 (not recommended)", 0 },
92 "Enable softupdates (default)", 1 },
93 {"SUJ", "Softupdates journaling",
94 "Enable file system journaling (default - "
95 "turn off for SSDs)", 1 },
96 {"TRIM", "Enable SSD TRIM support",
97 "Enable TRIM support, useful on solid-state drives",
103 choice = dlg_checklist("UFS Options", "", 0, 0, 0,
104 nitems(items), items, NULL,
106 if (choice == 1) /* Cancel */
110 strcpy(command, "newfs ");
111 for (i = 0; i < (int)nitems(items); i++) {
112 if (items[i].state == 0)
114 if (strcmp(items[i].name, "UFS1") == 0)
115 strcat(command, "-O1 ");
116 else if (strcmp(items[i].name, "SU") == 0)
117 strcat(command, "-U ");
118 else if (strcmp(items[i].name, "SUJ") == 0)
119 strcat(command, "-j ");
120 else if (strcmp(items[i].name, "TRIM") == 0)
121 strcat(command, "-t ");
123 } else if (strcmp(fstype, "freebsd-zfs") == 0) {
125 DIALOG_LISTITEM items[] = {
126 {"fletcher4", "checksum algorithm: fletcher4",
127 "Use fletcher4 for data integrity checking. "
129 {"fletcher2", "checksum algorithm: fletcher2",
130 "Use fletcher2 for data integrity checking. "
131 "(not recommended)", 0 },
132 {"sha256", "checksum algorithm: sha256",
133 "Use sha256 for data integrity checking. "
134 "(not recommended)", 0 },
135 {"atime", "Update atimes for files",
136 "Disable atime update", 0 },
141 choice = dlg_checklist("ZFS Options", "", 0, 0, 0,
142 nitems(items), items, NULL,
144 if (choice == 1) /* Cancel */
148 strcpy(command, "zpool create -f -m none ");
149 if (getenv("BSDINSTALL_TMPBOOT") != NULL) {
150 char zfsboot_path[MAXPATHLEN];
151 snprintf(zfsboot_path, sizeof(zfsboot_path), "%s/zfs",
152 getenv("BSDINSTALL_TMPBOOT"));
153 mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP |
155 sprintf(command, "%s -o cachefile=%s/zpool.cache ",
156 command, zfsboot_path);
158 for (i = 0; i < (int)nitems(items); i++) {
159 if (items[i].state == 0)
161 if (strcmp(items[i].name, "fletcher4") == 0)
162 strcat(command, "-O checksum=fletcher4 ");
163 else if (strcmp(items[i].name, "fletcher2") == 0)
164 strcat(command, "-O checksum=fletcher2 ");
165 else if (strcmp(items[i].name, "sha256") == 0)
166 strcat(command, "-O checksum=sha256 ");
167 else if (strcmp(items[i].name, "atime") == 0)
168 strcat(command, "-O atime=off ");
170 } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0 ||
171 strcmp(fstype, "ms-basic-data") == 0) {
173 DIALOG_LISTITEM items[] = {
174 {"FAT32", "FAT Type 32",
175 "Create a FAT32 filesystem (default)", 1 },
176 {"FAT16", "FAT Type 16",
177 "Create a FAT16 filesystem", 0 },
178 {"FAT12", "FAT Type 12",
179 "Create a FAT12 filesystem", 0 },
184 choice = dlg_checklist("FAT Options", "", 0, 0, 0,
185 nitems(items), items, NULL,
187 if (choice == 1) /* Cancel */
191 strcpy(command, "newfs_msdos ");
192 for (i = 0; i < (int)nitems(items); i++) {
193 if (items[i].state == 0)
195 if (strcmp(items[i].name, "FAT16") == 0)
196 strcat(command, "-F 16 ");
197 else if (strcmp(items[i].name, "FAT12") == 0)
198 strcat(command, "-F 12 ");
202 dialog_msgbox("Error", "No configurable options exist "
203 "for this filesystem.", 0, 0, TRUE);
209 choose_part_type(const char *def_scheme)
212 const char *scheme = NULL;
214 DIALOG_LISTITEM items[] = {
215 {"APM", "Apple Partition Map",
216 "Bootable on PowerPC Apple Hardware", 0 },
217 {"BSD", "BSD Labels",
218 "Bootable on most x86 systems", 0 },
219 {"GPT", "GUID Partition Table",
220 "Bootable on most x86 systems and EFI aware ARM64", 0 },
221 {"MBR", "DOS Partitions",
222 "Bootable on most x86 systems", 0 },
223 {"VTOC8", "Sun VTOC8 Partition Table",
224 "Bootable on Sun SPARC systems", 0 },
228 dialog_vars.default_item = __DECONST(char *, def_scheme);
229 cancel = dlg_menu("Partition Scheme",
230 "Select a partition scheme for this volume:", 0, 0, 0,
231 nitems(items), items, &choice, NULL);
232 dialog_vars.default_item = NULL;
237 if (!is_scheme_bootable(items[choice].name)) {
239 sprintf(message, "This partition scheme (%s) is not "
240 "bootable on this platform. Are you sure you want "
241 "to proceed?", items[choice].name);
242 dialog_vars.defaultno = TRUE;
243 cancel = dialog_yesno("Warning", message, 0, 0);
244 dialog_vars.defaultno = FALSE;
245 if (cancel) /* cancel */
249 scheme = items[choice].name;
255 gpart_partition(const char *lg_name, const char *scheme)
262 if (scheme == NULL) {
263 scheme = choose_part_type(default_scheme());
268 if (!is_scheme_bootable(scheme)) {
270 sprintf(message, "This partition scheme (%s) is not "
271 "bootable on this platform. Are you sure you want "
272 "to proceed?", scheme);
273 dialog_vars.defaultno = TRUE;
274 cancel = dialog_yesno("Warning", message, 0, 0);
275 dialog_vars.defaultno = FALSE;
276 if (cancel) { /* cancel */
277 /* Reset scheme so user can choose another */
284 r = gctl_get_handle();
285 gctl_ro_param(r, "class", -1, "PART");
286 gctl_ro_param(r, "arg0", -1, lg_name);
287 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
288 gctl_ro_param(r, "scheme", -1, scheme);
289 gctl_ro_param(r, "verb", -1, "create");
291 errstr = gctl_issue(r);
292 if (errstr != NULL && errstr[0] != '\0') {
293 gpart_show_error("Error", NULL, errstr);
300 if (bootcode_path(scheme) != NULL)
301 get_part_metadata(lg_name, 1)->bootcode = 1;
306 gpart_activate(struct gprovider *pp)
310 const char *errstr, *scheme;
311 const char *attribute = NULL;
315 * Some partition schemes need this partition to be marked 'active'
316 * for it to be bootable.
318 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
319 if (strcmp(gc->lg_name, "scheme") == 0) {
325 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0)
326 attribute = "active";
330 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
331 if (strcmp(gc->lg_name, "index") == 0) {
332 idx = atoi(gc->lg_val);
337 r = gctl_get_handle();
338 gctl_ro_param(r, "class", -1, "PART");
339 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
340 gctl_ro_param(r, "verb", -1, "set");
341 gctl_ro_param(r, "attrib", -1, attribute);
342 gctl_ro_param(r, "index", sizeof(idx), &idx);
344 errstr = gctl_issue(r);
345 if (errstr != NULL && errstr[0] != '\0')
346 gpart_show_error("Error", "Error marking partition active:",
352 gpart_set_root(const char *lg_name, const char *attribute)
357 r = gctl_get_handle();
358 gctl_ro_param(r, "class", -1, "PART");
359 gctl_ro_param(r, "arg0", -1, lg_name);
360 gctl_ro_param(r, "flags", -1, "C");
361 gctl_ro_param(r, "verb", -1, "set");
362 gctl_ro_param(r, "attrib", -1, attribute);
364 errstr = gctl_issue(r);
365 if (errstr != NULL && errstr[0] != '\0')
366 gpart_show_error("Error", "Error setting parameter on disk:",
372 gpart_bootcode(struct ggeom *gp)
374 const char *bootcode;
377 const char *errstr, *scheme;
379 size_t bootsize, bytes;
383 * Write default bootcode to the newly partitioned disk, if that
384 * applies on this platform.
386 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
387 if (strcmp(gc->lg_name, "scheme") == 0) {
393 bootcode = bootcode_path(scheme);
394 if (bootcode == NULL)
397 bootfd = open(bootcode, O_RDONLY);
399 dialog_msgbox("Bootcode Error", strerror(errno), 0, 0,
404 bootsize = lseek(bootfd, 0, SEEK_END);
405 boot = malloc(bootsize);
406 lseek(bootfd, 0, SEEK_SET);
408 while (bytes < bootsize)
409 bytes += read(bootfd, boot + bytes, bootsize - bytes);
412 r = gctl_get_handle();
413 gctl_ro_param(r, "class", -1, "PART");
414 gctl_ro_param(r, "arg0", -1, gp->lg_name);
415 gctl_ro_param(r, "verb", -1, "bootcode");
416 gctl_ro_param(r, "bootcode", bootsize, boot);
418 errstr = gctl_issue(r);
419 if (errstr != NULL && errstr[0] != '\0')
420 gpart_show_error("Bootcode Error", NULL, errstr);
426 gpart_partcode(struct gprovider *pp, const char *fstype)
430 const char *indexstr;
431 char message[255], command[255];
433 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
434 if (strcmp(gc->lg_name, "scheme") == 0) {
440 /* Make sure this partition scheme needs partcode on this platform */
441 if (partcode_path(scheme, fstype) == NULL)
444 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
445 if (strcmp(gc->lg_name, "index") == 0) {
446 indexstr = gc->lg_val;
451 /* Shell out to gpart for partcode for now */
452 sprintf(command, "gpart bootcode -p %s -i %s %s",
453 partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name);
454 if (system(command) != 0) {
455 sprintf(message, "Error installing partcode on partition %s",
457 dialog_msgbox("Error", message, 0, 0, TRUE);
462 gpart_destroy(struct ggeom *lg_geom)
465 struct gprovider *pp;
469 /* Delete all child metadata */
470 LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider)
473 /* Revert any local changes to get this geom into a pristine state */
474 r = gctl_get_handle();
475 gctl_ro_param(r, "class", -1, "PART");
476 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
477 gctl_ro_param(r, "verb", -1, "undo");
478 gctl_issue(r); /* Ignore errors -- these are non-fatal */
481 /* Now destroy the geom itself */
482 r = gctl_get_handle();
483 gctl_ro_param(r, "class", -1, "PART");
484 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
485 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
486 gctl_ro_param(r, "force", sizeof(force), &force);
487 gctl_ro_param(r, "verb", -1, "destroy");
488 errstr = gctl_issue(r);
489 if (errstr != NULL && errstr[0] != '\0') {
491 * Check if we reverted away the existence of the geom
492 * altogether. Show all other errors to the user.
494 if (strtol(errstr, NULL, 0) != EINVAL)
495 gpart_show_error("Error", NULL, errstr);
499 /* And any metadata associated with the partition scheme itself */
500 delete_part_metadata(lg_geom->lg_name);
504 gpart_edit(struct gprovider *pp)
508 struct gconsumer *cp;
510 const char *errstr, *oldtype, *scheme;
511 struct partition_metadata *md;
515 int hadlabel, choice, junk, nitems;
518 DIALOG_FORMITEM items[] = {
519 {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0,
520 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
521 "freebsd-swap)", FALSE},
522 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0,
523 FALSE, "Partition size. Append K, M, G for kilobytes, "
524 "megabytes or gigabytes.", FALSE},
525 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
526 FALSE, "Path at which to mount this partition (leave blank "
527 "for swap, set to / for root filesystem)", FALSE},
528 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
529 "Partition name. Not all partition schemes support this.",
534 * Find the PART geom we are manipulating. This may be a consumer of
535 * this provider, or its parent. Check the consumer case first.
538 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
539 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
540 /* Check for zombie geoms, treating them as blank */
542 LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) {
543 if (strcmp(gc->lg_name, "scheme") == 0) {
548 if (scheme == NULL || strcmp(scheme, "(none)") == 0) {
549 gpart_partition(cp->lg_geom->lg_name, NULL);
553 /* If this is a nested partition, edit as usual */
554 if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
557 /* Destroy the geom and all sub-partitions */
558 gpart_destroy(cp->lg_geom);
560 /* Now re-partition and return */
561 gpart_partition(cp->lg_geom->lg_name, NULL);
565 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
569 /* Disk not partitioned, so partition it */
570 gpart_partition(pp->lg_name, NULL);
574 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
575 if (strcmp(gc->lg_name, "scheme") == 0) {
581 nitems = scheme_supports_labels(scheme) ? 4 : 3;
583 /* Edit editable parameters of a partition */
585 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
586 if (strcmp(gc->lg_name, "type") == 0) {
587 oldtype = gc->lg_val;
588 items[0].text = gc->lg_val;
590 if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) {
592 items[3].text = gc->lg_val;
594 if (strcmp(gc->lg_name, "index") == 0)
595 idx = atoi(gc->lg_val);
598 TAILQ_FOREACH(md, &part_metadata, metadata) {
599 if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) {
600 if (md->fstab != NULL)
601 items[2].text = md->fstab->fs_file;
606 humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE,
607 HN_NOSPACE | HN_DECIMAL);
608 items[1].text = sizestr;
611 choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk);
613 if (choice) /* Cancel pressed */
616 /* If this is the root partition, check that this fs is bootable */
617 if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme,
620 sprintf(message, "This file system (%s) is not bootable "
621 "on this system. Are you sure you want to proceed?",
623 dialog_vars.defaultno = TRUE;
624 choice = dialog_yesno("Warning", message, 0, 0);
625 dialog_vars.defaultno = FALSE;
626 if (choice == 1) /* cancel */
630 /* Check if the label has a / in it */
631 if (strchr(items[3].text, '/') != NULL) {
632 dialog_msgbox("Error", "Label contains a /, which is not an "
633 "allowed character.", 0, 0, TRUE);
637 r = gctl_get_handle();
638 gctl_ro_param(r, "class", -1, "PART");
639 gctl_ro_param(r, "arg0", -1, geom->lg_name);
640 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
641 gctl_ro_param(r, "verb", -1, "modify");
642 gctl_ro_param(r, "index", sizeof(idx), &idx);
643 if (hadlabel || items[3].text[0] != '\0')
644 gctl_ro_param(r, "label", -1, items[3].text);
645 gctl_ro_param(r, "type", -1, items[0].text);
646 errstr = gctl_issue(r);
647 if (errstr != NULL && errstr[0] != '\0') {
648 gpart_show_error("Error", NULL, errstr);
654 newfs_command(items[0].text, newfs, 1);
655 set_default_part_metadata(pp->lg_name, scheme, items[0].text,
656 items[2].text, (strcmp(oldtype, items[0].text) != 0) ?
660 if (strcmp(oldtype, items[0].text) != 0 && cp != NULL)
661 gpart_destroy(cp->lg_geom);
662 if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text,
664 gpart_partition(pp->lg_name, "BSD");
666 for (i = 0; i < nitems(items); i++)
667 if (items[i].text_free)
672 set_default_part_metadata(const char *name, const char *scheme,
673 const char *type, const char *mountpoint, const char *newfs)
675 struct partition_metadata *md;
676 char *zpool_name = NULL;
677 const char *default_bootmount = NULL;
680 /* Set part metadata */
681 md = get_part_metadata(name, 1);
684 if (md->newfs != NULL) {
689 if (newfs != NULL && newfs[0] != '\0') {
690 md->newfs = malloc(strlen(newfs) + strlen(" /dev/") +
691 strlen(mountpoint) + 5 + strlen(name) + 1);
692 if (strcmp("freebsd-zfs", type) == 0) {
693 zpool_name = strdup((strlen(mountpoint) == 1) ?
694 "root" : &mountpoint[1]);
695 for (i = 0; zpool_name[i] != 0; i++)
696 if (!isalnum(zpool_name[i]))
698 sprintf(md->newfs, "%s %s /dev/%s", newfs,
701 sprintf(md->newfs, "%s /dev/%s", newfs, name);
706 if (strcmp(type, "freebsd-swap") == 0)
708 if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) {
709 if (default_bootmount == NULL)
711 else if (mountpoint == NULL || strlen(mountpoint) == 0)
712 mountpoint = default_bootmount;
715 /* VTOC8 needs partcode at the start of partitions */
716 if (strcmp(scheme, "VTOC8") == 0 && (strcmp(type, "freebsd-ufs") == 0
717 || strcmp(type, "freebsd-zfs") == 0))
720 if (mountpoint == NULL || mountpoint[0] == '\0') {
721 if (md->fstab != NULL) {
722 free(md->fstab->fs_spec);
723 free(md->fstab->fs_file);
724 free(md->fstab->fs_vfstype);
725 free(md->fstab->fs_mntops);
726 free(md->fstab->fs_type);
731 if (md->fstab == NULL) {
732 md->fstab = malloc(sizeof(struct fstab));
734 free(md->fstab->fs_spec);
735 free(md->fstab->fs_file);
736 free(md->fstab->fs_vfstype);
737 free(md->fstab->fs_mntops);
738 free(md->fstab->fs_type);
740 if (strcmp("freebsd-zfs", type) == 0) {
741 md->fstab->fs_spec = strdup(zpool_name);
743 md->fstab->fs_spec = malloc(strlen(name) +
744 strlen("/dev/") + 1);
745 sprintf(md->fstab->fs_spec, "/dev/%s", name);
747 md->fstab->fs_file = strdup(mountpoint);
748 /* Get VFS from text after freebsd-, if possible */
749 if (strncmp("freebsd-", type, 8) == 0)
750 md->fstab->fs_vfstype = strdup(&type[8]);
751 else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0
752 || strcmp("ms-basic-data", type) == 0)
753 md->fstab->fs_vfstype = strdup("msdosfs");
755 md->fstab->fs_vfstype = strdup(type); /* Guess */
756 if (strcmp(type, "freebsd-swap") == 0) {
757 md->fstab->fs_type = strdup(FSTAB_SW);
758 md->fstab->fs_freq = 0;
759 md->fstab->fs_passno = 0;
760 } else if (strcmp(type, "freebsd-zfs") == 0) {
761 md->fstab->fs_type = strdup(FSTAB_RW);
762 md->fstab->fs_freq = 0;
763 md->fstab->fs_passno = 0;
765 md->fstab->fs_type = strdup(FSTAB_RW);
766 if (strcmp(mountpoint, "/") == 0) {
767 md->fstab->fs_freq = 1;
768 md->fstab->fs_passno = 1;
770 md->fstab->fs_freq = 2;
771 md->fstab->fs_passno = 2;
774 md->fstab->fs_mntops = strdup(md->fstab->fs_type);
777 if (zpool_name != NULL)
782 int part_compare(const void *xa, const void *xb)
784 struct gprovider **a = (struct gprovider **)xa;
785 struct gprovider **b = (struct gprovider **)xb;
786 intmax_t astart, bstart;
790 LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
791 if (strcmp(gc->lg_name, "start") == 0) {
792 astart = strtoimax(gc->lg_val, NULL, 0);
795 LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
796 if (strcmp(gc->lg_name, "start") == 0) {
797 bstart = strtoimax(gc->lg_val, NULL, 0);
803 else if (astart > bstart)
810 gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
813 struct gprovider *pp, **providers;
814 intmax_t sectorsize, stripesize, offset;
817 intmax_t maxsize, maxstart;
818 intmax_t partstart, partend;
821 /* Now get the maximum free size and free start */
823 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
824 if (strcmp(gc->lg_name, "first") == 0)
825 start = strtoimax(gc->lg_val, NULL, 0);
826 if (strcmp(gc->lg_name, "last") == 0)
827 end = strtoimax(gc->lg_val, NULL, 0);
831 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
833 providers = calloc(nparts, sizeof(providers[0]));
834 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
836 qsort(providers, nparts, sizeof(providers[0]), part_compare);
840 for (i = 0; i < nparts; i++) {
843 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
844 if (strcmp(gc->lg_name, "start") == 0)
845 partstart = strtoimax(gc->lg_val, NULL, 0);
846 if (strcmp(gc->lg_name, "end") == 0)
847 partend = strtoimax(gc->lg_val, NULL, 0);
850 if (partstart - lastend > maxsize) {
851 maxsize = partstart - lastend - 1;
852 maxstart = lastend + 1;
858 if (end - lastend > maxsize) {
859 maxsize = end - lastend - 1;
860 maxstart = lastend + 1;
863 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
866 * Round the start and size of the largest available space up to
867 * the nearest multiple of the adjusted stripe size.
869 * The adjusted stripe size is the least common multiple of the
870 * actual stripe size, or the sector size if no stripe size was
871 * reported, and 4096. The reason for this is that contemporary
872 * disks often have 4096-byte physical sectors but report 512
873 * bytes instead for compatibility with older / broken operating
874 * systems and BIOSes. For the same reasons, virtualized storage
875 * may also report a 512-byte stripe size, or none at all.
877 sectorsize = pp->lg_sectorsize;
878 if ((stripesize = pp->lg_stripesize) == 0)
879 stripesize = sectorsize;
880 while (stripesize % 4096 != 0)
882 if ((offset = maxstart * sectorsize % stripesize) != 0) {
883 offset = (stripesize - offset) / sectorsize;
888 if (npartstart != NULL)
889 *npartstart = maxstart;
895 add_boot_partition(struct ggeom *geom, struct gprovider *pp,
896 const char *scheme, int interactive)
899 struct gprovider *ppi;
902 /* Check for existing freebsd-boot partition */
903 LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) {
904 struct partition_metadata *md;
905 const char *bootmount = NULL;
907 LIST_FOREACH(gc, &ppi->lg_config, lg_config)
908 if (strcmp(gc->lg_name, "type") == 0)
912 if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0)
916 * If the boot partition is not mountable and needs partcode,
917 * but doesn't have it, it doesn't satisfy our requirements.
919 md = get_part_metadata(ppi->lg_name, 0);
920 if (bootmount == NULL && (md == NULL || !md->bootcode))
923 /* If it is mountable, but mounted somewhere else, remount */
924 if (bootmount != NULL && md != NULL && md->fstab != NULL
925 && strlen(md->fstab->fs_file) > 0
926 && strcmp(md->fstab->fs_file, bootmount) != 0)
929 /* If it is mountable, but mountpoint is not set, mount it */
930 if (bootmount != NULL && md == NULL)
931 set_default_part_metadata(ppi->lg_name, scheme,
932 gc->lg_val, bootmount, NULL);
934 /* Looks good at this point, no added data needed */
939 choice = dialog_yesno("Boot Partition",
940 "This partition scheme requires a boot partition "
941 "for the disk to be bootable. Would you like to "
942 "make one now?", 0, 0);
946 if (choice == 0) { /* yes */
947 struct partition_metadata *md;
948 const char *bootmount = NULL;
949 char *bootpartname = NULL;
952 humanize_number(sizestr, 7,
953 bootpart_size(scheme), "B", HN_AUTOSCALE,
954 HN_NOSPACE | HN_DECIMAL);
956 gpart_create(pp, bootpart_type(scheme, &bootmount),
957 sizestr, bootmount, &bootpartname, 0);
959 if (bootpartname == NULL) /* Error reported to user already */
962 /* If the part is not mountable, make sure newfs isn't set */
963 if (bootmount == NULL) {
964 md = get_part_metadata(bootpartname, 0);
965 if (md != NULL && md->newfs != NULL) {
973 return (bootpart_size(scheme));
980 gpart_create(struct gprovider *pp, const char *default_type,
981 const char *default_size, const char *default_mountpoint,
982 char **partname, int interactive)
986 struct gconsumer *cp;
988 const char *errstr, *scheme;
989 char sizestr[32], startstr[32], output[64], *newpartname;
990 char newfs[255], options_fstype[64];
991 intmax_t maxsize, size, sector, firstfree, stripe;
993 int nitems, choice, junk;
996 DIALOG_FORMITEM items[] = {
997 {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0,
998 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
999 "freebsd-swap)", FALSE},
1000 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0,
1001 FALSE, "Partition size. Append K, M, G for kilobytes, "
1002 "megabytes or gigabytes.", FALSE},
1003 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
1004 FALSE, "Path at which to mount partition (blank for "
1005 "swap, / for root filesystem)", FALSE},
1006 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
1007 "Partition name. Not all partition schemes support this.",
1011 if (partname != NULL)
1014 /* Record sector and stripe sizes */
1015 sector = pp->lg_sectorsize;
1016 stripe = pp->lg_stripesize;
1019 * Find the PART geom we are manipulating. This may be a consumer of
1020 * this provider, or its parent. Check the consumer case first.
1023 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1024 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1029 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
1032 /* Now get the partition scheme */
1035 LIST_FOREACH(gc, &geom->lg_config, lg_config)
1036 if (strcmp(gc->lg_name, "scheme") == 0)
1037 scheme = gc->lg_val;
1040 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
1041 if (gpart_partition(pp->lg_name, NULL) == 0)
1043 "The partition table has been successfully created."
1044 " Please press Create again to create partitions.",
1051 * If we still don't have a geom, either the user has
1052 * canceled partitioning or there has been an error which has already
1053 * been displayed, so bail.
1058 maxsize = size = gpart_max_free(geom, &firstfree);
1060 dialog_msgbox("Error", "No free space left on device.", 0, 0,
1065 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
1066 HN_NOSPACE | HN_DECIMAL);
1067 items[1].text = sizestr;
1069 /* Special-case the MBR default type for nested partitions */
1070 if (strcmp(scheme, "MBR") == 0) {
1071 items[0].text = "freebsd";
1072 items[0].help = "Filesystem type (e.g. freebsd, fat32)";
1075 nitems = scheme_supports_labels(scheme) ? 4 : 3;
1077 if (default_type != NULL)
1078 items[0].text = (char *)default_type;
1079 if (default_size != NULL)
1080 items[1].text = (char *)default_size;
1081 if (default_mountpoint != NULL)
1082 items[2].text = (char *)default_mountpoint;
1084 /* Default options */
1085 strncpy(options_fstype, items[0].text,
1086 sizeof(options_fstype));
1087 newfs_command(options_fstype, newfs, 1);
1090 dialog_vars.extra_label = "Options";
1091 dialog_vars.extra_button = TRUE;
1092 choice = dlg_form("Add Partition", "", 0, 0, 0, nitems,
1094 dialog_vars.extra_button = FALSE;
1098 case 1: /* Cancel */
1100 case 3: /* Options */
1101 strncpy(options_fstype, items[0].text,
1102 sizeof(options_fstype));
1103 newfs_command(options_fstype, newfs, 0);
1109 * If the user changed the fs type after specifying options, undo
1110 * their choices in favor of the new filesystem's defaults.
1112 if (strcmp(options_fstype, items[0].text) != 0) {
1113 strncpy(options_fstype, items[0].text, sizeof(options_fstype));
1114 newfs_command(options_fstype, newfs, 1);
1118 if (strlen(items[1].text) > 0) {
1119 if (expand_number(items[1].text, &bytes) != 0) {
1122 sprintf(error, "Invalid size: %s\n", strerror(errno));
1123 dialog_msgbox("Error", error, 0, 0, TRUE);
1126 size = MIN((intmax_t)(bytes/sector), maxsize);
1129 /* Check if the label has a / in it */
1130 if (strchr(items[3].text, '/') != NULL) {
1131 dialog_msgbox("Error", "Label contains a /, which is not an "
1132 "allowed character.", 0, 0, TRUE);
1136 /* Warn if no mountpoint set */
1137 if (strcmp(items[0].text, "freebsd-ufs") == 0 &&
1138 items[2].text[0] != '/') {
1141 dialog_vars.defaultno = TRUE;
1142 choice = dialog_yesno("Warning",
1143 "This partition does not have a valid mountpoint "
1144 "(for the partition from which you intend to boot the "
1145 "operating system, the mountpoint should be /). Are you "
1146 "sure you want to continue?"
1148 dialog_vars.defaultno = FALSE;
1150 if (choice == 1) /* cancel */
1155 * Error if this scheme needs nested partitions, this is one, and
1156 * a mountpoint was set.
1158 if (strcmp(items[0].text, "freebsd") == 0 &&
1159 strlen(items[2].text) > 0) {
1160 dialog_msgbox("Error", "Partitions of type \"freebsd\" are "
1161 "nested BSD-type partition schemes and cannot have "
1162 "mountpoints. After creating one, select it and press "
1163 "Create again to add the actual file systems.", 0, 0, TRUE);
1167 /* If this is the root partition, check that this scheme is bootable */
1168 if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) {
1170 sprintf(message, "This partition scheme (%s) is not bootable "
1171 "on this platform. Are you sure you want to proceed?",
1173 dialog_vars.defaultno = TRUE;
1174 choice = dialog_yesno("Warning", message, 0, 0);
1175 dialog_vars.defaultno = FALSE;
1176 if (choice == 1) /* cancel */
1180 /* If this is the root partition, check that this fs is bootable */
1181 if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme,
1184 sprintf(message, "This file system (%s) is not bootable "
1185 "on this system. Are you sure you want to proceed?",
1187 dialog_vars.defaultno = TRUE;
1188 choice = dialog_yesno("Warning", message, 0, 0);
1189 dialog_vars.defaultno = FALSE;
1190 if (choice == 1) /* cancel */
1195 * If this is the root partition, and we need a boot partition, ask
1196 * the user to add one.
1199 if ((strcmp(items[0].text, "freebsd") == 0 ||
1200 strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0) {
1201 size_t bytes = add_boot_partition(geom, pp, scheme,
1204 /* Now adjust the part we are really adding forward */
1206 firstfree += bytes / sector;
1207 size -= (bytes + stripe)/sector;
1208 if (stripe > 0 && (firstfree*sector % stripe) != 0)
1209 firstfree += (stripe - ((firstfree*sector) %
1214 r = gctl_get_handle();
1215 gctl_ro_param(r, "class", -1, "PART");
1216 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1217 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1218 gctl_ro_param(r, "verb", -1, "add");
1220 gctl_ro_param(r, "type", -1, items[0].text);
1221 snprintf(sizestr, sizeof(sizestr), "%jd", size);
1222 gctl_ro_param(r, "size", -1, sizestr);
1223 snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1224 gctl_ro_param(r, "start", -1, startstr);
1225 if (items[3].text[0] != '\0')
1226 gctl_ro_param(r, "label", -1, items[3].text);
1227 gctl_rw_param(r, "output", sizeof(output), output);
1228 errstr = gctl_issue(r);
1229 if (errstr != NULL && errstr[0] != '\0') {
1230 gpart_show_error("Error", NULL, errstr);
1234 newpartname = strtok(output, " ");
1238 * Try to destroy any geom that gpart picked up already here from
1241 r = gctl_get_handle();
1242 gctl_ro_param(r, "class", -1, "PART");
1243 gctl_ro_param(r, "arg0", -1, newpartname);
1244 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1246 gctl_ro_param(r, "force", sizeof(junk), &junk);
1247 gctl_ro_param(r, "verb", -1, "destroy");
1248 gctl_issue(r); /* Error usually expected and non-fatal */
1252 if (strcmp(items[0].text, "freebsd") == 0)
1253 gpart_partition(newpartname, "BSD");
1255 set_default_part_metadata(newpartname, scheme,
1256 items[0].text, items[2].text, newfs);
1258 for (i = 0; i < nitems(items); i++)
1259 if (items[i].text_free)
1260 free(items[i].text);
1262 if (partname != NULL)
1263 *partname = strdup(newpartname);
1267 gpart_delete(struct gprovider *pp)
1271 struct gconsumer *cp;
1277 /* Is it a partition? */
1278 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
1280 /* Find out if this is the root of a gpart geom */
1282 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1283 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1288 /* If so, destroy all children */
1290 gpart_destroy(geom);
1292 /* If this is a partition, revert it, so it can be deleted */
1294 r = gctl_get_handle();
1295 gctl_ro_param(r, "class", -1, "PART");
1296 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1297 gctl_ro_param(r, "verb", -1, "undo");
1298 gctl_issue(r); /* Ignore non-fatal errors */
1304 * If this is not a partition, see if that is a problem, complain if
1305 * necessary, and return always, since we need not do anything further,
1308 if (!is_partition) {
1310 dialog_msgbox("Error",
1311 "Only partitions can be deleted.", 0, 0, TRUE);
1315 r = gctl_get_handle();
1316 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
1317 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
1318 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1319 gctl_ro_param(r, "verb", -1, "delete");
1321 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
1322 if (strcmp(gc->lg_name, "index") == 0) {
1323 idx = atoi(gc->lg_val);
1324 gctl_ro_param(r, "index", sizeof(idx), &idx);
1329 errstr = gctl_issue(r);
1330 if (errstr != NULL && errstr[0] != '\0') {
1331 gpart_show_error("Error", NULL, errstr);
1338 delete_part_metadata(pp->lg_name);
1342 gpart_revert_all(struct gmesh *mesh)
1344 struct gclass *classp;
1348 const char *modified;
1350 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1351 if (strcmp(classp->lg_name, "PART") == 0)
1355 if (strcmp(classp->lg_name, "PART") != 0) {
1356 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1360 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1361 modified = "true"; /* XXX: If we don't know (kernel too old),
1362 * assume there are modifications. */
1363 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1364 if (strcmp(gc->lg_name, "modified") == 0) {
1365 modified = gc->lg_val;
1370 if (strcmp(modified, "false") == 0)
1373 r = gctl_get_handle();
1374 gctl_ro_param(r, "class", -1, "PART");
1375 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1376 gctl_ro_param(r, "verb", -1, "undo");
1384 gpart_commit(struct gmesh *mesh)
1386 struct partition_metadata *md;
1387 struct gclass *classp;
1390 struct gconsumer *cp;
1391 struct gprovider *pp;
1394 const char *modified;
1397 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1398 if (strcmp(classp->lg_name, "PART") == 0)
1402 /* Figure out what filesystem / uses */
1403 rootfs = "ufs"; /* Assume ufs if nothing else present */
1404 TAILQ_FOREACH(md, &part_metadata, metadata) {
1405 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) {
1406 rootfs = md->fstab->fs_vfstype;
1411 if (strcmp(classp->lg_name, "PART") != 0) {
1412 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1416 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1417 modified = "true"; /* XXX: If we don't know (kernel too old),
1418 * assume there are modifications. */
1419 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1420 if (strcmp(gc->lg_name, "modified") == 0) {
1421 modified = gc->lg_val;
1426 if (strcmp(modified, "false") == 0)
1429 /* Add bootcode if necessary, before the commit */
1430 md = get_part_metadata(gp->lg_name, 0);
1431 if (md != NULL && md->bootcode)
1434 /* Now install partcode on its partitions, if necessary */
1435 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1436 md = get_part_metadata(pp->lg_name, 0);
1437 if (md == NULL || !md->bootcode)
1440 /* Mark this partition active if that's required */
1443 /* Check if the partition has sub-partitions */
1444 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1445 if (strcmp(cp->lg_geom->lg_class->lg_name,
1449 if (cp == NULL) /* No sub-partitions */
1450 gpart_partcode(pp, rootfs);
1453 r = gctl_get_handle();
1454 gctl_ro_param(r, "class", -1, "PART");
1455 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1456 gctl_ro_param(r, "verb", -1, "commit");
1458 errstr = gctl_issue(r);
1459 if (errstr != NULL && errstr[0] != '\0')
1460 gpart_show_error("Error", NULL, errstr);