aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/bsdinstall/partedit/partedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bsdinstall/partedit/partedit.c')
-rw-r--r--usr.sbin/bsdinstall/partedit/partedit.c628
1 files changed, 628 insertions, 0 deletions
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;
+ }
+ }
+ }
+}