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>
34 #include <bsddialog.h>
49 #define GPART_FLAGS "x" /* Do not commit changes by default */
52 gpart_show_error(const char *title, const char *explanation, const char *errstr)
57 struct bsddialog_conf conf;
59 if (explanation == NULL)
62 error = strtol(errstr, &errmsg, 0);
63 if (errmsg != errstr) {
64 while (errmsg[0] == ' ')
66 if (errmsg[0] != '\0')
67 sprintf(message, "%s%s. %s", explanation,
68 strerror(error), errmsg);
70 sprintf(message, "%s%s", explanation, strerror(error));
72 sprintf(message, "%s%s", explanation, errmsg);
75 bsddialog_initconf(&conf);
77 bsddialog_msgbox(&conf, message, 0, 0);
81 scheme_supports_labels(const char *scheme)
83 if (strcmp(scheme, "APM") == 0)
85 if (strcmp(scheme, "GPT") == 0)
92 newfs_command(const char *fstype, char *command, int use_default)
94 struct bsddialog_conf conf;
96 bsddialog_initconf(&conf);
98 if (strcmp(fstype, "freebsd-ufs") == 0) {
100 struct bsddialog_menuitem items[] = {
101 {"", false, 0, "UFS1", "UFS Version 1",
102 "Use version 1 of the UFS file system instead "
103 "of version 2 (not recommended)"},
104 {"", true, 0, "SU", "Softupdates",
105 "Enable softupdates (default)"},
106 {"", true, 0, "SUJ", "Softupdates journaling",
107 "Enable file system journaling (default - "
108 "turn off for SSDs)"},
109 {"", false, 0, "TRIM", "Enable SSD TRIM support",
110 "Enable TRIM support, useful on solid-state "
116 conf.title = "UFS Options";
117 choice = bsddialog_checklist(&conf, "", 0, 0, 0,
118 nitems(items), items, NULL);
119 if (choice == BSDDIALOG_CANCEL)
123 strcpy(command, "newfs ");
124 for (i = 0; i < (int)nitems(items); i++) {
125 if (items[i].on == false)
127 if (strcmp(items[i].name, "UFS1") == 0)
128 strcat(command, "-O1 ");
129 else if (strcmp(items[i].name, "SU") == 0)
130 strcat(command, "-U ");
131 else if (strcmp(items[i].name, "SUJ") == 0)
132 strcat(command, "-j ");
133 else if (strcmp(items[i].name, "TRIM") == 0)
134 strcat(command, "-t ");
136 } else if (strcmp(fstype, "freebsd-zfs") == 0) {
138 struct bsddialog_menuitem items[] = {
139 {"", 0, true, "fletcher4", "checksum algorithm: fletcher4",
140 "Use fletcher4 for data integrity checking. "
142 {"", 0, false, "fletcher2", "checksum algorithm: fletcher2",
143 "Use fletcher2 for data integrity checking. "
144 "(not recommended)"},
145 {"", 0, false, "sha256", "checksum algorithm: sha256",
146 "Use sha256 for data integrity checking. "
147 "(not recommended)"},
148 {"", 0, false, "atime", "Update atimes for files",
149 "Disable atime update"},
154 conf.title = "ZFS Options";
155 choice = bsddialog_checklist(&conf, "", 0, 0, 0,
156 nitems(items), items, NULL);
157 if (choice == BSDDIALOG_CANCEL)
161 strcpy(command, "zpool create -f -m none ");
162 if (getenv("BSDINSTALL_TMPBOOT") != NULL) {
163 char zfsboot_path[MAXPATHLEN];
164 snprintf(zfsboot_path, sizeof(zfsboot_path), "%s/zfs",
165 getenv("BSDINSTALL_TMPBOOT"));
166 mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP |
168 sprintf(command, "%s -o cachefile=%s/zpool.cache ",
169 command, zfsboot_path);
171 for (i = 0; i < (int)nitems(items); i++) {
172 if (items[i].on == false)
174 if (strcmp(items[i].name, "fletcher4") == 0)
175 strcat(command, "-O checksum=fletcher4 ");
176 else if (strcmp(items[i].name, "fletcher2") == 0)
177 strcat(command, "-O checksum=fletcher2 ");
178 else if (strcmp(items[i].name, "sha256") == 0)
179 strcat(command, "-O checksum=sha256 ");
180 else if (strcmp(items[i].name, "atime") == 0)
181 strcat(command, "-O atime=off ");
183 } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0 ||
184 strcmp(fstype, "ms-basic-data") == 0) {
186 struct bsddialog_menuitem items[] = {
187 {"", 0, true, "FAT32", "FAT Type 32",
188 "Create a FAT32 filesystem (default)"},
189 {"", 0, false, "FAT16", "FAT Type 16",
190 "Create a FAT16 filesystem"},
191 {"", 0, false, "FAT12", "FAT Type 12",
192 "Create a FAT12 filesystem"},
197 conf.title = "FAT Options";
198 choice = bsddialog_radiolist(&conf, "", 0, 0, 0,
199 nitems(items), items, NULL);
200 if (choice == BSDDIALOG_CANCEL)
204 strcpy(command, "newfs_msdos ");
205 for (i = 0; i < (int)nitems(items); i++) {
206 if (items[i].on == false)
208 if (strcmp(items[i].name, "FAT32") == 0)
209 strcat(command, "-F 32 -c 1");
210 else if (strcmp(items[i].name, "FAT16") == 0)
211 strcat(command, "-F 16 ");
212 else if (strcmp(items[i].name, "FAT12") == 0)
213 strcat(command, "-F 12 ");
217 conf.title = "Error";
218 bsddialog_msgbox(&conf, "No configurable options exist "
219 "for this filesystem.", 0, 0);
226 choose_part_type(const char *def_scheme)
228 int button, choice, i;
229 const char *scheme = NULL;
230 struct bsddialog_conf conf;
232 struct bsddialog_menuitem items[] = {
233 {"", false, 0, "APM", "Apple Partition Map",
234 "Bootable on PowerPC Apple Hardware" },
235 {"", false, 0, "BSD", "BSD Labels",
236 "Bootable on most x86 systems" },
237 {"", false, 0, "GPT", "GUID Partition Table",
238 "Bootable on most x86 systems and EFI aware ARM64" },
239 {"", false, 0, "MBR", "DOS Partitions",
240 "Bootable on most x86 systems" },
243 for (i = 0; i < (int)nitems(items); i++)
244 if (strcmp(items[i].name, def_scheme) == 0)
247 bsddialog_initconf(&conf);
250 conf.title = "Partition Scheme";
251 button = bsddialog_menu(&conf,
252 "Select a partition scheme for this volume:", 0, 0, 0,
253 nitems(items), items, &choice);
255 if (button == BSDDIALOG_CANCEL)
258 if (!is_scheme_bootable(items[choice].name)) {
260 sprintf(message, "This partition scheme (%s) is not "
261 "bootable on this platform. Are you sure you want "
262 "to proceed?", items[choice].name);
263 conf.button.default_cancel = true;
264 conf.title = "Warning";
265 button = bsddialog_yesno(&conf, message, 0, 0);
266 conf.button.default_cancel = false;
267 if (button == BSDDIALOG_NO)
271 scheme = items[choice].name;
277 gpart_partition(const char *lg_name, const char *scheme)
282 struct bsddialog_conf conf;
284 bsddialog_initconf(&conf);
287 if (scheme == NULL) {
288 scheme = choose_part_type(default_scheme());
293 if (!is_scheme_bootable(scheme)) {
295 sprintf(message, "This partition scheme (%s) is not "
296 "bootable on this platform. Are you sure you want "
297 "to proceed?", scheme);
298 conf.button.default_cancel = true;
299 conf.title = "Warning";
300 button = bsddialog_yesno(&conf, message, 0, 0);
301 conf.button.default_cancel = false;
302 if (button == BSDDIALOG_NO) {
303 /* Reset scheme so user can choose another */
310 r = gctl_get_handle();
311 gctl_ro_param(r, "class", -1, "PART");
312 gctl_ro_param(r, "arg0", -1, lg_name);
313 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
314 gctl_ro_param(r, "scheme", -1, scheme);
315 gctl_ro_param(r, "verb", -1, "create");
317 errstr = gctl_issue(r);
318 if (errstr != NULL && errstr[0] != '\0') {
319 gpart_show_error("Error", NULL, errstr);
326 if (bootcode_path(scheme) != NULL)
327 get_part_metadata(lg_name, 1)->bootcode = 1;
332 gpart_activate(struct gprovider *pp)
336 const char *errstr, *scheme;
337 const char *attribute = NULL;
341 * Some partition schemes need this partition to be marked 'active'
342 * for it to be bootable.
344 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
345 if (strcmp(gc->lg_name, "scheme") == 0) {
351 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0)
352 attribute = "active";
356 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
357 if (strcmp(gc->lg_name, "index") == 0) {
358 idx = atoi(gc->lg_val);
363 r = gctl_get_handle();
364 gctl_ro_param(r, "class", -1, "PART");
365 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
366 gctl_ro_param(r, "verb", -1, "set");
367 gctl_ro_param(r, "attrib", -1, attribute);
368 gctl_ro_param(r, "index", sizeof(idx), &idx);
370 errstr = gctl_issue(r);
371 if (errstr != NULL && errstr[0] != '\0')
372 gpart_show_error("Error", "Error marking partition active:",
378 gpart_set_root(const char *lg_name, const char *attribute)
383 r = gctl_get_handle();
384 gctl_ro_param(r, "class", -1, "PART");
385 gctl_ro_param(r, "arg0", -1, lg_name);
386 gctl_ro_param(r, "flags", -1, "C");
387 gctl_ro_param(r, "verb", -1, "set");
388 gctl_ro_param(r, "attrib", -1, attribute);
390 errstr = gctl_issue(r);
391 if (errstr != NULL && errstr[0] != '\0')
392 gpart_show_error("Error", "Error setting parameter on disk:",
398 gpart_bootcode(struct ggeom *gp)
400 const char *bootcode;
403 const char *errstr, *scheme;
405 size_t bootsize, bytes;
407 struct bsddialog_conf conf;
410 * Write default bootcode to the newly partitioned disk, if that
411 * applies on this platform.
413 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
414 if (strcmp(gc->lg_name, "scheme") == 0) {
420 bootcode = bootcode_path(scheme);
421 if (bootcode == NULL)
424 bootfd = open(bootcode, O_RDONLY);
426 bsddialog_initconf(&conf);
427 conf.title = "Bootcode Error";
428 bsddialog_msgbox(&conf, strerror(errno), 0, 0);
432 bootsize = lseek(bootfd, 0, SEEK_END);
433 boot = malloc(bootsize);
434 lseek(bootfd, 0, SEEK_SET);
436 while (bytes < bootsize)
437 bytes += read(bootfd, boot + bytes, bootsize - bytes);
440 r = gctl_get_handle();
441 gctl_ro_param(r, "class", -1, "PART");
442 gctl_ro_param(r, "arg0", -1, gp->lg_name);
443 gctl_ro_param(r, "verb", -1, "bootcode");
444 gctl_ro_param(r, "bootcode", bootsize, boot);
446 errstr = gctl_issue(r);
447 if (errstr != NULL && errstr[0] != '\0')
448 gpart_show_error("Bootcode Error", NULL, errstr);
454 gpart_partcode(struct gprovider *pp, const char *fstype)
458 const char *indexstr;
459 char message[255], command[255];
460 struct bsddialog_conf conf;
462 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
463 if (strcmp(gc->lg_name, "scheme") == 0) {
469 /* Make sure this partition scheme needs partcode on this platform */
470 if (partcode_path(scheme, fstype) == NULL)
473 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
474 if (strcmp(gc->lg_name, "index") == 0) {
475 indexstr = gc->lg_val;
480 /* Shell out to gpart for partcode for now */
481 sprintf(command, "gpart bootcode -p %s -i %s %s",
482 partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name);
483 if (system(command) != 0) {
484 sprintf(message, "Error installing partcode on partition %s",
486 bsddialog_initconf(&conf);
487 conf.title = "Error";
488 bsddialog_msgbox(&conf, message, 0, 0);
493 gpart_destroy(struct ggeom *lg_geom)
496 struct gprovider *pp;
500 /* Delete all child metadata */
501 LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider)
504 /* Revert any local changes to get this geom into a pristine state */
505 r = gctl_get_handle();
506 gctl_ro_param(r, "class", -1, "PART");
507 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
508 gctl_ro_param(r, "verb", -1, "undo");
509 gctl_issue(r); /* Ignore errors -- these are non-fatal */
512 /* Now destroy the geom itself */
513 r = gctl_get_handle();
514 gctl_ro_param(r, "class", -1, "PART");
515 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
516 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
517 gctl_ro_param(r, "force", sizeof(force), &force);
518 gctl_ro_param(r, "verb", -1, "destroy");
519 errstr = gctl_issue(r);
520 if (errstr != NULL && errstr[0] != '\0') {
522 * Check if we reverted away the existence of the geom
523 * altogether. Show all other errors to the user.
525 if (strtol(errstr, NULL, 0) != EINVAL)
526 gpart_show_error("Error", NULL, errstr);
530 /* And any metadata associated with the partition scheme itself */
531 delete_part_metadata(lg_geom->lg_name);
535 gpart_edit(struct gprovider *pp)
539 struct gconsumer *cp;
541 const char *errstr, *oldtype, *scheme;
542 struct partition_metadata *md;
546 int hadlabel, choice, nitems;
548 struct bsddialog_conf conf;
550 struct bsddialog_formitem items[] = {
551 { "Type:", 1, 1, "", 1, 12, 12, 15, NULL, 0,
552 "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
554 { "Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0,
555 "Partition size. Append K, M, G for kilobytes, "
556 "megabytes or gigabytes."},
557 { "Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0,
558 "Path at which to mount this partition (leave blank "
559 "for swap, set to / for root filesystem)"},
560 { "Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0,
561 "Partition name. Not all partition schemes support this."},
564 bsddialog_initconf(&conf);
567 * Find the PART geom we are manipulating. This may be a consumer of
568 * this provider, or its parent. Check the consumer case first.
571 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
572 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
573 /* Check for zombie geoms, treating them as blank */
575 LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) {
576 if (strcmp(gc->lg_name, "scheme") == 0) {
581 if (scheme == NULL || strcmp(scheme, "(none)") == 0) {
582 gpart_partition(cp->lg_geom->lg_name, NULL);
586 /* If this is a nested partition, edit as usual */
587 if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
590 /* Destroy the geom and all sub-partitions */
591 gpart_destroy(cp->lg_geom);
593 /* Now re-partition and return */
594 gpart_partition(cp->lg_geom->lg_name, NULL);
598 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
602 /* Disk not partitioned, so partition it */
603 gpart_partition(pp->lg_name, NULL);
607 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
608 if (strcmp(gc->lg_name, "scheme") == 0) {
614 nitems = scheme_supports_labels(scheme) ? 4 : 3;
616 /* Edit editable parameters of a partition */
618 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
619 if (strcmp(gc->lg_name, "type") == 0) {
620 oldtype = gc->lg_val;
621 items[0].init = gc->lg_val;
623 if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) {
625 items[3].init = gc->lg_val;
627 if (strcmp(gc->lg_name, "index") == 0)
628 idx = atoi(gc->lg_val);
631 TAILQ_FOREACH(md, &part_metadata, metadata) {
632 if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) {
633 if (md->fstab != NULL)
634 items[2].init = md->fstab->fs_file;
639 humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE,
640 HN_NOSPACE | HN_DECIMAL);
641 items[1].init = sizestr;
644 conf.form.value_without_ok = true;
645 conf.title = "Edit Partition";
646 choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items);
648 if (choice == BSDDIALOG_CANCEL)
651 /* If this is the root partition, check that this fs is bootable */
652 if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme,
655 sprintf(message, "This file system (%s) is not bootable "
656 "on this system. Are you sure you want to proceed?",
658 conf.button.default_cancel = true;
659 conf.title = "Warning";
660 choice = bsddialog_yesno(&conf, message, 0, 0);
661 conf.button.default_cancel = false;
662 if (choice == BSDDIALOG_CANCEL)
666 /* Check if the label has a / in it */
667 if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) {
668 conf.title = "Error";
669 bsddialog_msgbox(&conf, "Label contains a /, which is not an "
670 "allowed character.", 0, 0);
674 r = gctl_get_handle();
675 gctl_ro_param(r, "class", -1, "PART");
676 gctl_ro_param(r, "arg0", -1, geom->lg_name);
677 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
678 gctl_ro_param(r, "verb", -1, "modify");
679 gctl_ro_param(r, "index", sizeof(idx), &idx);
680 if (items[3].value != NULL && (hadlabel || items[3].value[0] != '\0'))
681 gctl_ro_param(r, "label", -1, items[3].value);
682 gctl_ro_param(r, "type", -1, items[0].value);
683 errstr = gctl_issue(r);
684 if (errstr != NULL && errstr[0] != '\0') {
685 gpart_show_error("Error", NULL, errstr);
691 newfs_command(items[0].value, newfs, 1);
692 set_default_part_metadata(pp->lg_name, scheme, items[0].value,
693 items[2].value, (strcmp(oldtype, items[0].value) != 0) ?
697 if (strcmp(oldtype, items[0].value) != 0 && cp != NULL)
698 gpart_destroy(cp->lg_geom);
699 if (strcmp(oldtype, items[0].value) != 0 && strcmp(items[0].value,
701 gpart_partition(pp->lg_name, "BSD");
703 for (i = 0; i < nitems(items); i++)
704 if (items[i].value != NULL)
705 free(items[i].value);
709 set_default_part_metadata(const char *name, const char *scheme,
710 const char *type, const char *mountpoint, const char *newfs)
712 struct partition_metadata *md;
713 char *zpool_name = NULL;
714 const char *default_bootmount = NULL;
717 /* Set part metadata */
718 md = get_part_metadata(name, 1);
721 if (md->newfs != NULL) {
726 if (newfs != NULL && newfs[0] != '\0') {
727 md->newfs = malloc(strlen(newfs) + strlen(" /dev/") +
728 strlen(mountpoint) + 5 + strlen(name) + 1);
729 if (strcmp("freebsd-zfs", type) == 0) {
730 zpool_name = strdup((strlen(mountpoint) == 1) ?
731 "root" : &mountpoint[1]);
732 for (i = 0; zpool_name[i] != 0; i++)
733 if (!isalnum(zpool_name[i]))
735 sprintf(md->newfs, "%s %s /dev/%s", newfs,
738 sprintf(md->newfs, "%s /dev/%s", newfs, name);
743 if (strcmp(type, "freebsd-swap") == 0)
745 if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) {
746 if (default_bootmount == NULL)
748 else if (mountpoint == NULL || strlen(mountpoint) == 0)
749 mountpoint = default_bootmount;
752 if (mountpoint == NULL || mountpoint[0] == '\0') {
753 if (md->fstab != NULL) {
754 free(md->fstab->fs_spec);
755 free(md->fstab->fs_file);
756 free(md->fstab->fs_vfstype);
757 free(md->fstab->fs_mntops);
758 free(md->fstab->fs_type);
763 if (md->fstab == NULL) {
764 md->fstab = malloc(sizeof(struct fstab));
766 free(md->fstab->fs_spec);
767 free(md->fstab->fs_file);
768 free(md->fstab->fs_vfstype);
769 free(md->fstab->fs_mntops);
770 free(md->fstab->fs_type);
772 if (strcmp("freebsd-zfs", type) == 0) {
773 md->fstab->fs_spec = strdup(zpool_name);
775 md->fstab->fs_spec = malloc(strlen(name) +
776 strlen("/dev/") + 1);
777 sprintf(md->fstab->fs_spec, "/dev/%s", name);
779 md->fstab->fs_file = strdup(mountpoint);
780 /* Get VFS from text after freebsd-, if possible */
781 if (strncmp("freebsd-", type, 8) == 0)
782 md->fstab->fs_vfstype = strdup(&type[8]);
783 else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0
784 || strcmp("ms-basic-data", type) == 0)
785 md->fstab->fs_vfstype = strdup("msdosfs");
787 md->fstab->fs_vfstype = strdup(type); /* Guess */
788 if (strcmp(type, "freebsd-swap") == 0) {
789 md->fstab->fs_type = strdup(FSTAB_SW);
790 md->fstab->fs_freq = 0;
791 md->fstab->fs_passno = 0;
792 } else if (strcmp(type, "freebsd-zfs") == 0) {
793 md->fstab->fs_type = strdup(FSTAB_RW);
794 md->fstab->fs_freq = 0;
795 md->fstab->fs_passno = 0;
797 md->fstab->fs_type = strdup(FSTAB_RW);
798 if (strcmp(mountpoint, "/") == 0) {
799 md->fstab->fs_freq = 1;
800 md->fstab->fs_passno = 1;
802 md->fstab->fs_freq = 2;
803 md->fstab->fs_passno = 2;
806 md->fstab->fs_mntops = strdup(md->fstab->fs_type);
809 if (zpool_name != NULL)
814 int part_compare(const void *xa, const void *xb)
816 struct gprovider **a = (struct gprovider **)xa;
817 struct gprovider **b = (struct gprovider **)xb;
818 intmax_t astart, bstart;
822 LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
823 if (strcmp(gc->lg_name, "start") == 0) {
824 astart = strtoimax(gc->lg_val, NULL, 0);
827 LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
828 if (strcmp(gc->lg_name, "start") == 0) {
829 bstart = strtoimax(gc->lg_val, NULL, 0);
835 else if (astart > bstart)
842 gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
845 struct gprovider *pp, **providers;
846 intmax_t sectorsize, stripesize, offset;
849 intmax_t maxsize, maxstart;
850 intmax_t partstart, partend;
853 /* Now get the maximum free size and free start */
855 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
856 if (strcmp(gc->lg_name, "first") == 0)
857 start = strtoimax(gc->lg_val, NULL, 0);
858 if (strcmp(gc->lg_name, "last") == 0)
859 end = strtoimax(gc->lg_val, NULL, 0);
863 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
865 providers = calloc(nparts, sizeof(providers[0]));
866 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
868 qsort(providers, nparts, sizeof(providers[0]), part_compare);
872 for (i = 0; i < nparts; i++) {
875 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
876 if (strcmp(gc->lg_name, "start") == 0)
877 partstart = strtoimax(gc->lg_val, NULL, 0);
878 if (strcmp(gc->lg_name, "end") == 0)
879 partend = strtoimax(gc->lg_val, NULL, 0);
882 if (partstart - lastend > maxsize) {
883 maxsize = partstart - lastend - 1;
884 maxstart = lastend + 1;
890 if (end - lastend > maxsize) {
891 maxsize = end - lastend;
892 maxstart = lastend + 1;
895 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
898 * Round the start and size of the largest available space up to
899 * the nearest multiple of the adjusted stripe size.
901 * The adjusted stripe size is the least common multiple of the
902 * actual stripe size, or the sector size if no stripe size was
903 * reported, and 4096. The reason for this is that contemporary
904 * disks often have 4096-byte physical sectors but report 512
905 * bytes instead for compatibility with older / broken operating
906 * systems and BIOSes. For the same reasons, virtualized storage
907 * may also report a 512-byte stripe size, or none at all.
909 sectorsize = pp->lg_sectorsize;
910 if ((stripesize = pp->lg_stripesize) == 0)
911 stripesize = sectorsize;
912 while (stripesize % 4096 != 0)
914 if ((offset = maxstart * sectorsize % stripesize) != 0) {
915 offset = (stripesize - offset) / sectorsize;
920 if (npartstart != NULL)
921 *npartstart = maxstart;
927 add_boot_partition(struct ggeom *geom, struct gprovider *pp,
928 const char *scheme, int interactive)
931 struct gprovider *ppi;
933 struct bsddialog_conf conf;
935 /* Check for existing freebsd-boot partition */
936 LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) {
937 struct partition_metadata *md;
938 const char *bootmount = NULL;
940 LIST_FOREACH(gc, &ppi->lg_config, lg_config)
941 if (strcmp(gc->lg_name, "type") == 0)
945 if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0)
949 * If the boot partition is not mountable and needs partcode,
950 * but doesn't have it, it doesn't satisfy our requirements.
952 md = get_part_metadata(ppi->lg_name, 0);
953 if (bootmount == NULL && (md == NULL || !md->bootcode))
956 /* If it is mountable, but mounted somewhere else, remount */
957 if (bootmount != NULL && md != NULL && md->fstab != NULL
958 && strlen(md->fstab->fs_file) > 0
959 && strcmp(md->fstab->fs_file, bootmount) != 0)
962 /* If it is mountable, but mountpoint is not set, mount it */
963 if (bootmount != NULL && md == NULL)
964 set_default_part_metadata(ppi->lg_name, scheme,
965 gc->lg_val, bootmount, NULL);
967 /* Looks good at this point, no added data needed */
972 bsddialog_initconf(&conf);
973 conf.title = "Boot Partition";
974 choice = bsddialog_yesno(&conf,
975 "This partition scheme requires a boot partition "
976 "for the disk to be bootable. Would you like to "
977 "make one now?", 0, 0);
979 choice = BSDDIALOG_YES;
982 if (choice == BSDDIALOG_YES) {
983 struct partition_metadata *md;
984 const char *bootmount = NULL;
985 char *bootpartname = NULL;
988 humanize_number(sizestr, 7,
989 bootpart_size(scheme), "B", HN_AUTOSCALE,
990 HN_NOSPACE | HN_DECIMAL);
992 gpart_create(pp, bootpart_type(scheme, &bootmount),
993 sizestr, bootmount, &bootpartname, 0);
995 if (bootpartname == NULL) /* Error reported to user already */
998 /* If the part is not mountable, make sure newfs isn't set */
999 if (bootmount == NULL) {
1000 md = get_part_metadata(bootpartname, 0);
1001 if (md != NULL && md->newfs != NULL) {
1009 return (bootpart_size(scheme));
1016 gpart_create(struct gprovider *pp, const char *default_type,
1017 const char *default_size, const char *default_mountpoint,
1018 char **partname, int interactive)
1022 struct gconsumer *cp;
1024 const char *errstr, *scheme;
1025 char sizestr[32], startstr[32], output[64], *newpartname;
1026 char newfs[255], options_fstype[64];
1027 intmax_t maxsize, size, sector, firstfree, stripe;
1029 int nitems, choice, junk;
1031 bool init_allocated;
1032 struct bsddialog_conf conf;
1034 struct bsddialog_formitem items[] = {
1035 {"Type:", 1, 1, "freebsd-ufs", 1, 12, 12, 15, NULL, 0,
1036 "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
1038 {"Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0,
1039 "Partition size. Append K, M, G for kilobytes, "
1040 "megabytes or gigabytes."},
1041 {"Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0,
1042 "Path at which to mount partition (blank for "
1043 "swap, / for root filesystem)"},
1044 {"Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0,
1045 "Partition name. Not all partition schemes support this."},
1048 bsddialog_initconf(&conf);
1050 if (partname != NULL)
1053 /* Record sector and stripe sizes */
1054 sector = pp->lg_sectorsize;
1055 stripe = pp->lg_stripesize;
1058 * Find the PART geom we are manipulating. This may be a consumer of
1059 * this provider, or its parent. Check the consumer case first.
1062 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1063 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1068 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
1071 /* Now get the partition scheme */
1074 LIST_FOREACH(gc, &geom->lg_config, lg_config)
1075 if (strcmp(gc->lg_name, "scheme") == 0)
1076 scheme = gc->lg_val;
1079 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
1080 if (gpart_partition(pp->lg_name, NULL) == 0) {
1081 bsddialog_msgbox(&conf,
1082 "The partition table has been successfully created."
1083 " Please press Create again to create partitions.",
1091 * If we still don't have a geom, either the user has
1092 * canceled partitioning or there has been an error which has already
1093 * been displayed, so bail.
1098 maxsize = size = gpart_max_free(geom, &firstfree);
1100 conf .title = "Error";
1101 bsddialog_msgbox(&conf, "No free space left on device.", 0, 0);
1105 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
1106 HN_NOSPACE | HN_DECIMAL);
1107 items[1].init = sizestr;
1109 /* Special-case the MBR default type for nested partitions */
1110 if (strcmp(scheme, "MBR") == 0) {
1111 items[0].init = "freebsd";
1112 items[0].bottomdesc = "Filesystem type (e.g. freebsd, fat32)";
1115 nitems = scheme_supports_labels(scheme) ? 4 : 3;
1117 if (default_type != NULL)
1118 items[0].init = (char *)default_type;
1119 if (default_size != NULL)
1120 items[1].init = (char *)default_size;
1121 if (default_mountpoint != NULL)
1122 items[2].init = (char *)default_mountpoint;
1124 /* Default options */
1125 strncpy(options_fstype, items[0].init,
1126 sizeof(options_fstype));
1127 newfs_command(options_fstype, newfs, 1);
1129 init_allocated = false;
1132 conf.button.with_extra = true;
1133 conf.button.extra_label = "Options";
1134 conf.form.value_without_ok = true;
1135 conf.title = "Add Partition";
1136 choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items);
1137 conf.button.with_extra = false;
1138 conf.button.extra_label = NULL;
1139 conf.form.value_without_ok = false;
1143 case BSDDIALOG_CANCEL:
1145 case BSDDIALOG_EXTRA: /* Options */
1146 strncpy(options_fstype, items[0].value,
1147 sizeof(options_fstype));
1148 newfs_command(options_fstype, newfs, 0);
1149 for (i = 0; i < nitems(items); i++) {
1151 free((char*)items[i].init);
1152 items[i].init = items[i].value;
1154 init_allocated = true;
1157 } else { /* auto partitioning */
1158 items[0].value = strdup(items[0].init);
1159 items[1].value = strdup(items[1].init);
1160 items[2].value = strdup(items[2].init);
1162 items[3].value = strdup(items[3].init);
1166 * If the user changed the fs type after specifying options, undo
1167 * their choices in favor of the new filesystem's defaults.
1169 if (strcmp(options_fstype, items[0].value) != 0) {
1170 strncpy(options_fstype, items[0].value, sizeof(options_fstype));
1171 newfs_command(options_fstype, newfs, 1);
1175 if (strlen(items[1].value) > 0) {
1176 if (expand_number(items[1].value, &bytes) != 0) {
1179 sprintf(error, "Invalid size: %s\n", strerror(errno));
1180 conf.title = "Error";
1181 bsddialog_msgbox(&conf, error, 0, 0);
1184 size = MIN((intmax_t)(bytes/sector), maxsize);
1187 /* Check if the label has a / in it */
1188 if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) {
1189 conf.title = "Error";
1190 bsddialog_msgbox(&conf, "Label contains a /, which is not an "
1191 "allowed character.", 0, 0);
1195 /* Warn if no mountpoint set */
1196 if (strcmp(items[0].value, "freebsd-ufs") == 0 &&
1197 items[2].value[0] != '/') {
1200 conf.button.default_cancel = true;
1201 conf.title = "Warning";
1202 choice = bsddialog_yesno(&conf,
1203 "This partition does not have a valid mountpoint "
1204 "(for the partition from which you intend to boot the "
1205 "operating system, the mountpoint should be /). Are you "
1206 "sure you want to continue?"
1208 conf.button.default_cancel = false;
1210 if (choice == BSDDIALOG_CANCEL)
1215 * Error if this scheme needs nested partitions, this is one, and
1216 * a mountpoint was set.
1218 if (strcmp(items[0].value, "freebsd") == 0 &&
1219 strlen(items[2].value) > 0) {
1220 conf.title = "Error";
1221 bsddialog_msgbox(&conf, "Partitions of type \"freebsd\" are "
1222 "nested BSD-type partition schemes and cannot have "
1223 "mountpoints. After creating one, select it and press "
1224 "Create again to add the actual file systems.", 0, 0);
1228 /* If this is the root partition, check that this scheme is bootable */
1229 if (strcmp(items[2].value, "/") == 0 && !is_scheme_bootable(scheme)) {
1231 sprintf(message, "This partition scheme (%s) is not bootable "
1232 "on this platform. Are you sure you want to proceed?",
1234 conf.button.default_cancel = true;
1235 conf.title = "Warning";
1236 choice = bsddialog_yesno(&conf, message, 0, 0);
1237 conf.button.default_cancel = false;
1238 if (choice == BSDDIALOG_CANCEL)
1242 /* If this is the root partition, check that this fs is bootable */
1243 if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme,
1246 sprintf(message, "This file system (%s) is not bootable "
1247 "on this system. Are you sure you want to proceed?",
1249 conf.button.default_cancel = true;
1250 conf.title = "Warning";
1251 choice = bsddialog_yesno(&conf, message, 0, 0);
1252 conf.button.default_cancel = false;
1253 if (choice == BSDDIALOG_CANCEL)
1258 * If this is the root partition, and we need a boot partition, ask
1259 * the user to add one.
1262 if ((strcmp(items[0].value, "freebsd") == 0 ||
1263 strcmp(items[2].value, "/") == 0) && bootpart_size(scheme) > 0) {
1264 size_t bytes = add_boot_partition(geom, pp, scheme,
1267 /* Now adjust the part we are really adding forward */
1269 firstfree += bytes / sector;
1270 size -= (bytes + stripe)/sector;
1271 if (stripe > 0 && (firstfree*sector % stripe) != 0)
1272 firstfree += (stripe - ((firstfree*sector) %
1279 r = gctl_get_handle();
1280 gctl_ro_param(r, "class", -1, "PART");
1281 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1282 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1283 gctl_ro_param(r, "verb", -1, "add");
1285 gctl_ro_param(r, "type", -1, items[0].value);
1286 snprintf(sizestr, sizeof(sizestr), "%jd", size);
1287 gctl_ro_param(r, "size", -1, sizestr);
1288 snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1289 gctl_ro_param(r, "start", -1, startstr);
1290 if (items[3].value != NULL && items[3].value[0] != '\0')
1291 gctl_ro_param(r, "label", -1, items[3].value);
1292 gctl_add_param(r, "output", sizeof(output), output,
1293 GCTL_PARAM_WR | GCTL_PARAM_ASCII);
1294 errstr = gctl_issue(r);
1295 if (errstr != NULL && errstr[0] != '\0') {
1296 gpart_show_error("Error", NULL, errstr);
1300 newpartname = strtok(output, " ");
1304 * Try to destroy any geom that gpart picked up already here from
1307 r = gctl_get_handle();
1308 gctl_ro_param(r, "class", -1, "PART");
1309 gctl_ro_param(r, "arg0", -1, newpartname);
1310 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1312 gctl_ro_param(r, "force", sizeof(junk), &junk);
1313 gctl_ro_param(r, "verb", -1, "destroy");
1314 gctl_issue(r); /* Error usually expected and non-fatal */
1318 if (strcmp(items[0].value, "freebsd") == 0)
1319 gpart_partition(newpartname, "BSD");
1321 set_default_part_metadata(newpartname, scheme,
1322 items[0].value, items[2].value, newfs);
1324 for (i = 0; i < nitems(items); i++) {
1325 if (items[i].value != NULL) {
1326 free(items[i].value);
1327 if (init_allocated && items[i].init != NULL)
1328 free((char*)items[i].init);
1332 if (partname != NULL)
1333 *partname = strdup(newpartname);
1337 gpart_delete(struct gprovider *pp)
1341 struct gconsumer *cp;
1346 struct bsddialog_conf conf;
1348 /* Is it a partition? */
1349 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
1351 /* Find out if this is the root of a gpart geom */
1353 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1354 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1359 /* If so, destroy all children */
1361 gpart_destroy(geom);
1363 /* If this is a partition, revert it, so it can be deleted */
1365 r = gctl_get_handle();
1366 gctl_ro_param(r, "class", -1, "PART");
1367 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1368 gctl_ro_param(r, "verb", -1, "undo");
1369 gctl_issue(r); /* Ignore non-fatal errors */
1375 * If this is not a partition, see if that is a problem, complain if
1376 * necessary, and return always, since we need not do anything further,
1379 if (!is_partition) {
1381 bsddialog_initconf(&conf);
1382 conf.title = "Error";
1383 bsddialog_msgbox(&conf,
1384 "Only partitions can be deleted.", 0, 0);
1389 r = gctl_get_handle();
1390 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
1391 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
1392 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1393 gctl_ro_param(r, "verb", -1, "delete");
1395 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
1396 if (strcmp(gc->lg_name, "index") == 0) {
1397 idx = atoi(gc->lg_val);
1398 gctl_ro_param(r, "index", sizeof(idx), &idx);
1403 errstr = gctl_issue(r);
1404 if (errstr != NULL && errstr[0] != '\0') {
1405 gpart_show_error("Error", NULL, errstr);
1412 delete_part_metadata(pp->lg_name);
1416 gpart_revert_all(struct gmesh *mesh)
1418 struct gclass *classp;
1422 const char *modified;
1423 struct bsddialog_conf conf;
1425 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1426 if (strcmp(classp->lg_name, "PART") == 0)
1430 if (strcmp(classp->lg_name, "PART") != 0) {
1431 bsddialog_initconf(&conf);
1432 conf.title = "Error";
1433 bsddialog_msgbox(&conf, "gpart not found!", 0, 0);
1437 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1438 modified = "true"; /* XXX: If we don't know (kernel too old),
1439 * assume there are modifications. */
1440 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1441 if (strcmp(gc->lg_name, "modified") == 0) {
1442 modified = gc->lg_val;
1447 if (strcmp(modified, "false") == 0)
1450 r = gctl_get_handle();
1451 gctl_ro_param(r, "class", -1, "PART");
1452 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1453 gctl_ro_param(r, "verb", -1, "undo");
1461 gpart_commit(struct gmesh *mesh)
1463 struct partition_metadata *md;
1464 struct gclass *classp;
1467 struct gconsumer *cp;
1468 struct gprovider *pp;
1471 const char *modified;
1473 struct bsddialog_conf conf;
1475 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1476 if (strcmp(classp->lg_name, "PART") == 0)
1480 /* Figure out what filesystem / uses */
1481 rootfs = "ufs"; /* Assume ufs if nothing else present */
1482 TAILQ_FOREACH(md, &part_metadata, metadata) {
1483 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) {
1484 rootfs = md->fstab->fs_vfstype;
1489 if (strcmp(classp->lg_name, "PART") != 0) {
1490 bsddialog_initconf(&conf);
1491 conf.title = "Error";
1492 bsddialog_msgbox(&conf, "gpart not found!", 0, 0);
1496 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1497 modified = "true"; /* XXX: If we don't know (kernel too old),
1498 * assume there are modifications. */
1499 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1500 if (strcmp(gc->lg_name, "modified") == 0) {
1501 modified = gc->lg_val;
1506 if (strcmp(modified, "false") == 0)
1509 /* Add bootcode if necessary, before the commit */
1510 md = get_part_metadata(gp->lg_name, 0);
1511 if (md != NULL && md->bootcode)
1514 /* Now install partcode on its partitions, if necessary */
1515 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1516 md = get_part_metadata(pp->lg_name, 0);
1517 if (md == NULL || !md->bootcode)
1520 /* Mark this partition active if that's required */
1523 /* Check if the partition has sub-partitions */
1524 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1525 if (strcmp(cp->lg_geom->lg_class->lg_name,
1529 if (cp == NULL) /* No sub-partitions */
1530 gpart_partcode(pp, rootfs);
1533 r = gctl_get_handle();
1534 gctl_ro_param(r, "class", -1, "PART");
1535 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1536 gctl_ro_param(r, "verb", -1, "commit");
1538 errstr = gctl_issue(r);
1539 if (errstr != NULL && errstr[0] != '\0')
1540 gpart_show_error("Error", NULL, errstr);