Path: blob/main/usr.sbin/bsdinstall/partedit/partedit.c
103478 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2011 Nathan Whitehorn4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>2930#include <bsddialog.h>31#include <err.h>32#include <errno.h>33#include <fstab.h>34#include <inttypes.h>35#include <libgeom.h>36#include <libutil.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <sysexits.h>4142#include "diskmenu.h"43#include "partedit.h"4445struct pmetadata_head part_metadata;46static int sade_mode = 0;4748static int apply_changes(struct gmesh *mesh);49static void apply_workaround(struct gmesh *mesh);50static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems);51static void add_geom_children(struct ggeom *gp, int recurse,52struct partedit_item **items, int *nitems);53static void init_fstab_metadata(void);54static void get_mount_points(struct partedit_item *items, int nitems);55static int validate_setup(void);5657static void58sigint_handler(int sig)59{60struct gmesh mesh;6162/* Revert all changes and exit dialog-mode cleanly on SIGINT */63if (geom_gettree(&mesh) == 0) {64gpart_revert_all(&mesh);65geom_deletetree(&mesh);66}6768bsddialog_end();6970exit(1);71}7273int74main(int argc, const char **argv)75{76struct partition_metadata *md;77const char *progname, *prompt;78struct partedit_item *items = NULL;79struct gmesh mesh;80int i, op, nitems;81int error;82struct bsddialog_conf conf;8384progname = getprogname();85if (strcmp(progname, "sade") == 0)86sade_mode = 1;8788TAILQ_INIT(&part_metadata);8990init_fstab_metadata();9192if (bsddialog_init() == BSDDIALOG_ERROR)93err(1, "%s", bsddialog_geterror());94bsddialog_initconf(&conf);95if (!sade_mode)96bsddialog_backtitle(&conf, OSNAME " Installer");97i = 0;9899/* Revert changes on SIGINT */100signal(SIGINT, sigint_handler);101102if (strcmp(progname, "autopart") == 0) { /* Guided */103prompt = "Please review the disk setup. When complete, press "104"the Finish button.";105/* Experimental ZFS autopartition support */106if (argc > 1 && strcmp(argv[1], "zfs") == 0) {107part_wizard("zfs");108} else {109part_wizard("ufs");110}111} else if (strcmp(progname, "scriptedpart") == 0) {112error = scripted_editor(argc, argv);113prompt = NULL;114if (error != 0) {115bsddialog_end();116return (error);117}118} else {119prompt = "Create partitions for " OSNAME ", F1 for help.\n"120"No changes will be made until you select Finish.";121}122123/* Show the part editor either immediately, or to confirm wizard */124while (prompt != NULL) {125bsddialog_clear(0);126if (!sade_mode)127bsddialog_backtitle(&conf, OSNAME " Installer");128129error = geom_gettree(&mesh);130if (error == 0)131items = read_geom_mesh(&mesh, &nitems);132if (error || items == NULL) {133conf.title = "Error";134bsddialog_msgbox(&conf, "No disks found. If you need "135"to install a kernel driver, choose Shell at the "136"installation menu.", 0, 0);137break;138}139140get_mount_points(items, nitems);141142if (i >= nitems)143i = nitems - 1;144op = diskmenu_show("Partition Editor", prompt, items, nitems,145&i);146147switch (op) {148case BUTTON_CREATE:149gpart_create((struct gprovider *)(items[i].cookie),150NULL, NULL, NULL, NULL, 1);151break;152case BUTTON_DELETE:153gpart_delete((struct gprovider *)(items[i].cookie));154break;155case BUTTON_MODIFY:156gpart_edit((struct gprovider *)(items[i].cookie));157break;158case BUTTON_REVERT:159gpart_revert_all(&mesh);160while ((md = TAILQ_FIRST(&part_metadata)) != NULL) {161if (md->fstab != NULL) {162free(md->fstab->fs_spec);163free(md->fstab->fs_file);164free(md->fstab->fs_vfstype);165free(md->fstab->fs_mntops);166free(md->fstab->fs_type);167free(md->fstab);168}169if (md->newfs != NULL)170free(md->newfs);171free(md->name);172173TAILQ_REMOVE(&part_metadata, md, metadata);174free(md);175}176init_fstab_metadata();177break;178case BUTTON_AUTO:179part_wizard("ufs");180break;181}182183error = 0;184if (op == BUTTON_FINISH) {185conf.button.ok_label = "Commit";186conf.button.with_extra = true;187conf.button.extra_label = "Revert & Exit";188conf.button.cancel_label = "Back";189conf.title = "Confirmation";190op = bsddialog_yesno(&conf, "Your changes will now be "191"written to disk. If you have chosen to overwrite "192"existing data, it will be PERMANENTLY ERASED. Are "193"you sure you want to commit your changes?", 0, 0);194conf.button.ok_label = NULL;195conf.button.with_extra = false;196conf.button.extra_label = NULL;197conf.button.cancel_label = NULL;198199if (op == BSDDIALOG_OK && validate_setup()) { /* Save */200error = apply_changes(&mesh);201if (!error)202apply_workaround(&mesh);203break;204} else if (op == BSDDIALOG_EXTRA) { /* Quit */205gpart_revert_all(&mesh);206error = -1;207break;208}209}210211geom_deletetree(&mesh);212free(items);213}214215if (prompt == NULL) {216error = geom_gettree(&mesh);217if (error == 0) {218if (validate_setup()) {219error = apply_changes(&mesh);220} else {221gpart_revert_all(&mesh);222error = -1;223}224geom_deletetree(&mesh);225}226}227228bsddialog_end();229230return (error);231}232233struct partition_metadata *234get_part_metadata(const char *name, int create)235{236struct partition_metadata *md;237238TAILQ_FOREACH(md, &part_metadata, metadata)239if (md->name != NULL && strcmp(md->name, name) == 0)240break;241242if (md == NULL && create) {243md = calloc(1, sizeof(*md));244md->name = strdup(name);245TAILQ_INSERT_TAIL(&part_metadata, md, metadata);246}247248return (md);249}250251void252delete_part_metadata(const char *name)253{254struct partition_metadata *md;255256TAILQ_FOREACH(md, &part_metadata, metadata) {257if (md->name != NULL && strcmp(md->name, name) == 0) {258if (md->fstab != NULL) {259free(md->fstab->fs_spec);260free(md->fstab->fs_file);261free(md->fstab->fs_vfstype);262free(md->fstab->fs_mntops);263free(md->fstab->fs_type);264free(md->fstab);265}266if (md->newfs != NULL)267free(md->newfs);268free(md->name);269270TAILQ_REMOVE(&part_metadata, md, metadata);271free(md);272break;273}274}275}276277static int278validate_setup(void)279{280struct partition_metadata *md, *root = NULL;281int button;282struct bsddialog_conf conf;283284TAILQ_FOREACH(md, &part_metadata, metadata) {285if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0)286root = md;287288/* XXX: Check for duplicate mountpoints */289}290291bsddialog_initconf(&conf);292293if (root == NULL) {294conf.title = "Error";295bsddialog_msgbox(&conf, "No root partition was found. "296"The root " OSNAME " partition must have a mountpoint "297"of '/'.", 0, 0);298return (false);299}300301/*302* Check for root partitions that we aren't formatting, which is303* usually a mistake304*/305if (root->newfs == NULL && !sade_mode) {306conf.button.default_cancel = true;307conf.title = "Warning";308button = bsddialog_yesno(&conf, "The chosen root partition "309"has a preexisting filesystem. If it contains an existing "310OSNAME " system, please update it with freebsd-update "311"instead of installing a new system on it. The partition "312"can also be erased by pressing \"No\" and then deleting "313"and recreating it. Are you sure you want to proceed?",3140, 0);315if (button == BSDDIALOG_CANCEL)316return (false);317}318319return (true);320}321322static int323mountpoint_sorter(const void *xa, const void *xb)324{325struct partition_metadata *a = *(struct partition_metadata **)xa;326struct partition_metadata *b = *(struct partition_metadata **)xb;327328if (a->fstab == NULL && b->fstab == NULL)329return 0;330if (a->fstab == NULL)331return 1;332if (b->fstab == NULL)333return -1;334335return strcmp(a->fstab->fs_file, b->fstab->fs_file);336}337338static int339apply_changes(struct gmesh *mesh)340{341struct partition_metadata *md;342char message[512];343int i, nitems, error, *miniperc;344const char **minilabel;345const char *fstab_path;346FILE *fstab;347char *command;348struct bsddialog_conf conf;349350nitems = 1; /* Partition table changes */351TAILQ_FOREACH(md, &part_metadata, metadata) {352if (md->newfs != NULL)353nitems++;354}355minilabel = calloc(nitems, sizeof(const char *));356miniperc = calloc(nitems, sizeof(int));357minilabel[0] = "Writing partition tables";358miniperc[0] = BSDDIALOG_MG_INPROGRESS;359i = 1;360TAILQ_FOREACH(md, &part_metadata, metadata) {361if (md->newfs != NULL) {362char *item;363364asprintf(&item, "Initializing %s", md->name);365minilabel[i] = item;366miniperc[i] = BSDDIALOG_MG_PENDING;367i++;368}369}370371i = 0;372bsddialog_initconf(&conf);373conf.title = "Initializing";374bsddialog_mixedgauge(&conf,375"Initializing file systems. Please wait.", 0, 0, i * 100 / nitems,376nitems, minilabel, miniperc);377gpart_commit(mesh);378miniperc[i] = BSDDIALOG_MG_COMPLETED;379i++;380381if (getenv("BSDINSTALL_LOG") == NULL)382setenv("BSDINSTALL_LOG", "/dev/null", 1);383384TAILQ_FOREACH(md, &part_metadata, metadata) {385if (md->newfs != NULL) {386miniperc[i] = BSDDIALOG_MG_INPROGRESS;387bsddialog_mixedgauge(&conf,388"Initializing file systems. Please wait.", 0, 0,389i * 100 / nitems, nitems, minilabel, miniperc);390asprintf(&command, "(echo %s; %s) >>%s 2>>%s",391md->newfs, md->newfs, getenv("BSDINSTALL_LOG"),392getenv("BSDINSTALL_LOG"));393error = system(command);394free(command);395miniperc[i] = (error == 0) ?396BSDDIALOG_MG_COMPLETED : BSDDIALOG_MG_FAILED;397i++;398}399}400bsddialog_mixedgauge(&conf, "Initializing file systems. Please wait.",4010, 0, i * 100 / nitems, nitems, minilabel, miniperc);402403for (i = 1; i < nitems; i++)404free(__DECONST(char *, minilabel[i]));405406free(minilabel);407free(miniperc);408409/* Sort filesystems for fstab so that mountpoints are ordered */410{411struct partition_metadata **tobesorted;412struct partition_metadata *tmp;413int nparts = 0;414TAILQ_FOREACH(md, &part_metadata, metadata)415nparts++;416tobesorted = malloc(sizeof(struct partition_metadata *)*nparts);417nparts = 0;418TAILQ_FOREACH_SAFE(md, &part_metadata, metadata, tmp) {419tobesorted[nparts++] = md;420TAILQ_REMOVE(&part_metadata, md, metadata);421}422qsort(tobesorted, nparts, sizeof(tobesorted[0]),423mountpoint_sorter);424425/* Now re-add everything */426while (nparts-- > 0)427TAILQ_INSERT_HEAD(&part_metadata,428tobesorted[nparts], metadata);429free(tobesorted);430}431432if (getenv("PATH_FSTAB") != NULL)433fstab_path = getenv("PATH_FSTAB");434else435fstab_path = "/etc/fstab";436fstab = fopen(fstab_path, "w+");437if (fstab == NULL) {438snprintf(message, sizeof(message),439"Cannot open fstab file %s for writing (%s)\n",440getenv("PATH_FSTAB"), strerror(errno));441conf.title = "Error";442bsddialog_msgbox(&conf, message, 0, 0);443return (-1);444}445fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n");446TAILQ_FOREACH(md, &part_metadata, metadata) {447if (md->fstab != NULL)448fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n",449md->fstab->fs_spec, md->fstab->fs_file,450md->fstab->fs_vfstype, md->fstab->fs_mntops,451md->fstab->fs_freq, md->fstab->fs_passno);452}453fclose(fstab);454455return (0);456}457458static void459apply_workaround(struct gmesh *mesh)460{461struct gclass *classp;462struct ggeom *gp;463struct gconfig *gc;464const char *scheme = NULL, *modified = NULL;465struct bsddialog_conf conf;466467LIST_FOREACH(classp, &mesh->lg_class, lg_class) {468if (strcmp(classp->lg_name, "PART") == 0)469break;470}471472if (strcmp(classp->lg_name, "PART") != 0) {473bsddialog_initconf(&conf);474conf.title = "Error";475bsddialog_msgbox(&conf, "gpart not found!", 0, 0);476return;477}478479LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {480LIST_FOREACH(gc, &gp->lg_config, lg_config) {481if (strcmp(gc->lg_name, "scheme") == 0) {482scheme = gc->lg_val;483} else if (strcmp(gc->lg_name, "modified") == 0) {484modified = gc->lg_val;485}486}487488if (scheme && strcmp(scheme, "GPT") == 0 &&489modified && strcmp(modified, "true") == 0) {490if (getenv("WORKAROUND_LENOVO"))491gpart_set_root(gp->lg_name, "lenovofix");492if (getenv("WORKAROUND_GPTACTIVE"))493gpart_set_root(gp->lg_name, "active");494}495}496}497498static struct partedit_item *499read_geom_mesh(struct gmesh *mesh, int *nitems)500{501struct gclass *classp;502struct ggeom *gp;503struct partedit_item *items;504505*nitems = 0;506items = NULL;507508/*509* Build the device table. First add all disks (and CDs).510*/511512LIST_FOREACH(classp, &mesh->lg_class, lg_class) {513if (strcmp(classp->lg_name, "DISK") != 0 &&514strcmp(classp->lg_name, "MD") != 0)515continue;516517/* Now recurse into all children */518LIST_FOREACH(gp, &classp->lg_geom, lg_geom)519add_geom_children(gp, 0, &items, nitems);520}521522return (items);523}524525static void526add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items,527int *nitems)528{529struct gconsumer *cp;530struct gprovider *pp;531struct gconfig *gc;532533if (strcmp(gp->lg_class->lg_name, "PART") == 0 &&534!LIST_EMPTY(&gp->lg_config)) {535LIST_FOREACH(gc, &gp->lg_config, lg_config) {536if (strcmp(gc->lg_name, "scheme") == 0)537(*items)[*nitems-1].type = gc->lg_val;538}539}540541if (LIST_EMPTY(&gp->lg_provider))542return;543544LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {545if (strcmp(gp->lg_class->lg_name, "LABEL") == 0)546continue;547548/* Skip WORM media */549if (strncmp(pp->lg_name, "cd", 2) == 0)550continue;551552*items = realloc(*items,553(*nitems+1)*sizeof(struct partedit_item));554(*items)[*nitems].indentation = recurse;555(*items)[*nitems].name = pp->lg_name;556(*items)[*nitems].size = pp->lg_mediasize;557(*items)[*nitems].mountpoint = NULL;558(*items)[*nitems].type = "";559(*items)[*nitems].cookie = pp;560561LIST_FOREACH(gc, &pp->lg_config, lg_config) {562if (strcmp(gc->lg_name, "type") == 0)563(*items)[*nitems].type = gc->lg_val;564}565566/* Skip swap-backed MD devices */567if (strcmp(gp->lg_class->lg_name, "MD") == 0 &&568strcmp((*items)[*nitems].type, "swap") == 0)569continue;570571(*nitems)++;572573LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)574add_geom_children(cp->lg_geom, recurse+1, items,575nitems);576577/* Only use first provider for acd */578if (strcmp(gp->lg_class->lg_name, "ACD") == 0)579break;580}581}582583static void584init_fstab_metadata(void)585{586struct fstab *fstab;587struct partition_metadata *md;588589setfsent();590while ((fstab = getfsent()) != NULL) {591md = calloc(1, sizeof(struct partition_metadata));592593md->name = NULL;594if (strncmp(fstab->fs_spec, "/dev/", 5) == 0)595md->name = strdup(&fstab->fs_spec[5]);596597md->fstab = malloc(sizeof(struct fstab));598md->fstab->fs_spec = strdup(fstab->fs_spec);599md->fstab->fs_file = strdup(fstab->fs_file);600md->fstab->fs_vfstype = strdup(fstab->fs_vfstype);601md->fstab->fs_mntops = strdup(fstab->fs_mntops);602md->fstab->fs_type = strdup(fstab->fs_type);603md->fstab->fs_freq = fstab->fs_freq;604md->fstab->fs_passno = fstab->fs_passno;605606md->newfs = NULL;607608TAILQ_INSERT_TAIL(&part_metadata, md, metadata);609}610}611612static void613get_mount_points(struct partedit_item *items, int nitems)614{615struct partition_metadata *md;616int i;617618for (i = 0; i < nitems; i++) {619TAILQ_FOREACH(md, &part_metadata, metadata) {620if (md->name != NULL && md->fstab != NULL &&621strcmp(md->name, items[i].name) == 0) {622items[i].mountpoint = md->fstab->fs_file;623break;624}625}626}627}628629630