2 * Copyright (c) 2011 Nathan Whitehorn
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/param.h>
41 #define GPART_FLAGS "x" /* Do not commit changes by default */
44 gpart_show_error(const char *title, const char *explanation, const char *errstr)
50 if (explanation == NULL)
53 error = strtol(errstr, &errmsg, 0);
54 if (errmsg != errstr) {
55 while (errmsg[0] == ' ')
57 if (errmsg[0] != '\0')
58 sprintf(message, "%s%s. %s", explanation,
59 strerror(error), errmsg);
61 sprintf(message, "%s%s", explanation, strerror(error));
63 sprintf(message, "%s%s", explanation, errmsg);
66 dialog_msgbox(title, message, 0, 0, TRUE);
70 scheme_supports_labels(const char *scheme)
72 if (strcmp(scheme, "APM") == 0)
74 if (strcmp(scheme, "GPT") == 0)
76 if (strcmp(scheme, "PC98") == 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 sizeof(items)/sizeof(items[0]), items, NULL,
106 if (choice == 1) /* Cancel */
110 strcpy(command, "newfs ");
111 for (i = 0; i < (int)(sizeof(items)/sizeof(items[0])); 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 sizeof(items)/sizeof(items[0]), 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 sprintf(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)(sizeof(items)/sizeof(items[0])); 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) {
172 DIALOG_LISTITEM items[] = {
173 {"FAT32", "FAT Type 32",
174 "Create a FAT32 filesystem (default)", 1 },
175 {"FAT16", "FAT Type 16",
176 "Create a FAT16 filesystem", 0 },
177 {"FAT12", "FAT Type 12",
178 "Create a FAT12 filesystem", 0 },
183 choice = dlg_checklist("FAT Options", "", 0, 0, 0,
184 sizeof(items)/sizeof(items[0]), items, NULL,
186 if (choice == 1) /* Cancel */
190 strcpy(command, "newfs_msdos ");
191 for (i = 0; i < (int)(sizeof(items)/sizeof(items[0])); i++) {
192 if (items[i].state == 0)
194 if (strcmp(items[i].name, "FAT32") == 0)
195 strcat(command, "-F 32 ");
196 else if (strcmp(items[i].name, "FAT16") == 0)
197 strcat(command, "-F 16 ");
198 else if (strcmp(items[i].name, "FAT12") == 0)
199 strcat(command, "-F 12 ");
203 dialog_msgbox("Error", "No configurable options exist "
204 "for this filesystem.", 0, 0, TRUE);
210 gpart_partition(const char *lg_name, const char *scheme)
216 DIALOG_LISTITEM items[] = {
217 {"APM", "Apple Partition Map",
218 "Bootable on PowerPC Apple Hardware", 0 },
219 {"BSD", "BSD Labels",
220 "Bootable on most x86 systems", 0 },
221 {"GPT", "GUID Partition Table",
222 "Bootable on most x86 systems", 0 },
223 {"MBR", "DOS Partitions",
224 "Bootable on most x86 systems", 0 },
225 {"PC98", "NEC PC9801 Partition Table",
226 "Bootable on NEC PC9801 systems", 0 },
227 {"VTOC8", "Sun VTOC8 Partition Table",
228 "Bootable on Sun SPARC systems", 0 },
232 if (scheme == NULL) {
233 dialog_vars.default_item = __DECONST(char *, default_scheme());
234 cancel = dlg_menu("Partition Scheme",
235 "Select a partition scheme for this volume:", 0, 0, 0,
236 sizeof(items) / sizeof(items[0]), items, &choice, NULL);
237 dialog_vars.default_item = NULL;
242 if (!is_scheme_bootable(items[choice].name)) {
244 sprintf(message, "This partition scheme (%s) is not "
245 "bootable on this platform. Are you sure you want "
246 "to proceed?", items[choice].name);
247 dialog_vars.defaultno = TRUE;
248 cancel = dialog_yesno("Warning", message, 0, 0);
249 dialog_vars.defaultno = FALSE;
250 if (cancel) /* cancel */
254 scheme = items[choice].name;
257 r = gctl_get_handle();
258 gctl_ro_param(r, "class", -1, "PART");
259 gctl_ro_param(r, "arg0", -1, lg_name);
260 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
261 gctl_ro_param(r, "scheme", -1, scheme);
262 gctl_ro_param(r, "verb", -1, "create");
264 errstr = gctl_issue(r);
265 if (errstr != NULL && errstr[0] != '\0') {
266 gpart_show_error("Error", NULL, errstr);
273 if (bootcode_path(scheme) != NULL)
274 get_part_metadata(lg_name, 1)->bootcode = 1;
279 gpart_activate(struct gprovider *pp)
283 const char *errstr, *scheme;
284 const char *attribute = NULL;
288 * Some partition schemes need this partition to be marked 'active'
289 * for it to be bootable.
291 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
292 if (strcmp(gc->lg_name, "scheme") == 0) {
298 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0 ||
299 strcmp(scheme, "PC98") == 0)
300 attribute = "active";
304 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
305 if (strcmp(gc->lg_name, "index") == 0) {
306 idx = atoi(gc->lg_val);
311 r = gctl_get_handle();
312 gctl_ro_param(r, "class", -1, "PART");
313 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
314 gctl_ro_param(r, "verb", -1, "set");
315 gctl_ro_param(r, "attrib", -1, attribute);
316 gctl_ro_param(r, "index", sizeof(idx), &idx);
318 errstr = gctl_issue(r);
319 if (errstr != NULL && errstr[0] != '\0')
320 gpart_show_error("Error", "Error marking partition active:",
326 gpart_bootcode(struct ggeom *gp)
328 const char *bootcode;
331 const char *errstr, *scheme;
333 size_t bootsize, bytes;
337 * Write default bootcode to the newly partitioned disk, if that
338 * applies on this platform.
340 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
341 if (strcmp(gc->lg_name, "scheme") == 0) {
347 bootcode = bootcode_path(scheme);
348 if (bootcode == NULL)
351 bootfd = open(bootcode, O_RDONLY);
353 dialog_msgbox("Bootcode Error", strerror(errno), 0, 0,
358 bootsize = lseek(bootfd, 0, SEEK_END);
359 boot = malloc(bootsize);
360 lseek(bootfd, 0, SEEK_SET);
362 while (bytes < bootsize)
363 bytes += read(bootfd, boot + bytes, bootsize - bytes);
366 r = gctl_get_handle();
367 gctl_ro_param(r, "class", -1, "PART");
368 gctl_ro_param(r, "arg0", -1, gp->lg_name);
369 gctl_ro_param(r, "verb", -1, "bootcode");
370 gctl_ro_param(r, "bootcode", bootsize, boot);
372 errstr = gctl_issue(r);
373 if (errstr != NULL && errstr[0] != '\0')
374 gpart_show_error("Bootcode Error", NULL, errstr);
380 gpart_partcode(struct gprovider *pp, const char *fstype)
384 const char *indexstr;
385 char message[255], command[255];
387 LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) {
388 if (strcmp(gc->lg_name, "scheme") == 0) {
394 /* Make sure this partition scheme needs partcode on this platform */
395 if (partcode_path(scheme, fstype) == NULL)
398 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
399 if (strcmp(gc->lg_name, "index") == 0) {
400 indexstr = gc->lg_val;
405 /* Shell out to gpart for partcode for now */
406 sprintf(command, "gpart bootcode -p %s -i %s %s",
407 partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name);
408 if (system(command) != 0) {
409 sprintf(message, "Error installing partcode on partition %s",
411 dialog_msgbox("Error", message, 0, 0, TRUE);
416 gpart_destroy(struct ggeom *lg_geom)
419 struct gprovider *pp;
423 /* Delete all child metadata */
424 LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider)
427 /* Revert any local changes to get this geom into a pristine state */
428 r = gctl_get_handle();
429 gctl_ro_param(r, "class", -1, "PART");
430 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
431 gctl_ro_param(r, "verb", -1, "undo");
432 gctl_issue(r); /* Ignore errors -- these are non-fatal */
435 /* Now destroy the geom itself */
436 r = gctl_get_handle();
437 gctl_ro_param(r, "class", -1, "PART");
438 gctl_ro_param(r, "arg0", -1, lg_geom->lg_name);
439 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
440 gctl_ro_param(r, "force", sizeof(force), &force);
441 gctl_ro_param(r, "verb", -1, "destroy");
442 errstr = gctl_issue(r);
443 if (errstr != NULL && errstr[0] != '\0') {
445 * Check if we reverted away the existence of the geom
446 * altogether. Show all other errors to the user.
448 if (strtol(errstr, NULL, 0) != EINVAL)
449 gpart_show_error("Error", NULL, errstr);
453 /* And any metadata associated with the partition scheme itself */
454 delete_part_metadata(lg_geom->lg_name);
458 gpart_edit(struct gprovider *pp)
462 struct gconsumer *cp;
464 const char *errstr, *oldtype, *scheme;
465 struct partition_metadata *md;
469 int hadlabel, choice, junk, nitems;
472 DIALOG_FORMITEM items[] = {
473 {0, "Type:", 5, 0, 0, FALSE, "", 11, 0, 12, 15, 0,
474 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
475 "freebsd-swap)", FALSE},
476 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 0, 0,
477 FALSE, "Partition size. Append K, M, G for kilobytes, "
478 "megabytes or gigabytes.", FALSE},
479 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
480 FALSE, "Path at which to mount this partition (leave blank "
481 "for swap, set to / for root filesystem)", FALSE},
482 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
483 "Partition name. Not all partition schemes support this.",
488 * Find the PART geom we are manipulating. This may be a consumer of
489 * this provider, or its parent. Check the consumer case first.
492 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
493 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
494 /* Check for zombie geoms, treating them as blank */
496 LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) {
497 if (strcmp(gc->lg_name, "scheme") == 0) {
502 if (scheme == NULL || strcmp(scheme, "(none)") == 0) {
503 gpart_partition(cp->lg_geom->lg_name, NULL);
507 /* If this is a nested partition, edit as usual */
508 if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
511 /* Destroy the geom and all sub-partitions */
512 gpart_destroy(cp->lg_geom);
514 /* Now re-partition and return */
515 gpart_partition(cp->lg_geom->lg_name, NULL);
519 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
523 /* Disk not partitioned, so partition it */
524 gpart_partition(pp->lg_name, NULL);
528 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
529 if (strcmp(gc->lg_name, "scheme") == 0) {
535 nitems = scheme_supports_labels(scheme) ? 4 : 3;
537 /* Edit editable parameters of a partition */
539 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
540 if (strcmp(gc->lg_name, "type") == 0) {
541 oldtype = gc->lg_val;
542 items[0].text = gc->lg_val;
544 if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) {
546 items[3].text = gc->lg_val;
548 if (strcmp(gc->lg_name, "index") == 0)
549 idx = atoi(gc->lg_val);
552 TAILQ_FOREACH(md, &part_metadata, metadata) {
553 if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) {
554 if (md->fstab != NULL)
555 items[2].text = md->fstab->fs_file;
560 humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE,
561 HN_NOSPACE | HN_DECIMAL);
562 items[1].text = sizestr;
565 choice = dlg_form("Edit Partition", "", 0, 0, 0, nitems, items, &junk);
567 if (choice) /* Cancel pressed */
570 /* Check if the label has a / in it */
571 if (strchr(items[3].text, '/') != NULL) {
572 dialog_msgbox("Error", "Label contains a /, which is not an "
573 "allowed character.", 0, 0, TRUE);
577 r = gctl_get_handle();
578 gctl_ro_param(r, "class", -1, "PART");
579 gctl_ro_param(r, "arg0", -1, geom->lg_name);
580 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
581 gctl_ro_param(r, "verb", -1, "modify");
582 gctl_ro_param(r, "index", sizeof(idx), &idx);
583 if (hadlabel || items[3].text[0] != '\0')
584 gctl_ro_param(r, "label", -1, items[3].text);
585 gctl_ro_param(r, "type", -1, items[0].text);
586 errstr = gctl_issue(r);
587 if (errstr != NULL && errstr[0] != '\0') {
588 gpart_show_error("Error", NULL, errstr);
594 newfs_command(items[0].text, newfs, 1);
595 set_default_part_metadata(pp->lg_name, scheme, items[0].text,
596 items[2].text, (strcmp(oldtype, items[0].text) != 0) ?
600 if (strcmp(oldtype, items[0].text) != 0 && cp != NULL)
601 gpart_destroy(cp->lg_geom);
602 if (strcmp(oldtype, items[0].text) != 0 && strcmp(items[0].text,
604 gpart_partition(pp->lg_name, "BSD");
606 for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++)
607 if (items[i].text_free)
612 set_default_part_metadata(const char *name, const char *scheme,
613 const char *type, const char *mountpoint, const char *newfs)
615 struct partition_metadata *md;
616 char *zpool_name = NULL;
619 /* Set part metadata */
620 md = get_part_metadata(name, 1);
623 if (md->newfs != NULL) {
628 if (newfs != NULL && newfs[0] != '\0') {
629 md->newfs = malloc(strlen(newfs) + strlen(" /dev/") +
630 strlen(mountpoint) + 5 + strlen(name) + 1);
631 if (strcmp("freebsd-zfs", type) == 0) {
632 zpool_name = strdup((strlen(mountpoint) == 1) ?
633 "root" : &mountpoint[1]);
634 for (i = 0; zpool_name[i] != 0; i++)
635 if (!isalnum(zpool_name[i]))
637 sprintf(md->newfs, "%s %s /dev/%s", newfs,
640 sprintf(md->newfs, "%s /dev/%s", newfs, name);
645 if (strcmp(type, "freebsd-swap") == 0)
647 if (strcmp(type, bootpart_type(scheme)) == 0)
650 /* VTOC8 needs partcode at the start of partitions */
651 if (strcmp(scheme, "VTOC8") == 0 && (strcmp(type, "freebsd-ufs") == 0
652 || strcmp(type, "freebsd-zfs") == 0))
655 if (mountpoint == NULL || mountpoint[0] == '\0') {
656 if (md->fstab != NULL) {
657 free(md->fstab->fs_spec);
658 free(md->fstab->fs_file);
659 free(md->fstab->fs_vfstype);
660 free(md->fstab->fs_mntops);
661 free(md->fstab->fs_type);
666 if (md->fstab == NULL) {
667 md->fstab = malloc(sizeof(struct fstab));
669 free(md->fstab->fs_spec);
670 free(md->fstab->fs_file);
671 free(md->fstab->fs_vfstype);
672 free(md->fstab->fs_mntops);
673 free(md->fstab->fs_type);
675 if (strcmp("freebsd-zfs", type) == 0) {
676 md->fstab->fs_spec = strdup(zpool_name);
678 md->fstab->fs_spec = malloc(strlen(name) +
679 strlen("/dev/") + 1);
680 sprintf(md->fstab->fs_spec, "/dev/%s", name);
682 md->fstab->fs_file = strdup(mountpoint);
683 /* Get VFS from text after freebsd-, if possible */
684 if (strncmp("freebsd-", type, 8) == 0)
685 md->fstab->fs_vfstype = strdup(&type[8]);
686 else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0)
687 md->fstab->fs_vfstype = strdup("msdosfs");
689 md->fstab->fs_vfstype = strdup(type); /* Guess */
690 if (strcmp(type, "freebsd-swap") == 0) {
691 md->fstab->fs_type = strdup(FSTAB_SW);
692 md->fstab->fs_freq = 0;
693 md->fstab->fs_passno = 0;
694 } else if (strcmp(type, "freebsd-zfs") == 0) {
695 md->fstab->fs_type = strdup(FSTAB_RW);
696 md->fstab->fs_freq = 0;
697 md->fstab->fs_passno = 0;
699 md->fstab->fs_type = strdup(FSTAB_RW);
700 if (strcmp(mountpoint, "/") == 0) {
701 md->fstab->fs_freq = 1;
702 md->fstab->fs_passno = 1;
704 md->fstab->fs_freq = 2;
705 md->fstab->fs_passno = 2;
708 md->fstab->fs_mntops = strdup(md->fstab->fs_type);
711 if (zpool_name != NULL)
716 int part_compare(const void *xa, const void *xb)
718 struct gprovider **a = (struct gprovider **)xa;
719 struct gprovider **b = (struct gprovider **)xb;
720 intmax_t astart, bstart;
724 LIST_FOREACH(gc, &(*a)->lg_config, lg_config)
725 if (strcmp(gc->lg_name, "start") == 0) {
726 astart = strtoimax(gc->lg_val, NULL, 0);
729 LIST_FOREACH(gc, &(*b)->lg_config, lg_config)
730 if (strcmp(gc->lg_name, "start") == 0) {
731 bstart = strtoimax(gc->lg_val, NULL, 0);
737 else if (astart > bstart)
744 gpart_max_free(struct ggeom *geom, intmax_t *npartstart)
747 struct gprovider *pp, **providers;
750 intmax_t maxsize, maxstart;
751 intmax_t partstart, partend;
754 /* Now get the maximum free size and free start */
756 LIST_FOREACH(gc, &geom->lg_config, lg_config) {
757 if (strcmp(gc->lg_name, "first") == 0)
758 start = strtoimax(gc->lg_val, NULL, 0);
759 if (strcmp(gc->lg_name, "last") == 0)
760 end = strtoimax(gc->lg_val, NULL, 0);
764 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
766 providers = calloc(nparts, sizeof(providers[0]));
767 LIST_FOREACH(pp, &geom->lg_provider, lg_provider)
769 qsort(providers, nparts, sizeof(providers[0]), part_compare);
773 for (i = 0; i < nparts; i++) {
776 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
777 if (strcmp(gc->lg_name, "start") == 0)
778 partstart = strtoimax(gc->lg_val, NULL, 0);
779 if (strcmp(gc->lg_name, "end") == 0)
780 partend = strtoimax(gc->lg_val, NULL, 0);
783 if (partstart - lastend > maxsize) {
784 maxsize = partstart - lastend - 1;
785 maxstart = lastend + 1;
791 if (end - lastend > maxsize) {
792 maxsize = end - lastend - 1;
793 maxstart = lastend + 1;
796 pp = LIST_FIRST(&geom->lg_consumer)->lg_provider;
798 /* Compute beginning of new partition and maximum available space */
799 if (pp->lg_stripesize > 0 &&
800 (maxstart*pp->lg_sectorsize % pp->lg_stripesize) != 0) {
801 intmax_t offset = (pp->lg_stripesize -
802 ((maxstart*pp->lg_sectorsize) % pp->lg_stripesize)) /
808 if (npartstart != NULL)
809 *npartstart = maxstart;
815 gpart_create(struct gprovider *pp, char *default_type, char *default_size,
816 char *default_mountpoint, char **partname, int interactive)
820 struct gconsumer *cp;
822 const char *errstr, *scheme;
823 char sizestr[32], startstr[32], output[64], *newpartname;
824 char newfs[255], options_fstype[64];
825 intmax_t maxsize, size, sector, firstfree, stripe;
827 int nitems, choice, junk;
830 DIALOG_FORMITEM items[] = {
831 {0, "Type:", 5, 0, 0, FALSE, "freebsd-ufs", 11, 0, 12, 15, 0,
832 FALSE, "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, "
833 "freebsd-swap)", FALSE},
834 {0, "Size:", 5, 1, 0, FALSE, "", 11, 1, 12, 15, 0,
835 FALSE, "Partition size. Append K, M, G for kilobytes, "
836 "megabytes or gigabytes.", FALSE},
837 {0, "Mountpoint:", 11, 2, 0, FALSE, "", 11, 2, 12, 15, 0,
838 FALSE, "Path at which to mount partition (blank for "
839 "swap, / for root filesystem)", FALSE},
840 {0, "Label:", 7, 3, 0, FALSE, "", 11, 3, 12, 15, 0, FALSE,
841 "Partition name. Not all partition schemes support this.",
845 if (partname != NULL)
848 /* Record sector and stripe sizes */
849 sector = pp->lg_sectorsize;
850 stripe = pp->lg_stripesize;
853 * Find the PART geom we are manipulating. This may be a consumer of
854 * this provider, or its parent. Check the consumer case first.
857 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
858 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
863 if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0)
866 /* Now get the partition scheme */
869 LIST_FOREACH(gc, &geom->lg_config, lg_config)
870 if (strcmp(gc->lg_name, "scheme") == 0)
874 if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) {
875 if (gpart_partition(pp->lg_name, NULL) == 0)
877 "The partition table has been successfully created."
878 " Please press Create again to create partitions.",
885 * If we still don't have a geom, either the user has
886 * canceled partitioning or there has been an error which has already
887 * been displayed, so bail.
892 maxsize = size = gpart_max_free(geom, &firstfree);
894 dialog_msgbox("Error", "No free space left on device.", 0, 0,
899 humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE,
900 HN_NOSPACE | HN_DECIMAL);
901 items[1].text = sizestr;
903 /* Special-case the MBR default type for nested partitions */
904 if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "PC98") == 0) {
905 items[0].text = "freebsd";
906 items[0].help = "Filesystem type (e.g. freebsd, fat32)";
909 nitems = scheme_supports_labels(scheme) ? 4 : 3;
911 if (default_type != NULL)
912 items[0].text = default_type;
913 if (default_size != NULL)
914 items[1].text = default_size;
915 if (default_mountpoint != NULL)
916 items[2].text = default_mountpoint;
918 /* Default options */
919 strncpy(options_fstype, items[0].text,
920 sizeof(options_fstype));
921 newfs_command(options_fstype, newfs, 1);
924 dialog_vars.extra_label = "Options";
925 dialog_vars.extra_button = TRUE;
926 choice = dlg_form("Add Partition", "", 0, 0, 0, nitems,
928 dialog_vars.extra_button = FALSE;
934 case 3: /* Options */
935 strncpy(options_fstype, items[0].text,
936 sizeof(options_fstype));
937 newfs_command(options_fstype, newfs, 0);
943 * If the user changed the fs type after specifying options, undo
944 * their choices in favor of the new filesystem's defaults.
946 if (strcmp(options_fstype, items[0].text) != 0) {
947 strncpy(options_fstype, items[0].text, sizeof(options_fstype));
948 newfs_command(options_fstype, newfs, 1);
952 if (strlen(items[1].text) > 0) {
953 if (expand_number(items[1].text, &bytes) != 0) {
956 sprintf(error, "Invalid size: %s\n", strerror(errno));
957 dialog_msgbox("Error", error, 0, 0, TRUE);
960 size = MIN((intmax_t)(bytes/sector), maxsize);
963 /* Check if the label has a / in it */
964 if (strchr(items[3].text, '/') != NULL) {
965 dialog_msgbox("Error", "Label contains a /, which is not an "
966 "allowed character.", 0, 0, TRUE);
970 /* Warn if no mountpoint set */
971 if (strcmp(items[0].text, "freebsd-ufs") == 0 &&
972 items[2].text[0] != '/') {
973 dialog_vars.defaultno = TRUE;
974 choice = dialog_yesno("Warning",
975 "This partition does not have a valid mountpoint "
976 "(for the partition from which you intend to boot the "
977 "operating system, the mountpoint should be /). Are you "
978 "sure you want to continue?"
980 dialog_vars.defaultno = FALSE;
981 if (choice == 1) /* cancel */
986 * Error if this scheme needs nested partitions, this is one, and
987 * a mountpoint was set.
989 if (strcmp(items[0].text, "freebsd") == 0 &&
990 strlen(items[2].text) > 0) {
991 dialog_msgbox("Error", "Partitions of type \"freebsd\" are "
992 "nested BSD-type partition schemes and cannot have "
993 "mountpoints. After creating one, select it and press "
994 "Create again to add the actual file systems.", 0, 0, TRUE);
998 /* If this is the root partition, check that this scheme is bootable */
999 if (strcmp(items[2].text, "/") == 0 && !is_scheme_bootable(scheme)) {
1001 sprintf(message, "This partition scheme (%s) is not bootable "
1002 "on this platform. Are you sure you want to proceed?",
1004 dialog_vars.defaultno = TRUE;
1005 choice = dialog_yesno("Warning", message, 0, 0);
1006 dialog_vars.defaultno = FALSE;
1007 if (choice == 1) /* cancel */
1011 /* If this is the root partition, check that this fs is bootable */
1012 if (strcmp(items[2].text, "/") == 0 && !is_fs_bootable(scheme,
1015 sprintf(message, "This file system (%s) is not bootable "
1016 "on this system. Are you sure you want to proceed?",
1018 dialog_vars.defaultno = TRUE;
1019 choice = dialog_yesno("Warning", message, 0, 0);
1020 dialog_vars.defaultno = FALSE;
1021 if (choice == 1) /* cancel */
1026 * If this is the root partition, and we need a boot partition, ask
1027 * the user to add one.
1030 /* Check for existing freebsd-boot partition */
1031 LIST_FOREACH(pp, &geom->lg_provider, lg_provider) {
1032 struct partition_metadata *md;
1033 md = get_part_metadata(pp->lg_name, 0);
1034 if (md == NULL || !md->bootcode)
1036 LIST_FOREACH(gc, &pp->lg_config, lg_config)
1037 if (strcmp(gc->lg_name, "type") == 0)
1039 if (gc != NULL && strcmp(gc->lg_val,
1040 bootpart_type(scheme)) == 0)
1044 /* If there isn't one, and we need one, ask */
1045 if ((strcmp(items[0].text, "freebsd") == 0 ||
1046 strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0 &&
1049 choice = dialog_yesno("Boot Partition",
1050 "This partition scheme requires a boot partition "
1051 "for the disk to be bootable. Would you like to "
1052 "make one now?", 0, 0);
1056 if (choice == 0) { /* yes */
1057 r = gctl_get_handle();
1058 gctl_ro_param(r, "class", -1, "PART");
1059 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1060 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1061 gctl_ro_param(r, "verb", -1, "add");
1062 gctl_ro_param(r, "type", -1, bootpart_type(scheme));
1063 snprintf(sizestr, sizeof(sizestr), "%jd",
1064 bootpart_size(scheme) / sector);
1065 gctl_ro_param(r, "size", -1, sizestr);
1066 snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1067 gctl_ro_param(r, "start", -1, startstr);
1068 gctl_rw_param(r, "output", sizeof(output), output);
1069 errstr = gctl_issue(r);
1070 if (errstr != NULL && errstr[0] != '\0')
1071 gpart_show_error("Error", NULL, errstr);
1074 get_part_metadata(strtok(output, " "), 1)->bootcode = 1;
1076 /* Now adjust the part we are really adding forward */
1077 firstfree += bootpart_size(scheme) / sector;
1078 size -= (bootpart_size(scheme) + stripe)/sector;
1079 if (stripe > 0 && (firstfree*sector % stripe) != 0)
1080 firstfree += (stripe - ((firstfree*sector) %
1085 r = gctl_get_handle();
1086 gctl_ro_param(r, "class", -1, "PART");
1087 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1088 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1089 gctl_ro_param(r, "verb", -1, "add");
1091 gctl_ro_param(r, "type", -1, items[0].text);
1092 snprintf(sizestr, sizeof(sizestr), "%jd", size);
1093 gctl_ro_param(r, "size", -1, sizestr);
1094 snprintf(startstr, sizeof(startstr), "%jd", firstfree);
1095 gctl_ro_param(r, "start", -1, startstr);
1096 if (items[3].text[0] != '\0')
1097 gctl_ro_param(r, "label", -1, items[3].text);
1098 gctl_rw_param(r, "output", sizeof(output), output);
1099 errstr = gctl_issue(r);
1100 if (errstr != NULL && errstr[0] != '\0') {
1101 gpart_show_error("Error", NULL, errstr);
1105 newpartname = strtok(output, " ");
1109 * Try to destroy any geom that gpart picked up already here from
1112 r = gctl_get_handle();
1113 gctl_ro_param(r, "class", -1, "PART");
1114 gctl_ro_param(r, "arg0", -1, newpartname);
1115 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1117 gctl_ro_param(r, "force", sizeof(junk), &junk);
1118 gctl_ro_param(r, "verb", -1, "destroy");
1119 gctl_issue(r); /* Error usually expected and non-fatal */
1122 if (strcmp(items[0].text, bootpart_type(scheme)) == 0)
1123 get_part_metadata(newpartname, 1)->bootcode = 1;
1124 else if (strcmp(items[0].text, "freebsd") == 0)
1125 gpart_partition(newpartname, "BSD");
1127 set_default_part_metadata(newpartname, scheme,
1128 items[0].text, items[2].text, newfs);
1130 for (i = 0; i < (sizeof(items) / sizeof(items[0])); i++)
1131 if (items[i].text_free)
1132 free(items[i].text);
1134 if (partname != NULL)
1135 *partname = strdup(newpartname);
1139 gpart_delete(struct gprovider *pp)
1143 struct gconsumer *cp;
1149 /* Is it a partition? */
1150 is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0);
1152 /* Find out if this is the root of a gpart geom */
1154 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1155 if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) {
1160 /* If so, destroy all children */
1162 gpart_destroy(geom);
1164 /* If this is a partition, revert it, so it can be deleted */
1166 r = gctl_get_handle();
1167 gctl_ro_param(r, "class", -1, "PART");
1168 gctl_ro_param(r, "arg0", -1, geom->lg_name);
1169 gctl_ro_param(r, "verb", -1, "undo");
1170 gctl_issue(r); /* Ignore non-fatal errors */
1176 * If this is not a partition, see if that is a problem, complain if
1177 * necessary, and return always, since we need not do anything further,
1180 if (!is_partition) {
1182 dialog_msgbox("Error",
1183 "Only partitions can be deleted.", 0, 0, TRUE);
1187 r = gctl_get_handle();
1188 gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name);
1189 gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name);
1190 gctl_ro_param(r, "flags", -1, GPART_FLAGS);
1191 gctl_ro_param(r, "verb", -1, "delete");
1193 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
1194 if (strcmp(gc->lg_name, "index") == 0) {
1195 idx = atoi(gc->lg_val);
1196 gctl_ro_param(r, "index", sizeof(idx), &idx);
1201 errstr = gctl_issue(r);
1202 if (errstr != NULL && errstr[0] != '\0') {
1203 gpart_show_error("Error", NULL, errstr);
1210 delete_part_metadata(pp->lg_name);
1214 gpart_revert_all(struct gmesh *mesh)
1216 struct gclass *classp;
1220 const char *modified;
1222 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1223 if (strcmp(classp->lg_name, "PART") == 0)
1227 if (strcmp(classp->lg_name, "PART") != 0) {
1228 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1232 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1233 modified = "true"; /* XXX: If we don't know (kernel too old),
1234 * assume there are modifications. */
1235 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1236 if (strcmp(gc->lg_name, "modified") == 0) {
1237 modified = gc->lg_val;
1242 if (strcmp(modified, "false") == 0)
1245 r = gctl_get_handle();
1246 gctl_ro_param(r, "class", -1, "PART");
1247 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1248 gctl_ro_param(r, "verb", -1, "undo");
1256 gpart_commit(struct gmesh *mesh)
1258 struct partition_metadata *md;
1259 struct gclass *classp;
1262 struct gconsumer *cp;
1263 struct gprovider *pp;
1266 const char *modified;
1269 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
1270 if (strcmp(classp->lg_name, "PART") == 0)
1274 /* Figure out what filesystem / uses */
1275 rootfs = "ufs"; /* Assume ufs if nothing else present */
1276 TAILQ_FOREACH(md, &part_metadata, metadata) {
1277 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) {
1278 rootfs = md->fstab->fs_vfstype;
1283 if (strcmp(classp->lg_name, "PART") != 0) {
1284 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
1288 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1289 modified = "true"; /* XXX: If we don't know (kernel too old),
1290 * assume there are modifications. */
1291 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
1292 if (strcmp(gc->lg_name, "modified") == 0) {
1293 modified = gc->lg_val;
1298 if (strcmp(modified, "false") == 0)
1301 /* Add bootcode if necessary, before the commit */
1302 md = get_part_metadata(gp->lg_name, 0);
1303 if (md != NULL && md->bootcode)
1306 /* Now install partcode on its partitions, if necessary */
1307 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1308 md = get_part_metadata(pp->lg_name, 0);
1309 if (md == NULL || !md->bootcode)
1312 /* Mark this partition active if that's required */
1315 /* Check if the partition has sub-partitions */
1316 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
1317 if (strcmp(cp->lg_geom->lg_class->lg_name,
1321 if (cp == NULL) /* No sub-partitions */
1322 gpart_partcode(pp, rootfs);
1325 r = gctl_get_handle();
1326 gctl_ro_param(r, "class", -1, "PART");
1327 gctl_ro_param(r, "arg0", -1, gp->lg_name);
1328 gctl_ro_param(r, "verb", -1, "commit");
1330 errstr = gctl_issue(r);
1331 if (errstr != NULL && errstr[0] != '\0')
1332 gpart_show_error("Error", NULL, errstr);