diff options
Diffstat (limited to 'usr.sbin/bsdinstall/partedit/part_wizard.c')
| -rw-r--r-- | usr.sbin/bsdinstall/partedit/part_wizard.c | 428 | 
1 files changed, 428 insertions, 0 deletions
| 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); +} | 
