1218799Snwhitehorn/*-
2218799Snwhitehorn * Copyright (c) 2011 Nathan Whitehorn
3218799Snwhitehorn * All rights reserved.
4218799Snwhitehorn *
5218799Snwhitehorn * Redistribution and use in source and binary forms, with or without
6218799Snwhitehorn * modification, are permitted provided that the following conditions
7218799Snwhitehorn * are met:
8218799Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9218799Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10218799Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11218799Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12218799Snwhitehorn *    documentation and/or other materials provided with the distribution.
13218799Snwhitehorn *
14218799Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15218799Snwhitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16218799Snwhitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17218799Snwhitehorn * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18218799Snwhitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19218799Snwhitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20218799Snwhitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21218799Snwhitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22218799Snwhitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23218799Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24218799Snwhitehorn * SUCH DAMAGE.
25218799Snwhitehorn *
26218799Snwhitehorn * $FreeBSD: stable/10/usr.sbin/bsdinstall/partedit/part_wizard.c 321166 2017-07-18 18:54:47Z ngie $
27218799Snwhitehorn */
28218799Snwhitehorn
29218799Snwhitehorn#include <sys/param.h>
30321166Sngie#include <sys/sysctl.h>
31218799Snwhitehorn#include <errno.h>
32321166Sngie#include <inttypes.h>
33218799Snwhitehorn#include <libutil.h>
34273831Snwhitehorn#include <string.h>
35273831Snwhitehorn
36218799Snwhitehorn#include <libgeom.h>
37218799Snwhitehorn#include <dialog.h>
38218799Snwhitehorn#include <dlg_keys.h>
39218799Snwhitehorn
40218799Snwhitehorn#include "partedit.h"
41218799Snwhitehorn
42218799Snwhitehorn#define MIN_FREE_SPACE		(1024*1024*1024) /* 1 GB */
43218799Snwhitehorn#define SWAP_SIZE(available)	MIN(available/20, 4*1024*1024*1024LL)
44218799Snwhitehorn
45218799Snwhitehornstatic char *boot_disk(struct gmesh *mesh);
46218799Snwhitehornstatic char *wizard_partition(struct gmesh *mesh, const char *disk);
47218799Snwhitehorn
48218799Snwhitehornint
49321166Sngiepart_wizard(const char *fsreq)
50321166Sngie{
51218799Snwhitehorn	char *disk, *schemeroot;
52273831Snwhitehorn	const char *fstype;
53321166Sngie	struct gmesh mesh;
54321166Sngie	int error;
55218799Snwhitehorn
56273831Snwhitehorn	if (fsreq != NULL)
57273831Snwhitehorn		fstype = fsreq;
58273831Snwhitehorn	else
59273831Snwhitehorn		fstype = "ufs";
60273831Snwhitehorn
61218799Snwhitehornstartwizard:
62218799Snwhitehorn	error = geom_gettree(&mesh);
63218799Snwhitehorn
64218799Snwhitehorn	dlg_put_backtitle();
65218799Snwhitehorn	error = geom_gettree(&mesh);
66218799Snwhitehorn	disk = boot_disk(&mesh);
67218799Snwhitehorn	if (disk == NULL)
68218799Snwhitehorn		return (1);
69218799Snwhitehorn
70218799Snwhitehorn	dlg_clear();
71218799Snwhitehorn	dlg_put_backtitle();
72218799Snwhitehorn	schemeroot = wizard_partition(&mesh, disk);
73218799Snwhitehorn	free(disk);
74218799Snwhitehorn	if (schemeroot == NULL)
75218799Snwhitehorn		return (1);
76218799Snwhitehorn
77218799Snwhitehorn	geom_deletetree(&mesh);
78218799Snwhitehorn	dlg_clear();
79218799Snwhitehorn	dlg_put_backtitle();
80218799Snwhitehorn	error = geom_gettree(&mesh);
81218799Snwhitehorn
82273831Snwhitehorn	error = wizard_makeparts(&mesh, schemeroot, fstype, 1);
83218799Snwhitehorn	if (error)
84218799Snwhitehorn		goto startwizard;
85218799Snwhitehorn	free(schemeroot);
86273831Snwhitehorn
87218799Snwhitehorn	geom_deletetree(&mesh);
88218799Snwhitehorn
89218799Snwhitehorn	return (0);
90218799Snwhitehorn}
91218799Snwhitehorn
92218799Snwhitehornstatic char *
93218799Snwhitehornboot_disk(struct gmesh *mesh)
94218799Snwhitehorn{
95218799Snwhitehorn	struct gclass *classp;
96218799Snwhitehorn	struct gconfig *gc;
97218799Snwhitehorn	struct ggeom *gp;
98218799Snwhitehorn	struct gprovider *pp;
99218799Snwhitehorn	DIALOG_LISTITEM *disks = NULL;
100219058Snwhitehorn	const char *type, *desc;
101218799Snwhitehorn	char diskdesc[512];
102218799Snwhitehorn	char *chosen;
103218799Snwhitehorn	int i, err, selected, n = 0;
104218799Snwhitehorn
105218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
106218799Snwhitehorn		if (strcmp(classp->lg_name, "DISK") != 0 &&
107225066Snwhitehorn		    strcmp(classp->lg_name, "RAID") != 0 &&
108218799Snwhitehorn		    strcmp(classp->lg_name, "MD") != 0)
109218799Snwhitehorn			continue;
110218799Snwhitehorn
111218799Snwhitehorn		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
112218799Snwhitehorn			if (LIST_EMPTY(&gp->lg_provider))
113218799Snwhitehorn				continue;
114218799Snwhitehorn
115218799Snwhitehorn			LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
116219058Snwhitehorn				desc = type = NULL;
117219058Snwhitehorn				LIST_FOREACH(gc, &pp->lg_config, lg_config) {
118273831Snwhitehorn					if (strcmp(gc->lg_name, "type") == 0)
119218799Snwhitehorn						type = gc->lg_val;
120273831Snwhitehorn					if (strcmp(gc->lg_name, "descr") == 0)
121219058Snwhitehorn						desc = gc->lg_val;
122219058Snwhitehorn				}
123218799Snwhitehorn
124219058Snwhitehorn				/* Skip swap-backed md and WORM devices */
125218799Snwhitehorn				if (strcmp(classp->lg_name, "MD") == 0 &&
126218799Snwhitehorn				    type != NULL && strcmp(type, "swap") == 0)
127218799Snwhitehorn					continue;
128219058Snwhitehorn				if (strncmp(pp->lg_name, "cd", 2) == 0)
129219058Snwhitehorn					continue;
130218799Snwhitehorn
131218799Snwhitehorn				disks = realloc(disks, (++n)*sizeof(disks[0]));
132218799Snwhitehorn				disks[n-1].name = pp->lg_name;
133218799Snwhitehorn				humanize_number(diskdesc, 7, pp->lg_mediasize,
134218799Snwhitehorn				    "B", HN_AUTOSCALE, HN_DECIMAL);
135218799Snwhitehorn				if (strncmp(pp->lg_name, "ad", 2) == 0)
136218799Snwhitehorn					strcat(diskdesc, " ATA Hard Disk");
137218799Snwhitehorn				else if (strncmp(pp->lg_name, "md", 2) == 0)
138218799Snwhitehorn					strcat(diskdesc, " Memory Disk");
139219058Snwhitehorn				else
140219058Snwhitehorn					strcat(diskdesc, " Disk");
141219058Snwhitehorn
142219058Snwhitehorn				if (desc != NULL)
143219058Snwhitehorn					snprintf(diskdesc, sizeof(diskdesc),
144219058Snwhitehorn					    "%s <%s>", diskdesc, desc);
145219058Snwhitehorn
146218799Snwhitehorn				disks[n-1].text = strdup(diskdesc);
147218799Snwhitehorn				disks[n-1].help = NULL;
148218799Snwhitehorn				disks[n-1].state = 0;
149218799Snwhitehorn			}
150218799Snwhitehorn		}
151218799Snwhitehorn	}
152218799Snwhitehorn
153218799Snwhitehorn	if (n > 1) {
154218799Snwhitehorn		err = dlg_menu("Partitioning",
155218799Snwhitehorn		    "Select the disk on which to install FreeBSD.", 0, 0, 0,
156218799Snwhitehorn		    n, disks, &selected, NULL);
157218799Snwhitehorn
158218799Snwhitehorn		chosen = (err == 0) ? strdup(disks[selected].name) : NULL;
159218799Snwhitehorn	} else if (n == 1) {
160218799Snwhitehorn		chosen = strdup(disks[0].name);
161218799Snwhitehorn	} else {
162218799Snwhitehorn		chosen = NULL;
163218799Snwhitehorn	}
164218799Snwhitehorn
165218799Snwhitehorn	for (i = 0; i < n; i++)
166218799Snwhitehorn		free(disks[i].text);
167218799Snwhitehorn
168218799Snwhitehorn	return (chosen);
169218799Snwhitehorn}
170218799Snwhitehorn
171218799Snwhitehornstatic struct gprovider *
172218799Snwhitehornprovider_for_name(struct gmesh *mesh, const char *name)
173218799Snwhitehorn{
174218799Snwhitehorn	struct gclass *classp;
175218799Snwhitehorn	struct gprovider *pp = NULL;
176218799Snwhitehorn	struct ggeom *gp;
177218799Snwhitehorn
178218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
179218799Snwhitehorn		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
180218799Snwhitehorn			if (LIST_EMPTY(&gp->lg_provider))
181218799Snwhitehorn				continue;
182218799Snwhitehorn
183218799Snwhitehorn			LIST_FOREACH(pp, &gp->lg_provider, lg_provider)
184218799Snwhitehorn				if (strcmp(pp->lg_name, name) == 0)
185218799Snwhitehorn					break;
186218799Snwhitehorn
187218799Snwhitehorn			if (pp != NULL) break;
188218799Snwhitehorn		}
189218799Snwhitehorn
190218799Snwhitehorn		if (pp != NULL) break;
191218799Snwhitehorn	}
192218799Snwhitehorn
193218799Snwhitehorn	return (pp);
194218799Snwhitehorn}
195218799Snwhitehorn
196218799Snwhitehornstatic char *
197218799Snwhitehornwizard_partition(struct gmesh *mesh, const char *disk)
198218799Snwhitehorn{
199218799Snwhitehorn	struct gclass *classp;
200218799Snwhitehorn	struct ggeom *gpart = NULL;
201218799Snwhitehorn	struct gconfig *gc;
202321166Sngie	char *retval = NULL;
203321166Sngie	const char *scheme = NULL;
204218799Snwhitehorn	char message[512];
205218799Snwhitehorn	int choice;
206218799Snwhitehorn
207218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class)
208218799Snwhitehorn		if (strcmp(classp->lg_name, "PART") == 0)
209218799Snwhitehorn			break;
210218799Snwhitehorn
211218799Snwhitehorn	if (classp != NULL) {
212273831Snwhitehorn		LIST_FOREACH(gpart, &classp->lg_geom, lg_geom)
213218799Snwhitehorn			if (strcmp(gpart->lg_name, disk) == 0)
214218799Snwhitehorn				break;
215218799Snwhitehorn	}
216218799Snwhitehorn
217218799Snwhitehorn	if (gpart != NULL) {
218218799Snwhitehorn		LIST_FOREACH(gc, &gpart->lg_config, lg_config) {
219218799Snwhitehorn			if (strcmp(gc->lg_name, "scheme") == 0) {
220218799Snwhitehorn				scheme = gc->lg_val;
221218799Snwhitehorn				break;
222218799Snwhitehorn			}
223218799Snwhitehorn		}
224218799Snwhitehorn	}
225218799Snwhitehorn
226218855Snwhitehorn	/* Treat uncommitted scheme deletions as no scheme */
227218855Snwhitehorn	if (scheme != NULL && strcmp(scheme, "(none)") == 0)
228218855Snwhitehorn		scheme = NULL;
229218855Snwhitehorn
230218799Snwhitehornquery:
231218799Snwhitehorn	dialog_vars.yes_label = "Entire Disk";
232218799Snwhitehorn	dialog_vars.no_label = "Partition";
233218799Snwhitehorn	if (gpart != NULL)
234218799Snwhitehorn		dialog_vars.defaultno = TRUE;
235218799Snwhitehorn
236218799Snwhitehorn	snprintf(message, sizeof(message), "Would you like to use this entire "
237218799Snwhitehorn	    "disk (%s) for FreeBSD or partition it to share it with other "
238218799Snwhitehorn	    "operating systems? Using the entire disk will erase any data "
239218799Snwhitehorn	    "currently stored there.", disk);
240218799Snwhitehorn	choice = dialog_yesno("Partition", message, 0, 0);
241218799Snwhitehorn
242218799Snwhitehorn	dialog_vars.yes_label = NULL;
243218799Snwhitehorn	dialog_vars.no_label = NULL;
244218799Snwhitehorn	dialog_vars.defaultno = FALSE;
245218799Snwhitehorn
246218799Snwhitehorn	if (choice == 1 && scheme != NULL && !is_scheme_bootable(scheme)) {
247218799Snwhitehorn		char warning[512];
248218799Snwhitehorn		int subchoice;
249218799Snwhitehorn
250218799Snwhitehorn		sprintf(warning, "The existing partition scheme on this "
251218799Snwhitehorn		    "disk (%s) is not bootable on this platform. To install "
252218799Snwhitehorn		    "FreeBSD, it must be repartitioned. This will destroy all "
253218799Snwhitehorn		    "data on the disk. Are you sure you want to proceed?",
254218799Snwhitehorn		    scheme);
255218799Snwhitehorn		subchoice = dialog_yesno("Non-bootable Disk", warning, 0, 0);
256218799Snwhitehorn		if (subchoice != 0)
257218799Snwhitehorn			goto query;
258218799Snwhitehorn
259226083Snwhitehorn		gpart_destroy(gpart);
260285769Sallanjude		scheme = choose_part_type(default_scheme());
261285769Sallanjude		if (scheme == NULL)
262285769Sallanjude			return NULL;
263285769Sallanjude		gpart_partition(disk, scheme);
264218799Snwhitehorn	}
265218799Snwhitehorn
266218855Snwhitehorn	if (scheme == NULL || choice == 0) {
267218855Snwhitehorn		if (gpart != NULL && scheme != NULL) {
268218855Snwhitehorn			/* Erase partitioned disk */
269218799Snwhitehorn			choice = dialog_yesno("Confirmation", "This will erase "
270218799Snwhitehorn			   "the disk. Are you sure you want to proceed?", 0, 0);
271218799Snwhitehorn			if (choice != 0)
272218799Snwhitehorn				goto query;
273218799Snwhitehorn
274226083Snwhitehorn			gpart_destroy(gpart);
275218799Snwhitehorn		}
276218799Snwhitehorn
277285769Sallanjude		scheme = choose_part_type(default_scheme());
278285769Sallanjude		if (scheme == NULL)
279285769Sallanjude			return NULL;
280285769Sallanjude		gpart_partition(disk, scheme);
281218799Snwhitehorn	}
282218799Snwhitehorn
283218799Snwhitehorn	if (strcmp(scheme, "PC98") == 0 || strcmp(scheme, "MBR") == 0) {
284218799Snwhitehorn		struct gmesh submesh;
285218799Snwhitehorn		geom_gettree(&submesh);
286218799Snwhitehorn		gpart_create(provider_for_name(&submesh, disk),
287218799Snwhitehorn		    "freebsd", NULL, NULL, &retval,
288218799Snwhitehorn		    choice /* Non-interactive for "Entire Disk" */);
289218799Snwhitehorn		geom_deletetree(&submesh);
290218799Snwhitehorn	} else {
291218799Snwhitehorn		retval = strdup(disk);
292218799Snwhitehorn	}
293218799Snwhitehorn
294218799Snwhitehorn	return (retval);
295218799Snwhitehorn}
296218799Snwhitehorn
297245701Snwhitehornint
298321166Sngiewizard_makeparts(struct gmesh *mesh, const char *disk, const char *fstype,
299321166Sngie    int interactive)
300218799Snwhitehorn{
301218799Snwhitehorn	struct gclass *classp;
302218799Snwhitehorn	struct ggeom *gp;
303218799Snwhitehorn	struct gprovider *pp;
304321166Sngie	char *fsnames[] = {"freebsd-ufs", "freebsd-zfs"};
305321166Sngie	char *fsname;
306321166Sngie	struct gmesh submesh;
307321166Sngie	char swapsizestr[10], rootsizestr[10];
308218799Snwhitehorn	intmax_t swapsize, available;
309218799Snwhitehorn	int retval;
310218799Snwhitehorn
311273831Snwhitehorn	if (strcmp(fstype, "zfs") == 0) {
312273831Snwhitehorn		fsname = fsnames[1];
313273831Snwhitehorn	} else {
314273831Snwhitehorn		/* default to UFS */
315273831Snwhitehorn		fsname = fsnames[0];
316273831Snwhitehorn	}
317273831Snwhitehorn
318218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class)
319218799Snwhitehorn		if (strcmp(classp->lg_name, "PART") == 0)
320218799Snwhitehorn			break;
321218799Snwhitehorn
322273831Snwhitehorn	LIST_FOREACH(gp, &classp->lg_geom, lg_geom)
323218799Snwhitehorn		if (strcmp(gp->lg_name, disk) == 0)
324218799Snwhitehorn			break;
325218799Snwhitehorn
326218799Snwhitehorn	pp = provider_for_name(mesh, disk);
327218799Snwhitehorn
328218799Snwhitehorn	available = gpart_max_free(gp, NULL)*pp->lg_sectorsize;
329245701Snwhitehorn	if (interactive && available < MIN_FREE_SPACE) {
330218799Snwhitehorn		char availablestr[10], neededstr[10], message[512];
331218799Snwhitehorn		humanize_number(availablestr, 7, available, "B", HN_AUTOSCALE,
332218799Snwhitehorn		    HN_DECIMAL);
333218799Snwhitehorn		humanize_number(neededstr, 7, MIN_FREE_SPACE, "B", HN_AUTOSCALE,
334218799Snwhitehorn		    HN_DECIMAL);
335218799Snwhitehorn		sprintf(message, "There is not enough free space on %s to "
336218799Snwhitehorn		    "install FreeBSD (%s free, %s required). Would you like "
337218799Snwhitehorn		    "to choose another disk or to open the partition editor?",
338218799Snwhitehorn		    disk, availablestr, neededstr);
339218799Snwhitehorn
340218799Snwhitehorn		dialog_vars.yes_label = "Another Disk";
341218799Snwhitehorn		dialog_vars.no_label = "Editor";
342218799Snwhitehorn		retval = dialog_yesno("Warning", message, 0, 0);
343218799Snwhitehorn		dialog_vars.yes_label = NULL;
344218799Snwhitehorn		dialog_vars.no_label = NULL;
345218799Snwhitehorn
346218799Snwhitehorn		return (!retval); /* Editor -> return 0 */
347218799Snwhitehorn	}
348218799Snwhitehorn
349218799Snwhitehorn	swapsize = SWAP_SIZE(available);
350218799Snwhitehorn	humanize_number(swapsizestr, 7, swapsize, "B", HN_AUTOSCALE,
351218799Snwhitehorn	    HN_NOSPACE | HN_DECIMAL);
352218799Snwhitehorn	humanize_number(rootsizestr, 7, available - swapsize - 1024*1024,
353218799Snwhitehorn	    "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL);
354218799Snwhitehorn
355218799Snwhitehorn	geom_gettree(&submesh);
356218799Snwhitehorn	pp = provider_for_name(&submesh, disk);
357273831Snwhitehorn	gpart_create(pp, fsname, rootsizestr, "/", NULL, 0);
358218799Snwhitehorn	geom_deletetree(&submesh);
359218799Snwhitehorn
360218799Snwhitehorn	geom_gettree(&submesh);
361218799Snwhitehorn	pp = provider_for_name(&submesh, disk);
362218799Snwhitehorn	gpart_create(pp, "freebsd-swap", swapsizestr, NULL, NULL, 0);
363218799Snwhitehorn	geom_deletetree(&submesh);
364218799Snwhitehorn
365218799Snwhitehorn	return (0);
366218799Snwhitehorn}
367