Path: blob/main/usr.sbin/bsdinstall/partedit/part_wizard.c
103499 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>29#include <sys/sysctl.h>3031#include <errno.h>32#include <inttypes.h>33#include <libutil.h>34#include <stdio.h>35#include <stdlib.h>36#include <string.h>37#include <unistd.h>3839#include <libgeom.h>40#include <bsddialog.h>4142#include "partedit.h"4344#define MIN_FREE_SPACE (1023*1024*1024) /* Just under 1 GB */4546static char *wizard_partition(struct gmesh *mesh, const char *disk);4748/*49* Determine default swap (partition) size in bytes for a given amount of free50* disk space in bytes. The algorithm should likely be revisited in light of51* contemporary memory and disk sizes.52*/53static intmax_t54swap_size(intmax_t available)55{56intmax_t swapsize;57unsigned long swap_maxpages;58size_t sz;5960swapsize = MIN(available/20, 4*1024*1024*1024LL);61sz = sizeof(swap_maxpages);62if (sysctlbyname("vm.swap_maxpages", &swap_maxpages, &sz, NULL, 0) == 0)63swapsize = MIN(swapsize, (intmax_t)swap_maxpages * getpagesize());6465return (swapsize);66}6768int69part_wizard(const char *fsreq)70{71char *disk, *schemeroot;72const char *fstype;73struct gmesh mesh;74int error;75struct bsddialog_conf conf;7677bsddialog_initconf(&conf);7879if (fsreq != NULL)80fstype = fsreq;81else82fstype = "ufs";8384startwizard:85error = geom_gettree(&mesh);86if (error != 0)87return (1);8889bsddialog_backtitle(&conf, OSNAME " Installer");90disk = boot_disk_select(&mesh);91if (disk == NULL) {92geom_deletetree(&mesh);93return (1);94}9596bsddialog_clear(0);97bsddialog_backtitle(&conf, OSNAME " Installer");98schemeroot = wizard_partition(&mesh, disk);99free(disk);100geom_deletetree(&mesh);101if (schemeroot == NULL)102return (1);103104bsddialog_clear(0);105bsddialog_backtitle(&conf, OSNAME " Installer");106error = geom_gettree(&mesh);107if (error != 0) {108free(schemeroot);109return (1);110}111112error = wizard_makeparts(&mesh, schemeroot, fstype, 1);113free(schemeroot);114geom_deletetree(&mesh);115if (error)116goto startwizard;117118return (0);119}120121char *122boot_disk_select(struct gmesh *mesh)123{124struct gclass *classp;125struct gconfig *gc;126struct ggeom *gp;127struct gprovider *pp;128struct bsddialog_menuitem *disks = NULL;129const char *type, *desc;130char diskdesc[512];131char *chosen;132int i, button, fd, selected, n = 0;133struct bsddialog_conf conf;134135bsddialog_initconf(&conf);136137LIST_FOREACH(classp, &mesh->lg_class, lg_class) {138if (strcmp(classp->lg_name, "DISK") != 0 &&139strcmp(classp->lg_name, "RAID") != 0 &&140strcmp(classp->lg_name, "MD") != 0)141continue;142143LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {144if (LIST_EMPTY(&gp->lg_provider))145continue;146147LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {148desc = type = NULL;149LIST_FOREACH(gc, &pp->lg_config, lg_config) {150if (strcmp(gc->lg_name, "type") == 0)151type = gc->lg_val;152if (strcmp(gc->lg_name, "descr") == 0)153desc = gc->lg_val;154}155156/* Skip swap-backed md and WORM devices */157if (strcmp(classp->lg_name, "MD") == 0 &&158type != NULL && strcmp(type, "swap") == 0)159continue;160if (strncmp(pp->lg_name, "cd", 2) == 0)161continue;162/*163* Check if the disk is available to be opened for164* write operations, it helps prevent the USB165* stick used to boot from being listed as an option166*/167fd = g_open(pp->lg_name, 1);168if (fd == -1) {169continue;170}171g_close(fd);172173disks = realloc(disks, (++n)*sizeof(disks[0]));174disks[n-1].name = pp->lg_name;175humanize_number(diskdesc, 7, pp->lg_mediasize,176"B", HN_AUTOSCALE, HN_DECIMAL);177if (strncmp(pp->lg_name, "ad", 2) == 0)178strcat(diskdesc, " ATA Hard Disk");179else if (strncmp(pp->lg_name, "md", 2) == 0)180strcat(diskdesc, " Memory Disk");181else182strcat(diskdesc, " Disk");183184if (desc != NULL)185snprintf(diskdesc, sizeof(diskdesc),186"%s <%s>", diskdesc, desc);187188disks[n-1].prefix = "";189disks[n-1].on = false;190disks[n-1].depth = 0;191disks[n-1].desc = strdup(diskdesc);192disks[n-1].bottomdesc = "";193}194}195}196197if (n > 1) {198conf.title = "Partitioning";199button = bsddialog_menu(&conf,200"Select the disk on which to install " OSNAME ".", 0, 0, 0,201n, disks, &selected);202203chosen = (button == BSDDIALOG_OK) ?204strdup(disks[selected].name) : NULL;205} else if (n == 1) {206chosen = strdup(disks[0].name);207} else {208chosen = NULL;209}210211for (i = 0; i < n; i++)212free((char*)disks[i].desc);213214return (chosen);215}216217static struct gprovider *218provider_for_name(struct gmesh *mesh, const char *name)219{220struct gclass *classp;221struct gprovider *pp = NULL;222struct ggeom *gp;223224LIST_FOREACH(classp, &mesh->lg_class, lg_class) {225LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {226if (LIST_EMPTY(&gp->lg_provider))227continue;228229LIST_FOREACH(pp, &gp->lg_provider, lg_provider)230if (strcmp(pp->lg_name, name) == 0)231break;232233if (pp != NULL) break;234}235236if (pp != NULL) break;237}238239return (pp);240}241242static char *243wizard_partition(struct gmesh *mesh, const char *disk)244{245struct gclass *classp;246struct ggeom *gpart = NULL;247struct gconfig *gc;248char *retval = NULL;249const char *scheme = NULL;250char message[512];251int choice;252struct bsddialog_conf conf;253254bsddialog_initconf(&conf);255256LIST_FOREACH(classp, &mesh->lg_class, lg_class)257if (strcmp(classp->lg_name, "PART") == 0)258break;259260if (classp != NULL) {261LIST_FOREACH(gpart, &classp->lg_geom, lg_geom)262if (strcmp(gpart->lg_name, disk) == 0)263break;264}265266if (gpart != NULL) {267LIST_FOREACH(gc, &gpart->lg_config, lg_config) {268if (strcmp(gc->lg_name, "scheme") == 0) {269scheme = gc->lg_val;270break;271}272}273}274275/* Treat uncommitted scheme deletions as no scheme */276if (scheme != NULL && strcmp(scheme, "(none)") == 0)277scheme = NULL;278279query:280conf.button.ok_label = "Entire Disk";281conf.button.cancel_label = "Partition";282if (gpart != NULL)283conf.button.default_cancel = true;284285snprintf(message, sizeof(message), "Would you like to use this entire "286"disk (%s) for " OSNAME " or partition it to share it with other "287"operating systems? Using the entire disk will erase any data "288"currently stored there.", disk);289conf.title = "Partition";290choice = bsddialog_yesno(&conf, message, 9, 45);291292conf.button.ok_label = NULL;293conf.button.cancel_label = NULL;294conf.button.default_cancel = false;295296if (choice == BSDDIALOG_NO && scheme != NULL && !is_scheme_bootable(scheme)) {297char warning[512];298int subchoice;299300snprintf(warning, sizeof(warning),301"The existing partition scheme on this "302"disk (%s) is not bootable on this platform. To install "303OSNAME ", it must be repartitioned. This will destroy all "304"data on the disk. Are you sure you want to proceed?",305scheme);306conf.title = "Non-bootable Disk";307subchoice = bsddialog_yesno(&conf, warning, 0, 0);308if (subchoice != BSDDIALOG_YES)309goto query;310311gpart_destroy(gpart);312scheme = choose_part_type(default_scheme());313if (scheme == NULL)314return NULL;315gpart_partition(disk, scheme);316}317318if (scheme == NULL || choice == 0) {319if (gpart != NULL && scheme != NULL) {320/* Erase partitioned disk */321conf.title = "Confirmation";322choice = bsddialog_yesno(&conf, "This will erase "323"the disk. Are you sure you want to proceed?", 0, 0);324if (choice != BSDDIALOG_YES)325goto query;326327gpart_destroy(gpart);328}329330scheme = choose_part_type(default_scheme());331if (scheme == NULL)332return NULL;333gpart_partition(disk, scheme);334}335336if (strcmp(scheme, "MBR") == 0) {337struct gmesh submesh;338339if (geom_gettree(&submesh) == 0) {340gpart_create(provider_for_name(&submesh, disk),341"freebsd", NULL, NULL, &retval,342choice /* Non-interactive for "Entire Disk" */);343geom_deletetree(&submesh);344}345} else {346retval = strdup(disk);347}348349return (retval);350}351352int353wizard_makeparts(struct gmesh *mesh, const char *disk, const char *fstype,354int interactive)355{356struct gclass *classp;357struct ggeom *gp;358struct gprovider *pp;359char *fsnames[] = {"freebsd-ufs", "freebsd-zfs"};360char *fsname;361struct gmesh submesh;362char swapsizestr[10], rootsizestr[10];363intmax_t swapsize, available;364int error, retval;365struct bsddialog_conf conf;366367if (strcmp(fstype, "zfs") == 0) {368fsname = fsnames[1];369} else {370/* default to UFS */371fsname = fsnames[0];372}373374LIST_FOREACH(classp, &mesh->lg_class, lg_class)375if (strcmp(classp->lg_name, "PART") == 0)376break;377378LIST_FOREACH(gp, &classp->lg_geom, lg_geom)379if (strcmp(gp->lg_name, disk) == 0)380break;381382pp = provider_for_name(mesh, disk);383384available = gpart_max_free(gp, NULL)*pp->lg_sectorsize;385if (interactive && available < MIN_FREE_SPACE) {386char availablestr[10], neededstr[10], message[512];387humanize_number(availablestr, 7, available, "B", HN_AUTOSCALE,388HN_DECIMAL);389humanize_number(neededstr, 7, MIN_FREE_SPACE, "B", HN_AUTOSCALE,390HN_DECIMAL);391snprintf(message, sizeof(message),392"There is not enough free space on %s to "393"install " OSNAME " (%s free, %s required). Would you like "394"to choose another disk or to open the partition editor?",395disk, availablestr, neededstr);396397bsddialog_initconf(&conf);398conf.button.ok_label = "Another Disk";399conf.button.cancel_label = "Editor";400conf.title = "Warning";401retval = bsddialog_yesno(&conf, message, 0, 0);402403return (!retval); /* Editor -> return 0 */404}405406swapsize = swap_size(available);407humanize_number(swapsizestr, 7, swapsize, "B", HN_AUTOSCALE,408HN_NOSPACE | HN_DECIMAL);409humanize_number(rootsizestr, 7, available - swapsize - 1024*1024,410"B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL);411412error = geom_gettree(&submesh);413if (error != 0)414return (error);415pp = provider_for_name(&submesh, disk);416gpart_create(pp, fsname, rootsizestr, "/", NULL, 0);417geom_deletetree(&submesh);418419error = geom_gettree(&submesh);420if (error != 0)421return (error);422pp = provider_for_name(&submesh, disk);423gpart_create(pp, "freebsd-swap", swapsizestr, NULL, NULL, 0);424geom_deletetree(&submesh);425426return (0);427}428429430