diff options
Diffstat (limited to 'usr.sbin/bsdinstall/partedit')
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/Makefile | 24 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/Makefile.depend | 19 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/diskmenu.c | 90 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/diskmenu.h | 52 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/gpart_ops.c | 1564 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/part_wizard.c | 428 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/partedit.c | 628 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/partedit.h | 108 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/partedit_efi.c | 104 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/partedit_generic.c | 79 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/partedit_powerpc.c | 145 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/partedit_x86.c | 153 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/sade.8 | 71 | ||||
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/scripted.c | 233 |
14 files changed, 3698 insertions, 0 deletions
diff --git a/usr.sbin/bsdinstall/partedit/Makefile b/usr.sbin/bsdinstall/partedit/Makefile new file mode 100644 index 000000000000..7c784addaec0 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/Makefile @@ -0,0 +1,24 @@ +BINDIR= ${LIBEXECDIR}/bsdinstall +PROG= partedit +LINKS= ${BINDIR}/partedit ${BINDIR}/autopart \ + ${BINDIR}/partedit ${BINDIR}/scriptedpart +SYMLINKS= ../libexec/bsdinstall/partedit /usr/sbin/sade +LIBADD+= geom util bsddialog + +PARTEDIT_ARCH= ${MACHINE} +.if ${MACHINE} == "i386" || ${MACHINE} == "amd64" +PARTEDIT_ARCH= x86 +.elif ${MACHINE} == "arm64" || ${MACHINE} == "riscv" +PARTEDIT_ARCH= efi +.endif +.if !exists(partedit_${PARTEDIT_ARCH}.c) +PARTEDIT_ARCH= generic +.endif + +SRCS= diskmenu.c partedit.c gpart_ops.c partedit_${PARTEDIT_ARCH}.c \ + part_wizard.c scripted.c + +WARNS?= 3 +MAN= sade.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/bsdinstall/partedit/Makefile.depend b/usr.sbin/bsdinstall/partedit/Makefile.depend new file mode 100644 index 000000000000..f9843ec634f4 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/Makefile.depend @@ -0,0 +1,19 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libbsddialog \ + lib/libc \ + lib/libcompiler_rt \ + lib/libgeom \ + lib/libutil \ + usr.sbin/bsdinstall/include.amd64 \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/bsdinstall/partedit/diskmenu.c b/usr.sbin/bsdinstall/partedit/diskmenu.c new file mode 100644 index 000000000000..55ef5a200658 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/diskmenu.c @@ -0,0 +1,90 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <bsddialog.h> +#include <libutil.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "diskmenu.h" + +int +diskmenu_show(const char *title, const char *text, struct partedit_item *items, + int nitems, int *focusitem) +{ + int i, output; + char size[16], *mp; + struct bsddialog_menuitem *bsditems; + struct bsddialog_conf conf; + + bsditems = malloc(nitems * sizeof(struct bsddialog_menuitem)); + if (bsditems == NULL) + return BSDDIALOG_ERROR; + for (i = 0; i < nitems; i++) { + bsditems[i].prefix = ""; + bsditems[i].on = false; + bsditems[i].depth = 2 * items[i].indentation; + /* old menu sets max namelen to 10 */ + bsditems[i].name = items[i].name; + humanize_number(size, 7, items[i].size, "B", + HN_AUTOSCALE, HN_DECIMAL); + mp = items[i].mountpoint != NULL ? items[i].mountpoint : ""; + asprintf(__DECONST(char**, &bsditems[i].desc), + " %-9s %-15s %s", size, items[i].type, mp); + bsditems[i].bottomdesc = ""; + } + + bsddialog_initconf(&conf); + conf.title = title; + conf.menu.align_left = true; + conf.text.escape = true; + conf.key.f1_message="[\\Z1\\ZbC\\Znreate]: a new partition.\n" + "[\\Z1\\ZbD\\Znelete]: selected partition(s).\n" + "[\\Z1\\ZbM\\Znodify]: partition type or mountpoint.\n" + "[\\Z1\\ZbR\\Znevert]: changes to disk setup.\n" + "[\\Z1\\ZbA\\Znuto]: guided partitioning tool.\n" + "[\\Z1\\ZbF\\Zninish]: will ask to apply changes."; + conf.menu.shortcut_buttons = true; + conf.button.ok_label = "Create"; + conf.button.with_extra = true; + conf.button.extra_label = "Delete"; + conf.button.cancel_label = "Modify"; + conf.button.with_help = true; + conf.button.help_label = "Revert"; + conf.button.right1_label = "Auto"; + conf.button.right2_label = "Finish"; + conf.button.default_label = "Finish"; + output = bsddialog_menu(&conf, text, 20, 0, 10, nitems, bsditems, + focusitem); + + for (i = 0; i < nitems; i++) + free((char *)bsditems[i].desc); + free(bsditems); + + return (output); +} diff --git a/usr.sbin/bsdinstall/partedit/diskmenu.h b/usr.sbin/bsdinstall/partedit/diskmenu.h new file mode 100644 index 000000000000..9fce2e813214 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/diskmenu.h @@ -0,0 +1,52 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PARTEDIT_DISKMENU_H +#define _PARTEDIT_DISKMENU_H + +#define BUTTON_CREATE BSDDIALOG_OK +#define BUTTON_DELETE BSDDIALOG_EXTRA +#define BUTTON_MODIFY BSDDIALOG_CANCEL +#define BUTTON_REVERT BSDDIALOG_HELP +#define BUTTON_AUTO BSDDIALOG_RIGHT1 +#define BUTTON_FINISH BSDDIALOG_RIGHT2 + +struct partedit_item { + int indentation; + const char *name; + intmax_t size; + const char *type; + char *mountpoint; + + void *cookie; +}; + +int diskmenu_show(const char *title, const char *text, + struct partedit_item *items, int nitems, int *selected); + +#endif diff --git a/usr.sbin/bsdinstall/partedit/gpart_ops.c b/usr.sbin/bsdinstall/partedit/gpart_ops.c new file mode 100644 index 000000000000..0bcd17950daf --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/gpart_ops.c @@ -0,0 +1,1564 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <bsddialog.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <libutil.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libgeom.h> + +#include "partedit.h" + +#define GPART_FLAGS "x" /* Do not commit changes by default */ + +static void +gpart_show_error(const char *title, const char *explanation, const char *errstr) +{ + char *errmsg; + char message[512]; + int error; + struct bsddialog_conf conf; + + if (explanation == NULL) + explanation = ""; + + error = strtol(errstr, &errmsg, 0); + if (errmsg != errstr) { + while (errmsg[0] == ' ') + errmsg++; + if (errmsg[0] != '\0') + snprintf(message, sizeof(message), "%s%s. %s", + explanation, strerror(error), errmsg); + else + snprintf(message, sizeof(message), "%s%s", explanation, + strerror(error)); + } else { + snprintf(message, sizeof(message), "%s%s", explanation, errmsg); + } + + bsddialog_initconf(&conf); + conf.title = title; + bsddialog_msgbox(&conf, message, 0, 0); +} + +static int +scheme_supports_labels(const char *scheme) +{ + if (strcmp(scheme, "APM") == 0) + return (1); + if (strcmp(scheme, "GPT") == 0) + return (1); + + return (0); +} + +static char * +newfs_command(const char *fstype, int use_default) +{ + struct bsddialog_conf conf; + FILE *fp; + char *buf; + size_t len; + + bsddialog_initconf(&conf); + fp = open_memstream(&buf, &len); + + if (strcmp(fstype, "freebsd-ufs") == 0) { + int i; + struct bsddialog_menuitem items[] = { + {"", false, 0, "UFS1", "UFS Version 1", + "Use version 1 of the UFS file system instead " + "of version 2 (not recommended)"}, + {"", true, 0, "SU", "Softupdates", + "Enable softupdates (default)"}, + {"", true, 0, "SUJ", "Softupdates journaling", + "Enable file system journaling (default - " + "turn off for SSDs)"}, + {"", false, 0, "TRIM", "Enable SSD TRIM support", + "Enable TRIM support, useful on solid-state " + "drives" }, + }; + + if (!use_default) { + int choice; + conf.title = "UFS Options"; + choice = bsddialog_checklist(&conf, "", 0, 0, 0, + nitems(items), items, NULL); + if (choice == BSDDIALOG_CANCEL) + goto out; + } + + fputs("newfs ", fp); + for (i = 0; i < (int)nitems(items); i++) { + if (items[i].on == false) + continue; + if (strcmp(items[i].name, "UFS1") == 0) + fputs("-O1 ", fp); + else if (strcmp(items[i].name, "SU") == 0) + fputs("-U ", fp); + else if (strcmp(items[i].name, "SUJ") == 0) + fputs("-j ", fp); + else if (strcmp(items[i].name, "TRIM") == 0) + fputs("-t ", fp); + } + } else if (strcmp(fstype, "freebsd-zfs") == 0) { + int i; + struct bsddialog_menuitem items[] = { + {"", 0, true, "fletcher4", "checksum algorithm: fletcher4", + "Use fletcher4 for data integrity checking. " + "(default)"}, + {"", 0, false, "fletcher2", "checksum algorithm: fletcher2", + "Use fletcher2 for data integrity checking. " + "(not recommended)"}, + {"", 0, false, "sha256", "checksum algorithm: sha256", + "Use sha256 for data integrity checking. " + "(not recommended)"}, + {"", 0, false, "atime", "Update atimes for files", + "Disable atime update"}, + }; + + if (!use_default) { + int choice; + conf.title = "ZFS Options"; + choice = bsddialog_checklist(&conf, "", 0, 0, 0, + nitems(items), items, NULL); + if (choice == BSDDIALOG_CANCEL) + goto out; + } + + fputs("zpool create -f -m none ", fp); + if (getenv("BSDINSTALL_TMPBOOT") != NULL) { + char zfsboot_path[MAXPATHLEN]; + + snprintf(zfsboot_path, sizeof(zfsboot_path), "%s/zfs", + getenv("BSDINSTALL_TMPBOOT")); + mkdir(zfsboot_path, S_IRWXU | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH); + fprintf(fp, " -o cachefile=%s/zpool.cache ", + zfsboot_path); + } + for (i = 0; i < (int)nitems(items); i++) { + if (items[i].on == false) + continue; + if (strcmp(items[i].name, "fletcher4") == 0) + fputs("-O checksum=fletcher4 ", fp); + else if (strcmp(items[i].name, "fletcher2") == 0) + fputs("-O checksum=fletcher2 ", fp); + else if (strcmp(items[i].name, "sha256") == 0) + fputs("-O checksum=sha256 ", fp); + else if (strcmp(items[i].name, "atime") == 0) + fputs("-O atime=off ", fp); + } + } else if (strcmp(fstype, "fat32") == 0 || strcmp(fstype, "efi") == 0 || + strcmp(fstype, "ms-basic-data") == 0) { + int i; + struct bsddialog_menuitem items[] = { + {"", 0, true, "FAT32", "FAT Type 32", + "Create a FAT32 filesystem (default)"}, + {"", 0, false, "FAT16", "FAT Type 16", + "Create a FAT16 filesystem"}, + {"", 0, false, "FAT12", "FAT Type 12", + "Create a FAT12 filesystem"}, + }; + + if (!use_default) { + int choice; + conf.title = "FAT Options"; + choice = bsddialog_radiolist(&conf, "", 0, 0, 0, + nitems(items), items, NULL); + if (choice == BSDDIALOG_CANCEL) + goto out; + } + + fputs("newfs_msdos ", fp); + for (i = 0; i < (int)nitems(items); i++) { + if (items[i].on == false) + continue; + if (strcmp(items[i].name, "FAT32") == 0) + fputs("-F 32 -c 1", fp); + else if (strcmp(items[i].name, "FAT16") == 0) + fputs("-F 16 ", fp); + else if (strcmp(items[i].name, "FAT12") == 0) + fputs("-F 12 ", fp); + } + } else { + if (!use_default) { + conf.title = "Error"; + bsddialog_msgbox(&conf, "No configurable options exist " + "for this filesystem.", 0, 0); + } + } + +out: + fclose(fp); + return (buf); +} + +const char * +choose_part_type(const char *def_scheme) +{ + int button, choice, i; + const char *scheme = NULL; + struct bsddialog_conf conf; + + struct bsddialog_menuitem items[] = { + {"", false, 0, "APM", "Apple Partition Map", + "Bootable on PowerPC Apple Hardware" }, + {"", false, 0, "BSD", "BSD Labels", + "Bootable on most x86 systems" }, + {"", false, 0, "GPT", "GUID Partition Table", + "Bootable on most x86 systems and EFI aware ARM64" }, + {"", false, 0, "MBR", "DOS Partitions", + "Bootable on most x86 systems" }, + }; + + for (i = 0; i < (int)nitems(items); i++) + if (strcmp(items[i].name, def_scheme) == 0) + choice = i; + + bsddialog_initconf(&conf); + +parttypemenu: + conf.title = "Partition Scheme"; + button = bsddialog_menu(&conf, + "Select a partition scheme for this volume:", 0, 0, 0, + nitems(items), items, &choice); + + if (button == BSDDIALOG_CANCEL) + return NULL; + + if (!is_scheme_bootable(items[choice].name)) { + char message[512]; + + snprintf(message, sizeof(message), + "This partition scheme (%s) is not " + "bootable on this platform. Are you sure you want " + "to proceed?", items[choice].name); + conf.button.default_cancel = true; + conf.title = "Warning"; + button = bsddialog_yesno(&conf, message, 0, 0); + conf.button.default_cancel = false; + if (button == BSDDIALOG_NO) + goto parttypemenu; + } + + scheme = items[choice].name; + + return scheme; +} + +int +gpart_partition(const char *lg_name, const char *scheme) +{ + int button; + struct gctl_req *r; + const char *errstr; + struct bsddialog_conf conf; + + bsddialog_initconf(&conf); + +schememenu: + if (scheme == NULL) { + scheme = choose_part_type(default_scheme()); + + if (scheme == NULL) + return (-1); + + if (!is_scheme_bootable(scheme)) { + char message[512]; + + snprintf(message, sizeof(message), + "This partition scheme (%s) is not " + "bootable on this platform. Are you sure you want " + "to proceed?", scheme); + conf.button.default_cancel = true; + conf.title = "Warning"; + button = bsddialog_yesno(&conf, message, 0, 0); + conf.button.default_cancel = false; + if (button == BSDDIALOG_NO) { + /* Reset scheme so user can choose another */ + scheme = NULL; + goto schememenu; + } + } + } + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, lg_name); + gctl_ro_param(r, "flags", -1, GPART_FLAGS); + gctl_ro_param(r, "scheme", -1, scheme); + gctl_ro_param(r, "verb", -1, "create"); + + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') { + gpart_show_error("Error", NULL, errstr); + gctl_free(r); + scheme = NULL; + goto schememenu; + } + gctl_free(r); + + if (bootcode_path(scheme) != NULL) + get_part_metadata(lg_name, 1)->bootcode = 1; + return (0); +} + +static void +gpart_activate(struct gprovider *pp) +{ + struct gconfig *gc; + struct gctl_req *r; + const char *errstr, *scheme; + const char *attribute = NULL; + intmax_t idx; + + /* + * Some partition schemes need this partition to be marked 'active' + * for it to be bootable. + */ + LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) { + scheme = gc->lg_val; + break; + } + } + + if (strcmp(scheme, "MBR") == 0 || strcmp(scheme, "EBR") == 0) + attribute = "active"; + else + return; + + LIST_FOREACH(gc, &pp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "index") == 0) { + idx = atoi(gc->lg_val); + break; + } + } + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); + gctl_ro_param(r, "verb", -1, "set"); + gctl_ro_param(r, "attrib", -1, attribute); + gctl_ro_param(r, "index", sizeof(idx), &idx); + + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') + gpart_show_error("Error", "Error marking partition active:", + errstr); + gctl_free(r); +} + +void +gpart_set_root(const char *lg_name, const char *attribute) +{ + struct gctl_req *r; + const char *errstr; + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, lg_name); + gctl_ro_param(r, "flags", -1, "C"); + gctl_ro_param(r, "verb", -1, "set"); + gctl_ro_param(r, "attrib", -1, attribute); + + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') + gpart_show_error("Error", "Error setting parameter on disk:", + errstr); + gctl_free(r); +} + +static void +gpart_bootcode(struct ggeom *gp) +{ + const char *bootcode; + struct gconfig *gc; + struct gctl_req *r; + const char *errstr, *scheme; + uint8_t *boot; + size_t bootsize, bytes; + int bootfd; + struct bsddialog_conf conf; + + /* + * Write default bootcode to the newly partitioned disk, if that + * applies on this platform. + */ + LIST_FOREACH(gc, &gp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) { + scheme = gc->lg_val; + break; + } + } + + bootcode = bootcode_path(scheme); + if (bootcode == NULL) + return; + + bootfd = open(bootcode, O_RDONLY); + if (bootfd < 0) { + bsddialog_initconf(&conf); + conf.title = "Bootcode Error"; + bsddialog_msgbox(&conf, strerror(errno), 0, 0); + return; + } + + bootsize = lseek(bootfd, 0, SEEK_END); + boot = malloc(bootsize); + lseek(bootfd, 0, SEEK_SET); + bytes = 0; + while (bytes < bootsize) + bytes += read(bootfd, boot + bytes, bootsize - bytes); + close(bootfd); + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, gp->lg_name); + gctl_ro_param(r, "verb", -1, "bootcode"); + gctl_ro_param(r, "bootcode", bootsize, boot); + + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') + gpart_show_error("Bootcode Error", NULL, errstr); + gctl_free(r); + free(boot); +} + +static void +gpart_partcode(struct gprovider *pp, const char *fstype) +{ + struct gconfig *gc; + const char *scheme; + const char *indexstr; + char message[255], command[255]; + struct bsddialog_conf conf; + + LIST_FOREACH(gc, &pp->lg_geom->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) { + scheme = gc->lg_val; + break; + } + } + + /* Make sure this partition scheme needs partcode on this platform */ + if (partcode_path(scheme, fstype) == NULL) + return; + + LIST_FOREACH(gc, &pp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "index") == 0) { + indexstr = gc->lg_val; + break; + } + } + + /* Shell out to gpart for partcode for now */ + snprintf(command, sizeof(command), "gpart bootcode -p %s -i %s %s", + partcode_path(scheme, fstype), indexstr, pp->lg_geom->lg_name); + if (system(command) != 0) { + snprintf(message, sizeof(message), + "Error installing partcode on partition %s", + pp->lg_name); + bsddialog_initconf(&conf); + conf.title = "Error"; + bsddialog_msgbox(&conf, message, 0, 0); + } +} + +void +gpart_destroy(struct ggeom *lg_geom) +{ + struct gctl_req *r; + struct gprovider *pp; + const char *errstr; + int force = 1; + + /* Delete all child metadata */ + LIST_FOREACH(pp, &lg_geom->lg_provider, lg_provider) + gpart_delete(pp); + + /* Revert any local changes to get this geom into a pristine state */ + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); + gctl_ro_param(r, "verb", -1, "undo"); + gctl_issue(r); /* Ignore errors -- these are non-fatal */ + gctl_free(r); + + /* Now destroy the geom itself */ + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, lg_geom->lg_name); + gctl_ro_param(r, "flags", -1, GPART_FLAGS); + gctl_ro_param(r, "force", sizeof(force), &force); + gctl_ro_param(r, "verb", -1, "destroy"); + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') { + /* + * Check if we reverted away the existence of the geom + * altogether. Show all other errors to the user. + */ + if (strtol(errstr, NULL, 0) != EINVAL) + gpart_show_error("Error", NULL, errstr); + } + gctl_free(r); + + /* And any metadata associated with the partition scheme itself */ + delete_part_metadata(lg_geom->lg_name); +} + +void +gpart_edit(struct gprovider *pp) +{ + struct gctl_req *r; + struct gconfig *gc; + struct gconsumer *cp; + struct ggeom *geom; + const char *errstr, *oldtype, *scheme; + struct partition_metadata *md; + char sizestr[32]; + char *newfs; + intmax_t idx; + int hadlabel, choice, nitems; + unsigned i; + struct bsddialog_conf conf; + + struct bsddialog_formitem items[] = { + { "Type:", 1, 1, "", 1, 12, 12, 15, NULL, 0, + "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " + "freebsd-swap)"}, + { "Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0, + "Partition size. Append K, M, G for kilobytes, " + "megabytes or gigabytes."}, + { "Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0, + "Path at which to mount this partition (leave blank " + "for swap, set to / for root filesystem)"}, + { "Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0, + "Partition name. Not all partition schemes support this."}, + }; + + bsddialog_initconf(&conf); + + /* + * Find the PART geom we are manipulating. This may be a consumer of + * this provider, or its parent. Check the consumer case first. + */ + geom = NULL; + LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) + if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { + /* Check for zombie geoms, treating them as blank */ + scheme = NULL; + LIST_FOREACH(gc, &cp->lg_geom->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) { + scheme = gc->lg_val; + break; + } + } + if (scheme == NULL || strcmp(scheme, "(none)") == 0) { + gpart_partition(cp->lg_geom->lg_name, NULL); + return; + } + + /* If this is a nested partition, edit as usual */ + if (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) + break; + + /* Destroy the geom and all sub-partitions */ + gpart_destroy(cp->lg_geom); + + /* Now re-partition and return */ + gpart_partition(cp->lg_geom->lg_name, NULL); + return; + } + + if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) + geom = pp->lg_geom; + + if (geom == NULL) { + /* Disk not partitioned, so partition it */ + gpart_partition(pp->lg_name, NULL); + return; + } + + LIST_FOREACH(gc, &geom->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) { + scheme = gc->lg_val; + break; + } + } + + nitems = scheme_supports_labels(scheme) ? 4 : 3; + + /* Edit editable parameters of a partition */ + hadlabel = 0; + LIST_FOREACH(gc, &pp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "type") == 0) { + oldtype = gc->lg_val; + items[0].init = gc->lg_val; + } + if (strcmp(gc->lg_name, "label") == 0 && gc->lg_val != NULL) { + hadlabel = 1; + items[3].init = gc->lg_val; + } + if (strcmp(gc->lg_name, "index") == 0) + idx = atoi(gc->lg_val); + } + + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->name != NULL && strcmp(md->name, pp->lg_name) == 0) { + if (md->fstab != NULL) + items[2].init = md->fstab->fs_file; + break; + } + } + + humanize_number(sizestr, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, + HN_NOSPACE | HN_DECIMAL); + items[1].init = sizestr; + +editpart: + conf.button.always_active = true; + conf.title = "Edit Partition"; + choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items, NULL); + conf.button.always_active = false; + + if (choice == BSDDIALOG_CANCEL) + goto endedit; + + /* If this is the root partition, check that this fs is bootable */ + if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme, + items[0].value)) { + char message[512]; + + snprintf(message, sizeof(message), + "This file system (%s) is not bootable " + "on this system. Are you sure you want to proceed?", + items[0].value); + conf.button.default_cancel = true; + conf.title = "Warning"; + choice = bsddialog_yesno(&conf, message, 0, 0); + conf.button.default_cancel = false; + if (choice == BSDDIALOG_CANCEL) + goto editpart; + } + + /* Check if the label has a / in it */ + if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) { + conf.title = "Error"; + bsddialog_msgbox(&conf, "Label contains a /, which is not an " + "allowed character.", 0, 0); + goto editpart; + } + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, geom->lg_name); + gctl_ro_param(r, "flags", -1, GPART_FLAGS); + gctl_ro_param(r, "verb", -1, "modify"); + gctl_ro_param(r, "index", sizeof(idx), &idx); + if (items[3].value != NULL && (hadlabel || items[3].value[0] != '\0')) + gctl_ro_param(r, "label", -1, items[3].value); + gctl_ro_param(r, "type", -1, items[0].value); + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') { + gpart_show_error("Error", NULL, errstr); + gctl_free(r); + goto editpart; + } + gctl_free(r); + + newfs = newfs_command(items[0].value, 1); + set_default_part_metadata(pp->lg_name, scheme, items[0].value, + items[2].value, (strcmp(oldtype, items[0].value) != 0) ? + newfs : NULL); + free(newfs); + +endedit: + if (strcmp(oldtype, items[0].value) != 0 && cp != NULL) + gpart_destroy(cp->lg_geom); + if (strcmp(oldtype, items[0].value) != 0 && strcmp(items[0].value, + "freebsd") == 0) + gpart_partition(pp->lg_name, "BSD"); + + for (i = 0; i < nitems(items); i++) + if (items[i].value != NULL) + free(items[i].value); +} + +void +set_default_part_metadata(const char *name, const char *scheme, + const char *type, const char *mountpoint, const char *newfs) +{ + struct partition_metadata *md; + char *zpool_name = NULL; + const char *default_bootmount = NULL; + int i; + + /* Set part metadata */ + md = get_part_metadata(name, 1); + + if (newfs) { + if (md->newfs != NULL) { + free(md->newfs); + md->newfs = NULL; + } + + if (newfs != NULL && newfs[0] != '\0') { + if (strcmp("freebsd-zfs", type) == 0) { + zpool_name = strdup((strlen(mountpoint) == 1) ? + "root" : &mountpoint[1]); + for (i = 0; zpool_name[i] != 0; i++) + if (!isalnum(zpool_name[i])) + zpool_name[i] = '_'; + asprintf(&md->newfs, "%s %s /dev/%s", newfs, + zpool_name, name); + } else { + asprintf(&md->newfs, "%s /dev/%s", newfs, name); + } + } + } + + if (strcmp(type, "freebsd-swap") == 0) + mountpoint = "none"; + if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) { + if (default_bootmount == NULL) + md->bootcode = 1; + else if (mountpoint == NULL || strlen(mountpoint) == 0) + mountpoint = default_bootmount; + } + + if (mountpoint == NULL || mountpoint[0] == '\0') { + if (md->fstab != NULL) { + free(md->fstab->fs_spec); + free(md->fstab->fs_file); + free(md->fstab->fs_vfstype); + free(md->fstab->fs_mntops); + free(md->fstab->fs_type); + free(md->fstab); + md->fstab = NULL; + } + } else { + if (md->fstab == NULL) { + md->fstab = malloc(sizeof(struct fstab)); + } else { + free(md->fstab->fs_spec); + free(md->fstab->fs_file); + free(md->fstab->fs_vfstype); + free(md->fstab->fs_mntops); + free(md->fstab->fs_type); + } + if (strcmp("freebsd-zfs", type) == 0) { + md->fstab->fs_spec = strdup(zpool_name); + } else { + asprintf(&md->fstab->fs_spec, "/dev/%s", name); + } + md->fstab->fs_file = strdup(mountpoint); + /* Get VFS from text after freebsd-, if possible */ + if (strncmp("freebsd-", type, 8) == 0) + md->fstab->fs_vfstype = strdup(&type[8]); + else if (strcmp("fat32", type) == 0 || strcmp("efi", type) == 0 + || strcmp("ms-basic-data", type) == 0) + md->fstab->fs_vfstype = strdup("msdosfs"); + else + md->fstab->fs_vfstype = strdup(type); /* Guess */ + if (strcmp(type, "freebsd-swap") == 0) { + md->fstab->fs_type = strdup(FSTAB_SW); + md->fstab->fs_freq = 0; + md->fstab->fs_passno = 0; + } else if (strcmp(type, "freebsd-zfs") == 0) { + md->fstab->fs_type = strdup(FSTAB_RW); + md->fstab->fs_freq = 0; + md->fstab->fs_passno = 0; + } else { + md->fstab->fs_type = strdup(FSTAB_RW); + if (strcmp(mountpoint, "/") == 0) { + md->fstab->fs_freq = 1; + md->fstab->fs_passno = 1; + } else { + md->fstab->fs_freq = 2; + md->fstab->fs_passno = 2; + } + } + md->fstab->fs_mntops = strdup(md->fstab->fs_type); + } + + if (zpool_name != NULL) + free(zpool_name); +} + +static +int part_compare(const void *xa, const void *xb) +{ + struct gprovider **a = (struct gprovider **)xa; + struct gprovider **b = (struct gprovider **)xb; + intmax_t astart, bstart; + struct gconfig *gc; + + astart = bstart = 0; + LIST_FOREACH(gc, &(*a)->lg_config, lg_config) + if (strcmp(gc->lg_name, "start") == 0) { + astart = strtoimax(gc->lg_val, NULL, 0); + break; + } + LIST_FOREACH(gc, &(*b)->lg_config, lg_config) + if (strcmp(gc->lg_name, "start") == 0) { + bstart = strtoimax(gc->lg_val, NULL, 0); + break; + } + + if (astart < bstart) + return -1; + else if (astart > bstart) + return 1; + else + return 0; +} + +intmax_t +gpart_max_free(struct ggeom *geom, intmax_t *npartstart) +{ + struct gconfig *gc; + struct gprovider *pp, **providers; + intmax_t sectorsize, stripesize, offset; + intmax_t lastend; + intmax_t start, end; + intmax_t maxsize, maxstart; + intmax_t partstart, partend; + int i, nparts; + + /* Now get the maximum free size and free start */ + start = end = 0; + LIST_FOREACH(gc, &geom->lg_config, lg_config) { + if (strcmp(gc->lg_name, "first") == 0) + start = strtoimax(gc->lg_val, NULL, 0); + if (strcmp(gc->lg_name, "last") == 0) + end = strtoimax(gc->lg_val, NULL, 0); + } + + i = nparts = 0; + LIST_FOREACH(pp, &geom->lg_provider, lg_provider) + nparts++; + providers = calloc(nparts, sizeof(providers[0])); + LIST_FOREACH(pp, &geom->lg_provider, lg_provider) + providers[i++] = pp; + qsort(providers, nparts, sizeof(providers[0]), part_compare); + + lastend = start - 1; + maxsize = 0; + for (i = 0; i < nparts; i++) { + pp = providers[i]; + + LIST_FOREACH(gc, &pp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "start") == 0) + partstart = strtoimax(gc->lg_val, NULL, 0); + if (strcmp(gc->lg_name, "end") == 0) + partend = strtoimax(gc->lg_val, NULL, 0); + } + + if (partstart - lastend > maxsize) { + maxsize = partstart - lastend - 1; + maxstart = lastend + 1; + } + + lastend = partend; + } + + if (end - lastend > maxsize) { + maxsize = end - lastend; + maxstart = lastend + 1; + } + + pp = LIST_FIRST(&geom->lg_consumer)->lg_provider; + + /* + * Round the start and size of the largest available space up to + * the nearest multiple of the adjusted stripe size. + * + * The adjusted stripe size is the least common multiple of the + * actual stripe size, or the sector size if no stripe size was + * reported, and 4096. The reason for this is that contemporary + * disks often have 4096-byte physical sectors but report 512 + * bytes instead for compatibility with older / broken operating + * systems and BIOSes. For the same reasons, virtualized storage + * may also report a 512-byte stripe size, or none at all. + */ + sectorsize = pp->lg_sectorsize; + if ((stripesize = pp->lg_stripesize) == 0) + stripesize = sectorsize; + while (stripesize % 4096 != 0) + stripesize *= 2; + if ((offset = maxstart * sectorsize % stripesize) != 0) { + offset = (stripesize - offset) / sectorsize; + maxstart += offset; + maxsize -= offset; + } + + if (npartstart != NULL) + *npartstart = maxstart; + + return (maxsize); +} + +static size_t +add_boot_partition(struct ggeom *geom, struct gprovider *pp, + const char *scheme, int interactive) +{ + struct gconfig *gc; + struct gprovider *ppi; + int choice; + struct bsddialog_conf conf; + + /* Check for existing freebsd-boot partition */ + LIST_FOREACH(ppi, &geom->lg_provider, lg_provider) { + struct partition_metadata *md; + const char *bootmount = NULL; + + LIST_FOREACH(gc, &ppi->lg_config, lg_config) + if (strcmp(gc->lg_name, "type") == 0) + break; + if (gc == NULL) + continue; + if (strcmp(gc->lg_val, bootpart_type(scheme, &bootmount)) != 0) + continue; + + /* + * If the boot partition is not mountable and needs partcode, + * but doesn't have it, it doesn't satisfy our requirements. + */ + md = get_part_metadata(ppi->lg_name, 0); + if (bootmount == NULL && (md == NULL || !md->bootcode)) + continue; + + /* If it is mountable, but mounted somewhere else, remount */ + if (bootmount != NULL && md != NULL && md->fstab != NULL + && strlen(md->fstab->fs_file) > 0 + && strcmp(md->fstab->fs_file, bootmount) != 0) + continue; + + /* If it is mountable, but mountpoint is not set, mount it */ + if (bootmount != NULL && md == NULL) + set_default_part_metadata(ppi->lg_name, scheme, + gc->lg_val, bootmount, NULL); + + /* Looks good at this point, no added data needed */ + return (0); + } + + if (interactive) { + bsddialog_initconf(&conf); + conf.title = "Boot Partition"; + choice = bsddialog_yesno(&conf, + "This partition scheme requires a boot partition " + "for the disk to be bootable. Would you like to " + "make one now?", 0, 0); + } else { + choice = BSDDIALOG_YES; + } + + if (choice == BSDDIALOG_YES) { + struct partition_metadata *md; + const char *bootmount = NULL; + char *bootpartname = NULL; + char sizestr[7]; + + humanize_number(sizestr, 7, + bootpart_size(scheme), "B", HN_AUTOSCALE, + HN_NOSPACE | HN_DECIMAL); + + gpart_create(pp, bootpart_type(scheme, &bootmount), + sizestr, bootmount, &bootpartname, 0); + + if (bootpartname == NULL) /* Error reported to user already */ + return 0; + + /* If the part is not mountable, make sure newfs isn't set */ + if (bootmount == NULL) { + md = get_part_metadata(bootpartname, 0); + if (md != NULL && md->newfs != NULL) { + free(md->newfs); + md->newfs = NULL; + } + } + + free(bootpartname); + + return (bootpart_size(scheme)); + } + + return (0); +} + +void +gpart_create(struct gprovider *pp, const char *default_type, + const char *default_size, const char *default_mountpoint, + char **partname, int interactive) +{ + struct gctl_req *r; + struct gconfig *gc; + struct gconsumer *cp; + struct ggeom *geom; + const char *errstr, *scheme; + char sizestr[32], startstr[32], output[64], *newpartname; + char *newfs, options_fstype[64]; + intmax_t maxsize, size, sector, firstfree, stripe; + uint64_t bytes; + int nitems, choice, junk; + unsigned i; + bool init_allocated; + struct bsddialog_conf conf; + + struct bsddialog_formitem items[] = { + {"Type:", 1, 1, "freebsd-ufs", 1, 12, 12, 15, NULL, 0, + "Filesystem type (e.g. freebsd-ufs, freebsd-zfs, " + "freebsd-swap)"}, + {"Size:", 2, 1, "", 2, 12, 12, 15, NULL, 0, + "Partition size. Append K, M, G for kilobytes, " + "megabytes or gigabytes."}, + {"Mountpoint:", 3, 1, "", 3, 12, 12, 15, NULL, 0, + "Path at which to mount partition (blank for " + "swap, / for root filesystem)"}, + {"Label:", 4, 1, "", 4, 12, 12, 15, NULL, 0, + "Partition name. Not all partition schemes support this."}, + }; + + bsddialog_initconf(&conf); + + if (partname != NULL) + *partname = NULL; + + /* Record sector and stripe sizes */ + sector = pp->lg_sectorsize; + stripe = pp->lg_stripesize; + + /* + * Find the PART geom we are manipulating. This may be a consumer of + * this provider, or its parent. Check the consumer case first. + */ + geom = NULL; + LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) + if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { + geom = cp->lg_geom; + break; + } + + if (geom == NULL && strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0) + geom = pp->lg_geom; + + /* Now get the partition scheme */ + scheme = NULL; + if (geom != NULL) { + LIST_FOREACH(gc, &geom->lg_config, lg_config) + if (strcmp(gc->lg_name, "scheme") == 0) + scheme = gc->lg_val; + } + + if (geom == NULL || scheme == NULL || strcmp(scheme, "(none)") == 0) { + if (gpart_partition(pp->lg_name, NULL) == 0) { + bsddialog_msgbox(&conf, + "The partition table has been successfully created." + " Please press Create again to create partitions.", + 0, 0); + } + + return; + } + + /* + * If we still don't have a geom, either the user has + * canceled partitioning or there has been an error which has already + * been displayed, so bail. + */ + if (geom == NULL) + return; + + maxsize = size = gpart_max_free(geom, &firstfree); + if (size <= 0) { + conf .title = "Error"; + bsddialog_msgbox(&conf, "No free space left on device.", 0, 0); + return; + } + + humanize_number(sizestr, 7, size*sector, "B", HN_AUTOSCALE, + HN_NOSPACE | HN_DECIMAL); + items[1].init = sizestr; + + /* Special-case the MBR default type for nested partitions */ + if (strcmp(scheme, "MBR") == 0) { + items[0].init = "freebsd"; + items[0].bottomdesc = "Filesystem type (e.g. freebsd, fat32)"; + } + + nitems = scheme_supports_labels(scheme) ? 4 : 3; + + if (default_type != NULL) + items[0].init = (char *)default_type; + if (default_size != NULL) + items[1].init = (char *)default_size; + if (default_mountpoint != NULL) + items[2].init = (char *)default_mountpoint; + + /* Default options */ + strncpy(options_fstype, items[0].init, + sizeof(options_fstype)); + newfs = newfs_command(options_fstype, 1); + + init_allocated = false; +addpartform: + if (interactive) { + conf.button.with_extra = true; + conf.button.extra_label = "Options"; + conf.button.always_active = true; + conf.title = "Add Partition"; + choice = bsddialog_form(&conf, "", 0, 0, 0, nitems, items, NULL); + conf.button.with_extra = false; + conf.button.extra_label = NULL; + conf.button.always_active = false; + switch (choice) { + case BSDDIALOG_OK: + break; + case BSDDIALOG_CANCEL: + return; + case BSDDIALOG_EXTRA: /* Options */ + free(newfs); + strncpy(options_fstype, items[0].value, + sizeof(options_fstype)); + newfs = newfs_command(options_fstype, 0); + for (i = 0; i < nitems(items); i++) { + if (init_allocated) + free((char*)items[i].init); + items[i].init = items[i].value; + } + init_allocated = true; + goto addpartform; + } + } else { /* auto partitioning */ + items[0].value = strdup(items[0].init); + items[1].value = strdup(items[1].init); + items[2].value = strdup(items[2].init); + if (nitems > 3) + items[3].value = strdup(items[3].init); + } + + /* + * If the user changed the fs type after specifying options, undo + * their choices in favor of the new filesystem's defaults. + */ + if (strcmp(options_fstype, items[0].value) != 0) { + free(newfs); + strncpy(options_fstype, items[0].value, sizeof(options_fstype)); + newfs = newfs_command(options_fstype, 1); + } + + size = maxsize; + if (strlen(items[1].value) > 0) { + if (expand_number(items[1].value, &bytes) != 0) { + char error[512]; + + snprintf(error, sizeof(error), "Invalid size: %s\n", + strerror(errno)); + conf.title = "Error"; + bsddialog_msgbox(&conf, error, 0, 0); + goto addpartform; + } + size = MIN((intmax_t)(bytes/sector), maxsize); + } + + /* Check if the label has a / in it */ + if (items[3].value != NULL && strchr(items[3].value, '/') != NULL) { + conf.title = "Error"; + bsddialog_msgbox(&conf, "Label contains a /, which is not an " + "allowed character.", 0, 0); + goto addpartform; + } + + /* Warn if no mountpoint set */ + if (strcmp(items[0].value, "freebsd-ufs") == 0 && + items[2].value[0] != '/') { + choice = 0; + if (interactive) { + conf.button.default_cancel = true; + conf.title = "Warning"; + choice = bsddialog_yesno(&conf, + "This partition does not have a valid mountpoint " + "(for the partition from which you intend to boot the " + "operating system, the mountpoint should be /). Are you " + "sure you want to continue?" + , 0, 0); + conf.button.default_cancel = false; + } + if (choice == BSDDIALOG_CANCEL) + goto addpartform; + } + + /* + * Error if this scheme needs nested partitions, this is one, and + * a mountpoint was set. + */ + if (strcmp(items[0].value, "freebsd") == 0 && + strlen(items[2].value) > 0) { + conf.title = "Error"; + bsddialog_msgbox(&conf, "Partitions of type \"freebsd\" are " + "nested BSD-type partition schemes and cannot have " + "mountpoints. After creating one, select it and press " + "Create again to add the actual file systems.", 0, 0); + goto addpartform; + } + + /* If this is the root partition, check that this scheme is bootable */ + if (strcmp(items[2].value, "/") == 0 && !is_scheme_bootable(scheme)) { + char message[512]; + + snprintf(message, sizeof(message), + "This partition scheme (%s) is not bootable " + "on this platform. Are you sure you want to proceed?", + scheme); + conf.button.default_cancel = true; + conf.title = "Warning"; + choice = bsddialog_yesno(&conf, message, 0, 0); + conf.button.default_cancel = false; + if (choice == BSDDIALOG_CANCEL) + goto addpartform; + } + + /* If this is the root partition, check that this fs is bootable */ + if (strcmp(items[2].value, "/") == 0 && !is_fs_bootable(scheme, + items[0].value)) { + char message[512]; + + snprintf(message, sizeof(message), + "This file system (%s) is not bootable " + "on this system. Are you sure you want to proceed?", + items[0].value); + conf.button.default_cancel = true; + conf.title = "Warning"; + choice = bsddialog_yesno(&conf, message, 0, 0); + conf.button.default_cancel = false; + if (choice == BSDDIALOG_CANCEL) + goto addpartform; + } + + /* + * If this is the root partition, and we need a boot partition, ask + * the user to add one. + */ + + if ((strcmp(items[0].value, "freebsd") == 0 || + strcmp(items[2].value, "/") == 0) && bootpart_size(scheme) > 0) { + size_t bytes = add_boot_partition(geom, pp, scheme, + interactive); + + /* Now adjust the part we are really adding forward */ + if (bytes > 0) { + firstfree += bytes / sector; + size -= (bytes + stripe)/sector; + if (stripe > 0 && (firstfree*sector % stripe) != 0) + firstfree += (stripe - ((firstfree*sector) % + stripe)) / sector; + } + } + + output[0] = '\0'; + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, geom->lg_name); + gctl_ro_param(r, "flags", -1, GPART_FLAGS); + gctl_ro_param(r, "verb", -1, "add"); + + gctl_ro_param(r, "type", -1, items[0].value); + snprintf(sizestr, sizeof(sizestr), "%jd", size); + gctl_ro_param(r, "size", -1, sizestr); + snprintf(startstr, sizeof(startstr), "%jd", firstfree); + gctl_ro_param(r, "start", -1, startstr); + if (items[3].value != NULL && items[3].value[0] != '\0') + gctl_ro_param(r, "label", -1, items[3].value); + gctl_add_param(r, "output", sizeof(output), output, + GCTL_PARAM_WR | GCTL_PARAM_ASCII); + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') { + gpart_show_error("Error", NULL, errstr); + gctl_free(r); + goto addpartform; + } + newpartname = strtok(output, " "); + gctl_free(r); + + /* + * Try to destroy any geom that gpart picked up already here from + * dirty blocks. + */ + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, newpartname); + gctl_ro_param(r, "flags", -1, GPART_FLAGS); + junk = 1; + gctl_ro_param(r, "force", sizeof(junk), &junk); + gctl_ro_param(r, "verb", -1, "destroy"); + gctl_issue(r); /* Error usually expected and non-fatal */ + gctl_free(r); + + + if (strcmp(items[0].value, "freebsd") == 0) + gpart_partition(newpartname, "BSD"); + else + set_default_part_metadata(newpartname, scheme, + items[0].value, items[2].value, newfs); + free(newfs); + + for (i = 0; i < nitems(items); i++) { + if (items[i].value != NULL) { + free(items[i].value); + if (init_allocated && items[i].init != NULL) + free((char*)items[i].init); + } + } + + if (partname != NULL) + *partname = strdup(newpartname); +} + +void +gpart_delete(struct gprovider *pp) +{ + struct gconfig *gc; + struct ggeom *geom; + struct gconsumer *cp; + struct gctl_req *r; + const char *errstr; + intmax_t idx; + int is_partition; + struct bsddialog_conf conf; + + /* Is it a partition? */ + is_partition = (strcmp(pp->lg_geom->lg_class->lg_name, "PART") == 0); + + /* Find out if this is the root of a gpart geom */ + geom = NULL; + LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) + if (strcmp(cp->lg_geom->lg_class->lg_name, "PART") == 0) { + geom = cp->lg_geom; + break; + } + + /* If so, destroy all children */ + if (geom != NULL) { + gpart_destroy(geom); + + /* If this is a partition, revert it, so it can be deleted */ + if (is_partition) { + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, geom->lg_name); + gctl_ro_param(r, "verb", -1, "undo"); + gctl_issue(r); /* Ignore non-fatal errors */ + gctl_free(r); + } + } + + /* + * If this is not a partition, see if that is a problem, complain if + * necessary, and return always, since we need not do anything further, + * error or no. + */ + if (!is_partition) { + if (geom == NULL) { + bsddialog_initconf(&conf); + conf.title = "Error"; + bsddialog_msgbox(&conf, + "Only partitions can be deleted.", 0, 0); + } + return; + } + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, pp->lg_geom->lg_class->lg_name); + gctl_ro_param(r, "arg0", -1, pp->lg_geom->lg_name); + gctl_ro_param(r, "flags", -1, GPART_FLAGS); + gctl_ro_param(r, "verb", -1, "delete"); + + LIST_FOREACH(gc, &pp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "index") == 0) { + idx = atoi(gc->lg_val); + gctl_ro_param(r, "index", sizeof(idx), &idx); + break; + } + } + + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') { + gpart_show_error("Error", NULL, errstr); + gctl_free(r); + return; + } + + gctl_free(r); + + delete_part_metadata(pp->lg_name); +} + +void +gpart_revert_all(struct gmesh *mesh) +{ + struct gclass *classp; + struct gconfig *gc; + struct ggeom *gp; + struct gctl_req *r; + const char *modified; + struct bsddialog_conf conf; + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) { + if (strcmp(classp->lg_name, "PART") == 0) + break; + } + + if (strcmp(classp->lg_name, "PART") != 0) { + bsddialog_initconf(&conf); + conf.title = "Error"; + bsddialog_msgbox(&conf, "gpart not found!", 0, 0); + return; + } + + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + modified = "true"; /* XXX: If we don't know (kernel too old), + * assume there are modifications. */ + LIST_FOREACH(gc, &gp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "modified") == 0) { + modified = gc->lg_val; + break; + } + } + + if (strcmp(modified, "false") == 0) + continue; + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, gp->lg_name); + gctl_ro_param(r, "verb", -1, "undo"); + + gctl_issue(r); + gctl_free(r); + } +} + +void +gpart_commit(struct gmesh *mesh) +{ + struct partition_metadata *md; + struct gclass *classp; + struct ggeom *gp; + struct gconfig *gc; + struct gconsumer *cp; + struct gprovider *pp; + struct gctl_req *r; + const char *errstr; + const char *modified; + const char *rootfs; + struct bsddialog_conf conf; + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) { + if (strcmp(classp->lg_name, "PART") == 0) + break; + } + + /* Figure out what filesystem / uses */ + rootfs = "ufs"; /* Assume ufs if nothing else present */ + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) { + rootfs = md->fstab->fs_vfstype; + break; + } + } + + if (strcmp(classp->lg_name, "PART") != 0) { + bsddialog_initconf(&conf); + conf.title = "Error"; + bsddialog_msgbox(&conf, "gpart not found!", 0, 0); + return; + } + + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + modified = "true"; /* XXX: If we don't know (kernel too old), + * assume there are modifications. */ + LIST_FOREACH(gc, &gp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "modified") == 0) { + modified = gc->lg_val; + break; + } + } + + if (strcmp(modified, "false") == 0) + continue; + + /* Add bootcode if necessary, before the commit */ + md = get_part_metadata(gp->lg_name, 0); + if (md != NULL && md->bootcode) + gpart_bootcode(gp); + + /* Now install partcode on its partitions, if necessary */ + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + md = get_part_metadata(pp->lg_name, 0); + if (md == NULL || !md->bootcode) + continue; + + /* Mark this partition active if that's required */ + gpart_activate(pp); + + /* Check if the partition has sub-partitions */ + LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) + if (strcmp(cp->lg_geom->lg_class->lg_name, + "PART") == 0) + break; + + if (cp == NULL) /* No sub-partitions */ + gpart_partcode(pp, rootfs); + } + + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, "PART"); + gctl_ro_param(r, "arg0", -1, gp->lg_name); + gctl_ro_param(r, "verb", -1, "commit"); + + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') + gpart_show_error("Error", NULL, errstr); + gctl_free(r); + } +} + diff --git a/usr.sbin/bsdinstall/partedit/part_wizard.c b/usr.sbin/bsdinstall/partedit/part_wizard.c new file mode 100644 index 000000000000..9146a2af782f --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/part_wizard.c @@ -0,0 +1,428 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/sysctl.h> + +#include <errno.h> +#include <inttypes.h> +#include <libutil.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libgeom.h> +#include <bsddialog.h> + +#include "partedit.h" + +#define MIN_FREE_SPACE (1023*1024*1024) /* Just under 1 GB */ + +static char *wizard_partition(struct gmesh *mesh, const char *disk); + +/* + * Determine default swap (partition) size in bytes for a given amount of free + * disk space in bytes. The algorithm should likely be revisited in light of + * contemporary memory and disk sizes. + */ +static intmax_t +swap_size(intmax_t available) +{ + intmax_t swapsize; + unsigned long swap_maxpages; + size_t sz; + + swapsize = MIN(available/20, 4*1024*1024*1024LL); + sz = sizeof(swap_maxpages); + if (sysctlbyname("vm.swap_maxpages", &swap_maxpages, &sz, NULL, 0) == 0) + swapsize = MIN(swapsize, (intmax_t)swap_maxpages * getpagesize()); + + return (swapsize); +} + +int +part_wizard(const char *fsreq) +{ + char *disk, *schemeroot; + const char *fstype; + struct gmesh mesh; + int error; + struct bsddialog_conf conf; + + bsddialog_initconf(&conf); + + if (fsreq != NULL) + fstype = fsreq; + else + fstype = "ufs"; + +startwizard: + error = geom_gettree(&mesh); + if (error != 0) + return (1); + + bsddialog_backtitle(&conf, OSNAME " Installer"); + disk = boot_disk_select(&mesh); + if (disk == NULL) { + geom_deletetree(&mesh); + return (1); + } + + bsddialog_clear(0); + bsddialog_backtitle(&conf, OSNAME " Installer"); + schemeroot = wizard_partition(&mesh, disk); + free(disk); + geom_deletetree(&mesh); + if (schemeroot == NULL) + return (1); + + bsddialog_clear(0); + bsddialog_backtitle(&conf, OSNAME " Installer"); + error = geom_gettree(&mesh); + if (error != 0) { + free(schemeroot); + return (1); + } + + error = wizard_makeparts(&mesh, schemeroot, fstype, 1); + free(schemeroot); + geom_deletetree(&mesh); + if (error) + goto startwizard; + + return (0); +} + +char * +boot_disk_select(struct gmesh *mesh) +{ + struct gclass *classp; + struct gconfig *gc; + struct ggeom *gp; + struct gprovider *pp; + struct bsddialog_menuitem *disks = NULL; + const char *type, *desc; + char diskdesc[512]; + char *chosen; + int i, button, fd, selected, n = 0; + struct bsddialog_conf conf; + + bsddialog_initconf(&conf); + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) { + if (strcmp(classp->lg_name, "DISK") != 0 && + strcmp(classp->lg_name, "RAID") != 0 && + strcmp(classp->lg_name, "MD") != 0) + continue; + + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + if (LIST_EMPTY(&gp->lg_provider)) + continue; + + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + desc = type = NULL; + LIST_FOREACH(gc, &pp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "type") == 0) + type = gc->lg_val; + if (strcmp(gc->lg_name, "descr") == 0) + desc = gc->lg_val; + } + + /* Skip swap-backed md and WORM devices */ + if (strcmp(classp->lg_name, "MD") == 0 && + type != NULL && strcmp(type, "swap") == 0) + continue; + if (strncmp(pp->lg_name, "cd", 2) == 0) + continue; + /* + * Check if the disk is available to be opened for + * write operations, it helps prevent the USB + * stick used to boot from being listed as an option + */ + fd = g_open(pp->lg_name, 1); + if (fd == -1) { + continue; + } + g_close(fd); + + disks = realloc(disks, (++n)*sizeof(disks[0])); + disks[n-1].name = pp->lg_name; + humanize_number(diskdesc, 7, pp->lg_mediasize, + "B", HN_AUTOSCALE, HN_DECIMAL); + if (strncmp(pp->lg_name, "ad", 2) == 0) + strcat(diskdesc, " ATA Hard Disk"); + else if (strncmp(pp->lg_name, "md", 2) == 0) + strcat(diskdesc, " Memory Disk"); + else + strcat(diskdesc, " Disk"); + + if (desc != NULL) + snprintf(diskdesc, sizeof(diskdesc), + "%s <%s>", diskdesc, desc); + + disks[n-1].prefix = ""; + disks[n-1].on = false; + disks[n-1].depth = 0; + disks[n-1].desc = strdup(diskdesc); + disks[n-1].bottomdesc = ""; + } + } + } + + if (n > 1) { + conf.title = "Partitioning"; + button = bsddialog_menu(&conf, + "Select the disk on which to install " OSNAME ".", 0, 0, 0, + n, disks, &selected); + + chosen = (button == BSDDIALOG_OK) ? + strdup(disks[selected].name) : NULL; + } else if (n == 1) { + chosen = strdup(disks[0].name); + } else { + chosen = NULL; + } + + for (i = 0; i < n; i++) + free((char*)disks[i].desc); + + return (chosen); +} + +static struct gprovider * +provider_for_name(struct gmesh *mesh, const char *name) +{ + struct gclass *classp; + struct gprovider *pp = NULL; + struct ggeom *gp; + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) { + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + if (LIST_EMPTY(&gp->lg_provider)) + continue; + + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) + if (strcmp(pp->lg_name, name) == 0) + break; + + if (pp != NULL) break; + } + + if (pp != NULL) break; + } + + return (pp); +} + +static char * +wizard_partition(struct gmesh *mesh, const char *disk) +{ + struct gclass *classp; + struct ggeom *gpart = NULL; + struct gconfig *gc; + char *retval = NULL; + const char *scheme = NULL; + char message[512]; + int choice; + struct bsddialog_conf conf; + + bsddialog_initconf(&conf); + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) + if (strcmp(classp->lg_name, "PART") == 0) + break; + + if (classp != NULL) { + LIST_FOREACH(gpart, &classp->lg_geom, lg_geom) + if (strcmp(gpart->lg_name, disk) == 0) + break; + } + + if (gpart != NULL) { + LIST_FOREACH(gc, &gpart->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) { + scheme = gc->lg_val; + break; + } + } + } + + /* Treat uncommitted scheme deletions as no scheme */ + if (scheme != NULL && strcmp(scheme, "(none)") == 0) + scheme = NULL; + +query: + conf.button.ok_label = "Entire Disk"; + conf.button.cancel_label = "Partition"; + if (gpart != NULL) + conf.button.default_cancel = true; + + snprintf(message, sizeof(message), "Would you like to use this entire " + "disk (%s) for " OSNAME " or partition it to share it with other " + "operating systems? Using the entire disk will erase any data " + "currently stored there.", disk); + conf.title = "Partition"; + choice = bsddialog_yesno(&conf, message, 9, 45); + + conf.button.ok_label = NULL; + conf.button.cancel_label = NULL; + conf.button.default_cancel = false; + + if (choice == BSDDIALOG_NO && scheme != NULL && !is_scheme_bootable(scheme)) { + char warning[512]; + int subchoice; + + snprintf(warning, sizeof(warning), + "The existing partition scheme on this " + "disk (%s) is not bootable on this platform. To install " + OSNAME ", it must be repartitioned. This will destroy all " + "data on the disk. Are you sure you want to proceed?", + scheme); + conf.title = "Non-bootable Disk"; + subchoice = bsddialog_yesno(&conf, warning, 0, 0); + if (subchoice != BSDDIALOG_YES) + goto query; + + gpart_destroy(gpart); + scheme = choose_part_type(default_scheme()); + if (scheme == NULL) + return NULL; + gpart_partition(disk, scheme); + } + + if (scheme == NULL || choice == 0) { + if (gpart != NULL && scheme != NULL) { + /* Erase partitioned disk */ + conf.title = "Confirmation"; + choice = bsddialog_yesno(&conf, "This will erase " + "the disk. Are you sure you want to proceed?", 0, 0); + if (choice != BSDDIALOG_YES) + goto query; + + gpart_destroy(gpart); + } + + scheme = choose_part_type(default_scheme()); + if (scheme == NULL) + return NULL; + gpart_partition(disk, scheme); + } + + if (strcmp(scheme, "MBR") == 0) { + struct gmesh submesh; + + if (geom_gettree(&submesh) == 0) { + gpart_create(provider_for_name(&submesh, disk), + "freebsd", NULL, NULL, &retval, + choice /* Non-interactive for "Entire Disk" */); + geom_deletetree(&submesh); + } + } else { + retval = strdup(disk); + } + + return (retval); +} + +int +wizard_makeparts(struct gmesh *mesh, const char *disk, const char *fstype, + int interactive) +{ + struct gclass *classp; + struct ggeom *gp; + struct gprovider *pp; + char *fsnames[] = {"freebsd-ufs", "freebsd-zfs"}; + char *fsname; + struct gmesh submesh; + char swapsizestr[10], rootsizestr[10]; + intmax_t swapsize, available; + int error, retval; + struct bsddialog_conf conf; + + if (strcmp(fstype, "zfs") == 0) { + fsname = fsnames[1]; + } else { + /* default to UFS */ + fsname = fsnames[0]; + } + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) + if (strcmp(classp->lg_name, "PART") == 0) + break; + + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) + if (strcmp(gp->lg_name, disk) == 0) + break; + + pp = provider_for_name(mesh, disk); + + available = gpart_max_free(gp, NULL)*pp->lg_sectorsize; + if (interactive && available < MIN_FREE_SPACE) { + char availablestr[10], neededstr[10], message[512]; + humanize_number(availablestr, 7, available, "B", HN_AUTOSCALE, + HN_DECIMAL); + humanize_number(neededstr, 7, MIN_FREE_SPACE, "B", HN_AUTOSCALE, + HN_DECIMAL); + snprintf(message, sizeof(message), + "There is not enough free space on %s to " + "install " OSNAME " (%s free, %s required). Would you like " + "to choose another disk or to open the partition editor?", + disk, availablestr, neededstr); + + bsddialog_initconf(&conf); + conf.button.ok_label = "Another Disk"; + conf.button.cancel_label = "Editor"; + conf.title = "Warning"; + retval = bsddialog_yesno(&conf, message, 0, 0); + + return (!retval); /* Editor -> return 0 */ + } + + swapsize = swap_size(available); + humanize_number(swapsizestr, 7, swapsize, "B", HN_AUTOSCALE, + HN_NOSPACE | HN_DECIMAL); + humanize_number(rootsizestr, 7, available - swapsize - 1024*1024, + "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); + + error = geom_gettree(&submesh); + if (error != 0) + return (error); + pp = provider_for_name(&submesh, disk); + gpart_create(pp, fsname, rootsizestr, "/", NULL, 0); + geom_deletetree(&submesh); + + error = geom_gettree(&submesh); + if (error != 0) + return (error); + pp = provider_for_name(&submesh, disk); + gpart_create(pp, "freebsd-swap", swapsizestr, NULL, NULL, 0); + geom_deletetree(&submesh); + + return (0); +} diff --git a/usr.sbin/bsdinstall/partedit/partedit.c b/usr.sbin/bsdinstall/partedit/partedit.c new file mode 100644 index 000000000000..148cb74edc75 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/partedit.c @@ -0,0 +1,628 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> + +#include <bsddialog.h> +#include <err.h> +#include <errno.h> +#include <fstab.h> +#include <inttypes.h> +#include <libgeom.h> +#include <libutil.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include "diskmenu.h" +#include "partedit.h" + +struct pmetadata_head part_metadata; +static int sade_mode = 0; + +static int apply_changes(struct gmesh *mesh); +static void apply_workaround(struct gmesh *mesh); +static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems); +static void add_geom_children(struct ggeom *gp, int recurse, + struct partedit_item **items, int *nitems); +static void init_fstab_metadata(void); +static void get_mount_points(struct partedit_item *items, int nitems); +static int validate_setup(void); + +static void +sigint_handler(int sig) +{ + struct gmesh mesh; + + /* Revert all changes and exit dialog-mode cleanly on SIGINT */ + if (geom_gettree(&mesh) == 0) { + gpart_revert_all(&mesh); + geom_deletetree(&mesh); + } + + bsddialog_end(); + + exit(1); +} + +int +main(int argc, const char **argv) +{ + struct partition_metadata *md; + const char *progname, *prompt; + struct partedit_item *items = NULL; + struct gmesh mesh; + int i, op, nitems; + int error; + struct bsddialog_conf conf; + + progname = getprogname(); + if (strcmp(progname, "sade") == 0) + sade_mode = 1; + + TAILQ_INIT(&part_metadata); + + init_fstab_metadata(); + + if (bsddialog_init() == BSDDIALOG_ERROR) + err(1, "%s", bsddialog_geterror()); + bsddialog_initconf(&conf); + if (!sade_mode) + bsddialog_backtitle(&conf, OSNAME " Installer"); + i = 0; + + /* Revert changes on SIGINT */ + signal(SIGINT, sigint_handler); + + if (strcmp(progname, "autopart") == 0) { /* Guided */ + prompt = "Please review the disk setup. When complete, press " + "the Finish button."; + /* Experimental ZFS autopartition support */ + if (argc > 1 && strcmp(argv[1], "zfs") == 0) { + part_wizard("zfs"); + } else { + part_wizard("ufs"); + } + } else if (strcmp(progname, "scriptedpart") == 0) { + error = scripted_editor(argc, argv); + prompt = NULL; + if (error != 0) { + bsddialog_end(); + return (error); + } + } else { + prompt = "Create partitions for " OSNAME ", F1 for help.\n" + "No changes will be made until you select Finish."; + } + + /* Show the part editor either immediately, or to confirm wizard */ + while (prompt != NULL) { + bsddialog_clear(0); + if (!sade_mode) + bsddialog_backtitle(&conf, OSNAME " Installer"); + + error = geom_gettree(&mesh); + if (error == 0) + items = read_geom_mesh(&mesh, &nitems); + if (error || items == NULL) { + conf.title = "Error"; + bsddialog_msgbox(&conf, "No disks found. If you need " + "to install a kernel driver, choose Shell at the " + "installation menu.", 0, 0); + break; + } + + get_mount_points(items, nitems); + + if (i >= nitems) + i = nitems - 1; + op = diskmenu_show("Partition Editor", prompt, items, nitems, + &i); + + switch (op) { + case BUTTON_CREATE: + gpart_create((struct gprovider *)(items[i].cookie), + NULL, NULL, NULL, NULL, 1); + break; + case BUTTON_DELETE: + gpart_delete((struct gprovider *)(items[i].cookie)); + break; + case BUTTON_MODIFY: + gpart_edit((struct gprovider *)(items[i].cookie)); + break; + case BUTTON_REVERT: + gpart_revert_all(&mesh); + while ((md = TAILQ_FIRST(&part_metadata)) != NULL) { + if (md->fstab != NULL) { + free(md->fstab->fs_spec); + free(md->fstab->fs_file); + free(md->fstab->fs_vfstype); + free(md->fstab->fs_mntops); + free(md->fstab->fs_type); + free(md->fstab); + } + if (md->newfs != NULL) + free(md->newfs); + free(md->name); + + TAILQ_REMOVE(&part_metadata, md, metadata); + free(md); + } + init_fstab_metadata(); + break; + case BUTTON_AUTO: + part_wizard("ufs"); + break; + } + + error = 0; + if (op == BUTTON_FINISH) { + conf.button.ok_label = "Commit"; + conf.button.with_extra = true; + conf.button.extra_label = "Revert & Exit"; + conf.button.cancel_label = "Back"; + conf.title = "Confirmation"; + op = bsddialog_yesno(&conf, "Your changes will now be " + "written to disk. If you have chosen to overwrite " + "existing data, it will be PERMANENTLY ERASED. Are " + "you sure you want to commit your changes?", 0, 0); + conf.button.ok_label = NULL; + conf.button.with_extra = false; + conf.button.extra_label = NULL; + conf.button.cancel_label = NULL; + + if (op == BSDDIALOG_OK && validate_setup()) { /* Save */ + error = apply_changes(&mesh); + if (!error) + apply_workaround(&mesh); + break; + } else if (op == BSDDIALOG_EXTRA) { /* Quit */ + gpart_revert_all(&mesh); + error = -1; + break; + } + } + + geom_deletetree(&mesh); + free(items); + } + + if (prompt == NULL) { + error = geom_gettree(&mesh); + if (error == 0) { + if (validate_setup()) { + error = apply_changes(&mesh); + } else { + gpart_revert_all(&mesh); + error = -1; + } + geom_deletetree(&mesh); + } + } + + bsddialog_end(); + + return (error); +} + +struct partition_metadata * +get_part_metadata(const char *name, int create) +{ + struct partition_metadata *md; + + TAILQ_FOREACH(md, &part_metadata, metadata) + if (md->name != NULL && strcmp(md->name, name) == 0) + break; + + if (md == NULL && create) { + md = calloc(1, sizeof(*md)); + md->name = strdup(name); + TAILQ_INSERT_TAIL(&part_metadata, md, metadata); + } + + return (md); +} + +void +delete_part_metadata(const char *name) +{ + struct partition_metadata *md; + + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->name != NULL && strcmp(md->name, name) == 0) { + if (md->fstab != NULL) { + free(md->fstab->fs_spec); + free(md->fstab->fs_file); + free(md->fstab->fs_vfstype); + free(md->fstab->fs_mntops); + free(md->fstab->fs_type); + free(md->fstab); + } + if (md->newfs != NULL) + free(md->newfs); + free(md->name); + + TAILQ_REMOVE(&part_metadata, md, metadata); + free(md); + break; + } + } +} + +static int +validate_setup(void) +{ + struct partition_metadata *md, *root = NULL; + int button; + struct bsddialog_conf conf; + + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) + root = md; + + /* XXX: Check for duplicate mountpoints */ + } + + bsddialog_initconf(&conf); + + if (root == NULL) { + conf.title = "Error"; + bsddialog_msgbox(&conf, "No root partition was found. " + "The root " OSNAME " partition must have a mountpoint " + "of '/'.", 0, 0); + return (false); + } + + /* + * Check for root partitions that we aren't formatting, which is + * usually a mistake + */ + if (root->newfs == NULL && !sade_mode) { + conf.button.default_cancel = true; + conf.title = "Warning"; + button = bsddialog_yesno(&conf, "The chosen root partition " + "has a preexisting filesystem. If it contains an existing " + OSNAME " system, please update it with freebsd-update " + "instead of installing a new system on it. The partition " + "can also be erased by pressing \"No\" and then deleting " + "and recreating it. Are you sure you want to proceed?", + 0, 0); + if (button == BSDDIALOG_CANCEL) + return (false); + } + + return (true); +} + +static int +mountpoint_sorter(const void *xa, const void *xb) +{ + struct partition_metadata *a = *(struct partition_metadata **)xa; + struct partition_metadata *b = *(struct partition_metadata **)xb; + + if (a->fstab == NULL && b->fstab == NULL) + return 0; + if (a->fstab == NULL) + return 1; + if (b->fstab == NULL) + return -1; + + return strcmp(a->fstab->fs_file, b->fstab->fs_file); +} + +static int +apply_changes(struct gmesh *mesh) +{ + struct partition_metadata *md; + char message[512]; + int i, nitems, error, *miniperc; + const char **minilabel; + const char *fstab_path; + FILE *fstab; + char *command; + struct bsddialog_conf conf; + + nitems = 1; /* Partition table changes */ + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->newfs != NULL) + nitems++; + } + minilabel = calloc(nitems, sizeof(const char *)); + miniperc = calloc(nitems, sizeof(int)); + minilabel[0] = "Writing partition tables"; + miniperc[0] = BSDDIALOG_MG_INPROGRESS; + i = 1; + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->newfs != NULL) { + char *item; + + asprintf(&item, "Initializing %s", md->name); + minilabel[i] = item; + miniperc[i] = BSDDIALOG_MG_PENDING; + i++; + } + } + + i = 0; + bsddialog_initconf(&conf); + conf.title = "Initializing"; + bsddialog_mixedgauge(&conf, + "Initializing file systems. Please wait.", 0, 0, i * 100 / nitems, + nitems, minilabel, miniperc); + gpart_commit(mesh); + miniperc[i] = BSDDIALOG_MG_COMPLETED; + i++; + + if (getenv("BSDINSTALL_LOG") == NULL) + setenv("BSDINSTALL_LOG", "/dev/null", 1); + + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->newfs != NULL) { + miniperc[i] = BSDDIALOG_MG_INPROGRESS; + bsddialog_mixedgauge(&conf, + "Initializing file systems. Please wait.", 0, 0, + i * 100 / nitems, nitems, minilabel, miniperc); + asprintf(&command, "(echo %s; %s) >>%s 2>>%s", + md->newfs, md->newfs, getenv("BSDINSTALL_LOG"), + getenv("BSDINSTALL_LOG")); + error = system(command); + free(command); + miniperc[i] = (error == 0) ? + BSDDIALOG_MG_COMPLETED : BSDDIALOG_MG_FAILED; + i++; + } + } + bsddialog_mixedgauge(&conf, "Initializing file systems. Please wait.", + 0, 0, i * 100 / nitems, nitems, minilabel, miniperc); + + for (i = 1; i < nitems; i++) + free(__DECONST(char *, minilabel[i])); + + free(minilabel); + free(miniperc); + + /* Sort filesystems for fstab so that mountpoints are ordered */ + { + struct partition_metadata **tobesorted; + struct partition_metadata *tmp; + int nparts = 0; + TAILQ_FOREACH(md, &part_metadata, metadata) + nparts++; + tobesorted = malloc(sizeof(struct partition_metadata *)*nparts); + nparts = 0; + TAILQ_FOREACH_SAFE(md, &part_metadata, metadata, tmp) { + tobesorted[nparts++] = md; + TAILQ_REMOVE(&part_metadata, md, metadata); + } + qsort(tobesorted, nparts, sizeof(tobesorted[0]), + mountpoint_sorter); + + /* Now re-add everything */ + while (nparts-- > 0) + TAILQ_INSERT_HEAD(&part_metadata, + tobesorted[nparts], metadata); + free(tobesorted); + } + + if (getenv("PATH_FSTAB") != NULL) + fstab_path = getenv("PATH_FSTAB"); + else + fstab_path = "/etc/fstab"; + fstab = fopen(fstab_path, "w+"); + if (fstab == NULL) { + snprintf(message, sizeof(message), + "Cannot open fstab file %s for writing (%s)\n", + getenv("PATH_FSTAB"), strerror(errno)); + conf.title = "Error"; + bsddialog_msgbox(&conf, message, 0, 0); + return (-1); + } + fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n"); + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->fstab != NULL) + fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n", + md->fstab->fs_spec, md->fstab->fs_file, + md->fstab->fs_vfstype, md->fstab->fs_mntops, + md->fstab->fs_freq, md->fstab->fs_passno); + } + fclose(fstab); + + return (0); +} + +static void +apply_workaround(struct gmesh *mesh) +{ + struct gclass *classp; + struct ggeom *gp; + struct gconfig *gc; + const char *scheme = NULL, *modified = NULL; + struct bsddialog_conf conf; + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) { + if (strcmp(classp->lg_name, "PART") == 0) + break; + } + + if (strcmp(classp->lg_name, "PART") != 0) { + bsddialog_initconf(&conf); + conf.title = "Error"; + bsddialog_msgbox(&conf, "gpart not found!", 0, 0); + return; + } + + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + LIST_FOREACH(gc, &gp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) { + scheme = gc->lg_val; + } else if (strcmp(gc->lg_name, "modified") == 0) { + modified = gc->lg_val; + } + } + + if (scheme && strcmp(scheme, "GPT") == 0 && + modified && strcmp(modified, "true") == 0) { + if (getenv("WORKAROUND_LENOVO")) + gpart_set_root(gp->lg_name, "lenovofix"); + if (getenv("WORKAROUND_GPTACTIVE")) + gpart_set_root(gp->lg_name, "active"); + } + } +} + +static struct partedit_item * +read_geom_mesh(struct gmesh *mesh, int *nitems) +{ + struct gclass *classp; + struct ggeom *gp; + struct partedit_item *items; + + *nitems = 0; + items = NULL; + + /* + * Build the device table. First add all disks (and CDs). + */ + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) { + if (strcmp(classp->lg_name, "DISK") != 0 && + strcmp(classp->lg_name, "MD") != 0) + continue; + + /* Now recurse into all children */ + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) + add_geom_children(gp, 0, &items, nitems); + } + + return (items); +} + +static void +add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items, + int *nitems) +{ + struct gconsumer *cp; + struct gprovider *pp; + struct gconfig *gc; + + if (strcmp(gp->lg_class->lg_name, "PART") == 0 && + !LIST_EMPTY(&gp->lg_config)) { + LIST_FOREACH(gc, &gp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "scheme") == 0) + (*items)[*nitems-1].type = gc->lg_val; + } + } + + if (LIST_EMPTY(&gp->lg_provider)) + return; + + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + if (strcmp(gp->lg_class->lg_name, "LABEL") == 0) + continue; + + /* Skip WORM media */ + if (strncmp(pp->lg_name, "cd", 2) == 0) + continue; + + *items = realloc(*items, + (*nitems+1)*sizeof(struct partedit_item)); + (*items)[*nitems].indentation = recurse; + (*items)[*nitems].name = pp->lg_name; + (*items)[*nitems].size = pp->lg_mediasize; + (*items)[*nitems].mountpoint = NULL; + (*items)[*nitems].type = ""; + (*items)[*nitems].cookie = pp; + + LIST_FOREACH(gc, &pp->lg_config, lg_config) { + if (strcmp(gc->lg_name, "type") == 0) + (*items)[*nitems].type = gc->lg_val; + } + + /* Skip swap-backed MD devices */ + if (strcmp(gp->lg_class->lg_name, "MD") == 0 && + strcmp((*items)[*nitems].type, "swap") == 0) + continue; + + (*nitems)++; + + LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) + add_geom_children(cp->lg_geom, recurse+1, items, + nitems); + + /* Only use first provider for acd */ + if (strcmp(gp->lg_class->lg_name, "ACD") == 0) + break; + } +} + +static void +init_fstab_metadata(void) +{ + struct fstab *fstab; + struct partition_metadata *md; + + setfsent(); + while ((fstab = getfsent()) != NULL) { + md = calloc(1, sizeof(struct partition_metadata)); + + md->name = NULL; + if (strncmp(fstab->fs_spec, "/dev/", 5) == 0) + md->name = strdup(&fstab->fs_spec[5]); + + md->fstab = malloc(sizeof(struct fstab)); + md->fstab->fs_spec = strdup(fstab->fs_spec); + md->fstab->fs_file = strdup(fstab->fs_file); + md->fstab->fs_vfstype = strdup(fstab->fs_vfstype); + md->fstab->fs_mntops = strdup(fstab->fs_mntops); + md->fstab->fs_type = strdup(fstab->fs_type); + md->fstab->fs_freq = fstab->fs_freq; + md->fstab->fs_passno = fstab->fs_passno; + + md->newfs = NULL; + + TAILQ_INSERT_TAIL(&part_metadata, md, metadata); + } +} + +static void +get_mount_points(struct partedit_item *items, int nitems) +{ + struct partition_metadata *md; + int i; + + for (i = 0; i < nitems; i++) { + TAILQ_FOREACH(md, &part_metadata, metadata) { + if (md->name != NULL && md->fstab != NULL && + strcmp(md->name, items[i].name) == 0) { + items[i].mountpoint = md->fstab->fs_file; + break; + } + } + } +} diff --git a/usr.sbin/bsdinstall/partedit/partedit.h b/usr.sbin/bsdinstall/partedit/partedit.h new file mode 100644 index 000000000000..57f590113f43 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/partedit.h @@ -0,0 +1,108 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PARTEDIT_PARTEDIT_H +#define _PARTEDIT_PARTEDIT_H + +#include <sys/queue.h> +#include <inttypes.h> +#include <fstab.h> + +#include "opt_osname.h" + +struct gprovider; +struct gmesh; +struct ggeom; + +TAILQ_HEAD(pmetadata_head, partition_metadata); +extern struct pmetadata_head part_metadata; + +struct partition_metadata { + char *name; /* name of this partition, as in GEOM */ + + struct fstab *fstab; /* fstab data for this partition */ + char *newfs; /* shell command to initialize partition */ + + int bootcode; + + TAILQ_ENTRY(partition_metadata) metadata; +}; + +struct partition_metadata *get_part_metadata(const char *name, int create); +void delete_part_metadata(const char *name); + +int part_wizard(const char *fstype); +int scripted_editor(int argc, const char **argv); +char *boot_disk_select(struct gmesh *mesh); +int wizard_makeparts(struct gmesh *mesh, const char *disk, const char *fstype, + int interactive); + +/* gpart operations */ +void gpart_delete(struct gprovider *pp); +void gpart_destroy(struct ggeom *lg_geom); +void gpart_edit(struct gprovider *pp); +void gpart_create(struct gprovider *pp, const char *default_type, + const char *default_size, const char *default_mountpoint, + char **output, int interactive); +intmax_t gpart_max_free(struct ggeom *gp, intmax_t *start); +void gpart_revert(struct gprovider *pp); +void gpart_revert_all(struct gmesh *mesh); +void gpart_commit(struct gmesh *mesh); +int gpart_partition(const char *lg_name, const char *scheme); +void set_default_part_metadata(const char *name, const char *scheme, + const char *type, const char *mountpoint, const char *newfs); +void gpart_set_root(const char *lg_name, const char *attribute); +const char *choose_part_type(const char *def_scheme); + +/* machine-dependent bootability checks */ +const char *default_scheme(void); /* Default partition scheme */ +int is_scheme_bootable(const char *scheme); /* Non-zero if scheme boots */ +int is_fs_bootable(const char *scheme, const char *fs); /* Ditto if FS boots */ + +/* Size of boot partition in bytes. Zero if no boot partition */ +size_t bootpart_size(const char *scheme); + +/* + * Type and mountpoint of boot partition for given scheme. If boot partition + * should not be mounted, set mountpoint to NULL or leave it unchanged. + * Note that mountpoint non-NULL implies partcode_path() will be ignored. + * Do *NOT* set both! + */ +const char *bootpart_type(const char *scheme, const char **mountpoint); + +/* Path to bootcode that goes in the scheme (e.g. disk MBR). NULL if none */ +const char *bootcode_path(const char *scheme); + +/* + * Path to boot blocks to be dd'ed into the partition suggested by bootpart_* + * for the given scheme and root filesystem type. If the boot partition should + * be mounted rather than dd'ed to, return NULL here. + */ +const char *partcode_path(const char *scheme, const char *fs_type); + +#endif diff --git a/usr.sbin/bsdinstall/partedit/partedit_efi.c b/usr.sbin/bsdinstall/partedit/partedit_efi.c new file mode 100644 index 000000000000..7c4f85a69b8b --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/partedit_efi.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 Cavium Inc. + * All rights reserved. + * + * Developed by Semihalf. + * Based on work by Nathan Whitehorn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <string.h> + +#include "partedit.h" + +/* + * partedit implementation for platforms on which the installer only offers + * UEFI-based boot. Currently, this includes arm64 and RISC-V. + */ + +/* EFI partition size in bytes */ +#define EFI_BOOTPART_SIZE (260 * 1024 * 1024) + +const char * +default_scheme(void) +{ + + return ("GPT"); +} + +int +is_scheme_bootable(const char *part_type) +{ + + if (strcmp(part_type, "GPT") == 0) + return (1); + + return (0); +} + +int +is_fs_bootable(const char *part_type, const char *fs) +{ + + if (strcmp(fs, "freebsd-ufs") == 0) + return (1); + + return (0); +} + +size_t +bootpart_size(const char *scheme) +{ + + /* We only support GPT with EFI */ + if (strcmp(scheme, "GPT") != 0) + return (0); + + return (EFI_BOOTPART_SIZE); +} + +const char * +bootpart_type(const char *scheme, const char **mountpoint) +{ + + /* Only EFI is supported as boot partition */ + *mountpoint = "/boot/efi"; + return ("efi"); +} + +const char * +bootcode_path(const char *part_type) +{ + + return (NULL); +} + +const char * +partcode_path(const char *part_type, const char *fs_type) +{ + + /* No boot partition data. */ + return (NULL); +} + diff --git a/usr.sbin/bsdinstall/partedit/partedit_generic.c b/usr.sbin/bsdinstall/partedit/partedit_generic.c new file mode 100644 index 000000000000..dd6a44b96e91 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/partedit_generic.c @@ -0,0 +1,79 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <string.h> + +#include "partedit.h" + +const char * +default_scheme(void) { + /* + * Our loader can parse GPT, so pick that as the default for lack of + * a better idea. + */ + + return ("GPT"); +} + +int +is_scheme_bootable(const char *part_type) { + /* + * We don't know anything about this platform, so don't irritate the + * user by claiming the chosen partition scheme isn't bootable. + */ + + return (1); +} + +int +is_fs_bootable(const char *part_type, const char *fs) { + return (1); +} + +/* No clue => no boot partition, bootcode, or partcode */ + +size_t +bootpart_size(const char *part_type) { + return (0); +} + +const char * +bootpart_type(const char *scheme, const char **mountpoint) { + return ("freebsd-boot"); +} + +const char * +bootcode_path(const char *part_type) { + return (NULL); +} + +const char * +partcode_path(const char *part_type, const char *fs_type) { + return (NULL); +} + diff --git a/usr.sbin/bsdinstall/partedit/partedit_powerpc.c b/usr.sbin/bsdinstall/partedit/partedit_powerpc.c new file mode 100644 index 000000000000..27fc9c2bde6e --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/partedit_powerpc.c @@ -0,0 +1,145 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/sysctl.h> +#include <string.h> + +#include "partedit.h" + +static char platform[255] = ""; + +const char * +default_scheme(void) { + size_t platlen = sizeof(platform); + if (strlen(platform) == 0) + sysctlbyname("hw.platform", platform, &platlen, NULL, -1); + + if (strcmp(platform, "powermac") == 0) + return ("APM"); + if (strcmp(platform, "chrp") == 0 || strcmp(platform, "ps3") == 0 || + strcmp(platform, "mpc85xx") == 0) + return ("MBR"); + + /* Pick GPT as a generic default */ + return ("GPT"); +} + +int +is_scheme_bootable(const char *part_type) { + size_t platlen = sizeof(platform); + if (strlen(platform) == 0) + sysctlbyname("hw.platform", platform, &platlen, NULL, -1); + + if (strcmp(platform, "powermac") == 0 && strcmp(part_type, "APM") == 0) + return (1); + if (strcmp(platform, "powernv") == 0 && strcmp(part_type, "GPT") == 0) + return (1); + if ((strcmp(platform, "chrp") == 0 || strcmp(platform, "ps3") == 0) && + (strcmp(part_type, "MBR") == 0 || strcmp(part_type, "BSD") == 0 || + strcmp(part_type, "GPT") == 0)) + return (1); + if (strcmp(platform, "mpc85xx") == 0 && strcmp(part_type, "MBR") == 0) + return (1); + + return (0); +} + +int +is_fs_bootable(const char *part_type, const char *fs) +{ + if (strcmp(fs, "freebsd-ufs") == 0) + return (1); + + return (0); +} + +size_t +bootpart_size(const char *part_type) +{ + size_t platlen = sizeof(platform); + if (strlen(platform) == 0) + sysctlbyname("hw.platform", platform, &platlen, NULL, -1); + + if (strcmp(part_type, "APM") == 0) + return (800*1024); + if (strcmp(part_type, "BSD") == 0) /* Nothing for nested */ + return (0); + if (strcmp(platform, "chrp") == 0) + return (800*1024); + if (strcmp(platform, "ps3") == 0 || strcmp(platform, "powernv") == 0) + return (512*1024*1024); + if (strcmp(platform, "mpc85xx") == 0) + return (16*1024*1024); + return (0); +} + +const char * +bootpart_type(const char *scheme, const char **mountpoint) +{ + size_t platlen = sizeof(platform); + if (strlen(platform) == 0) + sysctlbyname("hw.platform", platform, &platlen, NULL, -1); + + if (strcmp(platform, "chrp") == 0) + return ("prep-boot"); + if (strcmp(platform, "powermac") == 0) + return ("apple-boot"); + if (strcmp(platform, "powernv") == 0 || strcmp(platform, "ps3") == 0) { + *mountpoint = "/boot"; + if (strcmp(scheme, "GPT") == 0) + return ("ms-basic-data"); + else if (strcmp(scheme, "MBR") == 0) + return ("fat32"); + } + if (strcmp(platform, "mpc85xx") == 0) { + *mountpoint = "/boot/uboot"; + return ("fat16"); + } + + return ("freebsd-boot"); +} + +const char * +bootcode_path(const char *part_type) { + return (NULL); +} + +const char * +partcode_path(const char *part_type, const char *fs_type) { + size_t platlen = sizeof(platform); + if (strlen(platform) == 0) + sysctlbyname("hw.platform", platform, &platlen, NULL, -1); + + if (strcmp(part_type, "APM") == 0) + return ("/boot/boot1.hfs"); + if (strcmp(platform, "chrp") == 0) + return ("/boot/boot1.elf"); + return (NULL); +} + diff --git a/usr.sbin/bsdinstall/partedit/partedit_x86.c b/usr.sbin/bsdinstall/partedit/partedit_x86.c new file mode 100644 index 000000000000..6a536271c25e --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/partedit_x86.c @@ -0,0 +1,153 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/sysctl.h> +#include <string.h> + +#include "partedit.h" + +/* EFI partition size in bytes */ +#define EFI_BOOTPART_SIZE (260 * 1024 * 1024) + +static const char * +x86_bootmethod(void) +{ + static char fw[255] = ""; + size_t len = sizeof(fw); + int error; + + if (strlen(fw) == 0) { + error = sysctlbyname("machdep.bootmethod", fw, &len, NULL, -1); + if (error != 0) + return ("BIOS"); + } + + return (fw); +} + +const char * +default_scheme(void) +{ + if (strcmp(x86_bootmethod(), "UEFI") == 0) + return ("GPT"); + else + return ("MBR"); +} + +int +is_scheme_bootable(const char *part_type) +{ + + if (strcmp(part_type, "GPT") == 0) + return (1); + if (strcmp(x86_bootmethod(), "BIOS") == 0) { + if (strcmp(part_type, "BSD") == 0) + return (1); + if (strcmp(part_type, "MBR") == 0) + return (1); + } + + return (0); +} + +int +is_fs_bootable(const char *part_type, const char *fs) +{ + + if (strcmp(fs, "freebsd-ufs") == 0) + return (1); + + if (strcmp(fs, "freebsd-zfs") == 0 && + strcmp(part_type, "GPT") == 0 && + strcmp(x86_bootmethod(), "BIOS") == 0) + return (1); + + return (0); +} + +size_t +bootpart_size(const char *scheme) +{ + + /* No partcode except for GPT */ + if (strcmp(scheme, "GPT") != 0) + return (0); + + if (strcmp(x86_bootmethod(), "BIOS") == 0) + return (512*1024); + else + return (EFI_BOOTPART_SIZE); + + return (0); +} + +const char * +bootpart_type(const char *scheme, const char **mountpoint) +{ + + if (strcmp(x86_bootmethod(), "UEFI") == 0) { + *mountpoint = "/boot/efi"; + return ("efi"); + } + + return ("freebsd-boot"); +} + +const char * +bootcode_path(const char *part_type) +{ + + if (strcmp(x86_bootmethod(), "UEFI") == 0) + return (NULL); + + if (strcmp(part_type, "GPT") == 0) + return ("/boot/pmbr"); + if (strcmp(part_type, "MBR") == 0) + return ("/boot/mbr"); + if (strcmp(part_type, "BSD") == 0) + return ("/boot/boot"); + + return (NULL); +} + +const char * +partcode_path(const char *part_type, const char *fs_type) +{ + + if (strcmp(part_type, "GPT") == 0 && strcmp(x86_bootmethod(), "UEFI") != 0) { + if (strcmp(fs_type, "zfs") == 0) + return ("/boot/gptzfsboot"); + else + return ("/boot/gptboot"); + } + + /* No partcode except for non-UEFI GPT */ + return (NULL); +} + diff --git a/usr.sbin/bsdinstall/partedit/sade.8 b/usr.sbin/bsdinstall/partedit/sade.8 new file mode 100644 index 000000000000..2e676c19ba4b --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/sade.8 @@ -0,0 +1,71 @@ +.\" Copyright (c) 1997 +.\" Jordan Hubbard <jkh@FreeBSD.org>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Jordan Hubbard AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd December 30, 2012 +.Dt SADE 8 +.Os +.Sh NAME +.Nm sade +.Nd sysadmins disk editor +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility is used for various disk administration tasks on +.Fx +systems. +.Pp +It is generally invoked without arguments for the default +behavior, where the main menu is presented. +.Sh NOTES +The +.Nm +utility aims to provide a handy tool for disk management +tasks on an already installed system. +The goal is to provide the same text interface for disk management in +.Xr bsdinstall 8 +in the post-installation environment. +.Sh SEE ALSO +.Xr bsdinstall 8 , +.Xr gpart 8 +.Sh HISTORY +A program called +.Nm +first appeared in +.Fx 6.3 +as a utility encapsulating features from the +.Xr sysinstall 8 +installer. +It was replaced in +.Fx 10.0 +with the equivalent part of +.Xr bsdinstall 8 . +.Sh AUTHORS +.An Nathan Whitehorn Aq Mt nwhitehorn@FreeBSD.org +.Sh BUGS +The utility misses a lot of nice features, such as tools for +manipulating +.Xr gmirror 8 . +These will be added later. diff --git a/usr.sbin/bsdinstall/partedit/scripted.c b/usr.sbin/bsdinstall/partedit/scripted.c new file mode 100644 index 000000000000..9527f3828135 --- /dev/null +++ b/usr.sbin/bsdinstall/partedit/scripted.c @@ -0,0 +1,233 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2013 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> + +#include <ctype.h> +#include <libgeom.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "partedit.h" + +static struct gprovider * +provider_for_name(struct gmesh *mesh, const char *name) +{ + struct gclass *classp; + struct gprovider *pp = NULL; + struct ggeom *gp; + + LIST_FOREACH(classp, &mesh->lg_class, lg_class) { + LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { + if (LIST_EMPTY(&gp->lg_provider)) + continue; + + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) + if (strcmp(pp->lg_name, name) == 0) + break; + + if (pp != NULL) break; + } + + if (pp != NULL) break; + } + + return (pp); +} + +static int +part_config(char *disk, const char *scheme, char *config) +{ + char *partition, *ap, *size = NULL, *type = NULL, *mount = NULL; + struct gclass *classp; + struct gmesh mesh; + struct ggeom *gpart = NULL; + int error; + + if (scheme == NULL) + scheme = default_scheme(); + + error = geom_gettree(&mesh); + if (error != 0) + return (-1); + if (provider_for_name(&mesh, disk) == NULL) { + fprintf(stderr, "GEOM provider %s not found\n", disk); + geom_deletetree(&mesh); + return (-1); + } + + /* Remove any existing partitioning and create new scheme */ + LIST_FOREACH(classp, &mesh.lg_class, lg_class) + if (strcmp(classp->lg_name, "PART") == 0) + break; + if (classp != NULL) { + LIST_FOREACH(gpart, &classp->lg_geom, lg_geom) + if (strcmp(gpart->lg_name, disk) == 0) + break; + } + if (gpart != NULL) + gpart_destroy(gpart); + gpart_partition(disk, scheme); + + if (strcmp(scheme, "MBR") == 0) { + struct gmesh submesh; + + if (geom_gettree(&submesh) == 0) { + gpart_create(provider_for_name(&submesh, disk), + "freebsd", NULL, NULL, &disk, 0); + geom_deletetree(&submesh); + } + } else { + disk = strdup(disk); + } + + geom_deletetree(&mesh); + error = geom_gettree(&mesh); + if (error != 0) { + free(disk); + return (-1); + } + + /* Create partitions */ + if (config == NULL) { + wizard_makeparts(&mesh, disk, "ufs", 0); + goto finished; + } + + while ((partition = strsep(&config, ",")) != NULL) { + while ((ap = strsep(&partition, " \t\n")) != NULL) { + if (*ap == '\0') + continue; + if (size == NULL) + size = ap; + else if (type == NULL) + type = ap; + else if (mount == NULL) + mount = ap; + } + if (size == NULL) + continue; + if (strcmp(size, "auto") == 0) + size = NULL; + gpart_create(provider_for_name(&mesh, disk), type, size, mount, + NULL, 0); + geom_deletetree(&mesh); + error = geom_gettree(&mesh); + if (error != 0) { + free(disk); + return (-1); + } + size = type = mount = NULL; + } + +finished: + geom_deletetree(&mesh); + free(disk); + + return (0); +} + +static int +parse_disk_config(char *input) +{ + char *ap; + char *disk = NULL, *scheme = NULL, *partconfig = NULL; + + while (input != NULL && *input != 0) { + if (isspace(*input)) { + input++; + continue; + } + + switch(*input) { + case '{': + input++; + partconfig = strchr(input, '}'); + if (partconfig == NULL) { + fprintf(stderr, "Malformed partition setup " + "string: %s\n", input); + return (1); + } + *partconfig = '\0'; + ap = partconfig+1; + partconfig = input; + input = ap; + break; + default: + if (disk == NULL) + disk = strsep(&input, " \t\n"); + else if (scheme == NULL) + scheme = strsep(&input, " \t\n"); + else { + fprintf(stderr, "Unknown directive: %s\n", + strsep(&input, " \t\n")); + return (1); + } + } + } while (input != NULL && *input != 0); + + if (disk == NULL || strcmp(disk, "DEFAULT") == 0) { + struct gmesh mesh; + + if (geom_gettree(&mesh) == 0) { + disk = boot_disk_select(&mesh); + geom_deletetree(&mesh); + } + } + + return (part_config(disk, scheme, partconfig)); +} + +int +scripted_editor(int argc, const char **argv) +{ + FILE *fp; + char *input, *token; + size_t len; + int i, error = 0; + + fp = open_memstream(&input, &len); + fputs(argv[1], fp); + for (i = 2; i < argc; i++) { + fprintf(fp, " %s", argv[i]); + } + fclose(fp); + + while ((token = strsep(&input, ";")) != NULL) { + error = parse_disk_config(token); + if (error != 0) { + free(input); + return (error); + } + } + free(input); + + return (0); +} + |
