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$
27218799Snwhitehorn */
28218799Snwhitehorn
29218799Snwhitehorn#include <sys/param.h>
30218799Snwhitehorn#include <errno.h>
31218799Snwhitehorn#include <libutil.h>
32218799Snwhitehorn#include <inttypes.h>
33218799Snwhitehorn
34218799Snwhitehorn#include <libgeom.h>
35218799Snwhitehorn#include <dialog.h>
36218799Snwhitehorn#include <dlg_keys.h>
37218799Snwhitehorn
38218799Snwhitehorn#include "partedit.h"
39218799Snwhitehorn
40218799Snwhitehorn#define MIN_FREE_SPACE		(1024*1024*1024) /* 1 GB */
41218799Snwhitehorn#define SWAP_SIZE(available)	MIN(available/20, 4*1024*1024*1024LL)
42218799Snwhitehorn
43218799Snwhitehornstatic char *boot_disk(struct gmesh *mesh);
44218799Snwhitehornstatic char *wizard_partition(struct gmesh *mesh, const char *disk);
45218799Snwhitehorn
46218799Snwhitehornint
47218799Snwhitehornpart_wizard(void) {
48218799Snwhitehorn	int error;
49218799Snwhitehorn	struct gmesh mesh;
50218799Snwhitehorn	char *disk, *schemeroot;
51218799Snwhitehorn
52218799Snwhitehornstartwizard:
53218799Snwhitehorn	error = geom_gettree(&mesh);
54218799Snwhitehorn
55218799Snwhitehorn	dlg_put_backtitle();
56218799Snwhitehorn	error = geom_gettree(&mesh);
57218799Snwhitehorn	disk = boot_disk(&mesh);
58218799Snwhitehorn	if (disk == NULL)
59218799Snwhitehorn		return (1);
60218799Snwhitehorn
61218799Snwhitehorn	dlg_clear();
62218799Snwhitehorn	dlg_put_backtitle();
63218799Snwhitehorn	schemeroot = wizard_partition(&mesh, disk);
64218799Snwhitehorn	free(disk);
65218799Snwhitehorn	if (schemeroot == NULL)
66218799Snwhitehorn		return (1);
67218799Snwhitehorn
68218799Snwhitehorn	geom_deletetree(&mesh);
69218799Snwhitehorn	dlg_clear();
70218799Snwhitehorn	dlg_put_backtitle();
71218799Snwhitehorn	error = geom_gettree(&mesh);
72218799Snwhitehorn
73248240Snwhitehorn	error = wizard_makeparts(&mesh, schemeroot, 1);
74218799Snwhitehorn	if (error)
75218799Snwhitehorn		goto startwizard;
76218799Snwhitehorn	free(schemeroot);
77218799Snwhitehorn
78218799Snwhitehorn	geom_deletetree(&mesh);
79218799Snwhitehorn
80218799Snwhitehorn	return (0);
81218799Snwhitehorn}
82218799Snwhitehorn
83218799Snwhitehornstatic char *
84218799Snwhitehornboot_disk(struct gmesh *mesh)
85218799Snwhitehorn{
86218799Snwhitehorn	struct gclass *classp;
87218799Snwhitehorn	struct gconfig *gc;
88218799Snwhitehorn	struct ggeom *gp;
89218799Snwhitehorn	struct gprovider *pp;
90218799Snwhitehorn	DIALOG_LISTITEM *disks = NULL;
91219058Snwhitehorn	const char *type, *desc;
92218799Snwhitehorn	char diskdesc[512];
93218799Snwhitehorn	char *chosen;
94218799Snwhitehorn	int i, err, selected, n = 0;
95218799Snwhitehorn
96218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
97218799Snwhitehorn		if (strcmp(classp->lg_name, "DISK") != 0 &&
98225066Snwhitehorn		    strcmp(classp->lg_name, "RAID") != 0 &&
99218799Snwhitehorn		    strcmp(classp->lg_name, "MD") != 0)
100218799Snwhitehorn			continue;
101218799Snwhitehorn
102218799Snwhitehorn		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
103218799Snwhitehorn			if (LIST_EMPTY(&gp->lg_provider))
104218799Snwhitehorn				continue;
105218799Snwhitehorn
106218799Snwhitehorn			LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
107219058Snwhitehorn				desc = type = NULL;
108219058Snwhitehorn				LIST_FOREACH(gc, &pp->lg_config, lg_config) {
109218799Snwhitehorn					if (strcmp(gc->lg_name, "type") == 0)
110218799Snwhitehorn						type = gc->lg_val;
111219058Snwhitehorn					if (strcmp(gc->lg_name, "descr") == 0)
112219058Snwhitehorn						desc = gc->lg_val;
113219058Snwhitehorn				}
114218799Snwhitehorn
115219058Snwhitehorn				/* Skip swap-backed md and WORM devices */
116218799Snwhitehorn				if (strcmp(classp->lg_name, "MD") == 0 &&
117218799Snwhitehorn				    type != NULL && strcmp(type, "swap") == 0)
118218799Snwhitehorn					continue;
119219058Snwhitehorn				if (strncmp(pp->lg_name, "cd", 2) == 0)
120219058Snwhitehorn					continue;
121218799Snwhitehorn
122218799Snwhitehorn				disks = realloc(disks, (++n)*sizeof(disks[0]));
123218799Snwhitehorn				disks[n-1].name = pp->lg_name;
124218799Snwhitehorn				humanize_number(diskdesc, 7, pp->lg_mediasize,
125218799Snwhitehorn				    "B", HN_AUTOSCALE, HN_DECIMAL);
126218799Snwhitehorn				if (strncmp(pp->lg_name, "ad", 2) == 0)
127218799Snwhitehorn					strcat(diskdesc, " ATA Hard Disk");
128218799Snwhitehorn				else if (strncmp(pp->lg_name, "md", 2) == 0)
129218799Snwhitehorn					strcat(diskdesc, " Memory Disk");
130219058Snwhitehorn				else
131219058Snwhitehorn					strcat(diskdesc, " Disk");
132219058Snwhitehorn
133219058Snwhitehorn				if (desc != NULL)
134219058Snwhitehorn					snprintf(diskdesc, sizeof(diskdesc),
135219058Snwhitehorn					    "%s <%s>", diskdesc, desc);
136219058Snwhitehorn
137218799Snwhitehorn				disks[n-1].text = strdup(diskdesc);
138218799Snwhitehorn				disks[n-1].help = NULL;
139218799Snwhitehorn				disks[n-1].state = 0;
140218799Snwhitehorn			}
141218799Snwhitehorn		}
142218799Snwhitehorn	}
143218799Snwhitehorn
144218799Snwhitehorn	if (n > 1) {
145218799Snwhitehorn		err = dlg_menu("Partitioning",
146218799Snwhitehorn		    "Select the disk on which to install FreeBSD.", 0, 0, 0,
147218799Snwhitehorn		    n, disks, &selected, NULL);
148218799Snwhitehorn
149218799Snwhitehorn		chosen = (err == 0) ? strdup(disks[selected].name) : NULL;
150218799Snwhitehorn	} else if (n == 1) {
151218799Snwhitehorn		chosen = strdup(disks[0].name);
152218799Snwhitehorn	} else {
153218799Snwhitehorn		chosen = NULL;
154218799Snwhitehorn	}
155218799Snwhitehorn
156218799Snwhitehorn	for (i = 0; i < n; i++)
157218799Snwhitehorn		free(disks[i].text);
158218799Snwhitehorn
159218799Snwhitehorn	return (chosen);
160218799Snwhitehorn}
161218799Snwhitehorn
162218799Snwhitehornstatic struct gprovider *
163218799Snwhitehornprovider_for_name(struct gmesh *mesh, const char *name)
164218799Snwhitehorn{
165218799Snwhitehorn	struct gclass *classp;
166218799Snwhitehorn	struct gprovider *pp = NULL;
167218799Snwhitehorn	struct ggeom *gp;
168218799Snwhitehorn
169218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
170218799Snwhitehorn		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
171218799Snwhitehorn			if (LIST_EMPTY(&gp->lg_provider))
172218799Snwhitehorn				continue;
173218799Snwhitehorn
174218799Snwhitehorn			LIST_FOREACH(pp, &gp->lg_provider, lg_provider)
175218799Snwhitehorn				if (strcmp(pp->lg_name, name) == 0)
176218799Snwhitehorn					break;
177218799Snwhitehorn
178218799Snwhitehorn			if (pp != NULL) break;
179218799Snwhitehorn		}
180218799Snwhitehorn
181218799Snwhitehorn		if (pp != NULL) break;
182218799Snwhitehorn	}
183218799Snwhitehorn
184218799Snwhitehorn	return (pp);
185218799Snwhitehorn}
186218799Snwhitehorn
187218799Snwhitehornstatic char *
188218799Snwhitehornwizard_partition(struct gmesh *mesh, const char *disk)
189218799Snwhitehorn{
190218799Snwhitehorn	struct gclass *classp;
191218799Snwhitehorn	struct ggeom *gpart = NULL;
192218799Snwhitehorn	struct gconfig *gc;
193218799Snwhitehorn	char message[512];
194218799Snwhitehorn	const char *scheme = NULL;
195218799Snwhitehorn	char *retval = NULL;
196218799Snwhitehorn	int choice;
197218799Snwhitehorn
198218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class)
199218799Snwhitehorn		if (strcmp(classp->lg_name, "PART") == 0)
200218799Snwhitehorn			break;
201218799Snwhitehorn
202218799Snwhitehorn	if (classp != NULL) {
203218799Snwhitehorn		LIST_FOREACH(gpart, &classp->lg_geom, lg_geom)
204218799Snwhitehorn			if (strcmp(gpart->lg_name, disk) == 0)
205218799Snwhitehorn				break;
206218799Snwhitehorn	}
207218799Snwhitehorn
208218799Snwhitehorn	if (gpart != NULL) {
209218799Snwhitehorn		LIST_FOREACH(gc, &gpart->lg_config, lg_config) {
210218799Snwhitehorn			if (strcmp(gc->lg_name, "scheme") == 0) {
211218799Snwhitehorn				scheme = gc->lg_val;
212218799Snwhitehorn				break;
213218799Snwhitehorn			}
214218799Snwhitehorn		}
215218799Snwhitehorn	}
216218799Snwhitehorn
217218855Snwhitehorn	/* Treat uncommitted scheme deletions as no scheme */
218218855Snwhitehorn	if (scheme != NULL && strcmp(scheme, "(none)") == 0)
219218855Snwhitehorn		scheme = NULL;
220218855Snwhitehorn
221218799Snwhitehornquery:
222218799Snwhitehorn	dialog_vars.yes_label = "Entire Disk";
223218799Snwhitehorn	dialog_vars.no_label = "Partition";
224218799Snwhitehorn	if (gpart != NULL)
225218799Snwhitehorn		dialog_vars.defaultno = TRUE;
226218799Snwhitehorn
227218799Snwhitehorn	snprintf(message, sizeof(message), "Would you like to use this entire "
228218799Snwhitehorn	    "disk (%s) for FreeBSD or partition it to share it with other "
229218799Snwhitehorn	    "operating systems? Using the entire disk will erase any data "
230218799Snwhitehorn	    "currently stored there.", disk);
231218799Snwhitehorn	choice = dialog_yesno("Partition", message, 0, 0);
232218799Snwhitehorn
233218799Snwhitehorn	dialog_vars.yes_label = NULL;
234218799Snwhitehorn	dialog_vars.no_label = NULL;
235218799Snwhitehorn	dialog_vars.defaultno = FALSE;
236218799Snwhitehorn
237218799Snwhitehorn	if (choice == 1 && scheme != NULL && !is_scheme_bootable(scheme)) {
238218799Snwhitehorn		char warning[512];
239218799Snwhitehorn		int subchoice;
240218799Snwhitehorn
241218799Snwhitehorn		sprintf(warning, "The existing partition scheme on this "
242218799Snwhitehorn		    "disk (%s) is not bootable on this platform. To install "
243218799Snwhitehorn		    "FreeBSD, it must be repartitioned. This will destroy all "
244218799Snwhitehorn		    "data on the disk. Are you sure you want to proceed?",
245218799Snwhitehorn		    scheme);
246218799Snwhitehorn		subchoice = dialog_yesno("Non-bootable Disk", warning, 0, 0);
247218799Snwhitehorn		if (subchoice != 0)
248218799Snwhitehorn			goto query;
249218799Snwhitehorn
250226212Snwhitehorn		gpart_destroy(gpart);
251218799Snwhitehorn		gpart_partition(disk, default_scheme());
252218799Snwhitehorn		scheme = default_scheme();
253218799Snwhitehorn	}
254218799Snwhitehorn
255218855Snwhitehorn	if (scheme == NULL || choice == 0) {
256218855Snwhitehorn		if (gpart != NULL && scheme != NULL) {
257218855Snwhitehorn			/* Erase partitioned disk */
258218799Snwhitehorn			choice = dialog_yesno("Confirmation", "This will erase "
259218799Snwhitehorn			   "the disk. Are you sure you want to proceed?", 0, 0);
260218799Snwhitehorn			if (choice != 0)
261218799Snwhitehorn				goto query;
262218799Snwhitehorn
263226212Snwhitehorn			gpart_destroy(gpart);
264218799Snwhitehorn		}
265218799Snwhitehorn
266218799Snwhitehorn		gpart_partition(disk, default_scheme());
267218799Snwhitehorn		scheme = default_scheme();
268218799Snwhitehorn	}
269218799Snwhitehorn
270218799Snwhitehorn	if (strcmp(scheme, "PC98") == 0 || strcmp(scheme, "MBR") == 0) {
271218799Snwhitehorn		struct gmesh submesh;
272218799Snwhitehorn		geom_gettree(&submesh);
273218799Snwhitehorn		gpart_create(provider_for_name(&submesh, disk),
274218799Snwhitehorn		    "freebsd", NULL, NULL, &retval,
275218799Snwhitehorn		    choice /* Non-interactive for "Entire Disk" */);
276218799Snwhitehorn		geom_deletetree(&submesh);
277218799Snwhitehorn	} else {
278218799Snwhitehorn		retval = strdup(disk);
279218799Snwhitehorn	}
280218799Snwhitehorn
281218799Snwhitehorn	return (retval);
282218799Snwhitehorn}
283218799Snwhitehorn
284248240Snwhitehornint
285248240Snwhitehornwizard_makeparts(struct gmesh *mesh, const char *disk, int interactive)
286218799Snwhitehorn{
287218799Snwhitehorn	struct gmesh submesh;
288218799Snwhitehorn	struct gclass *classp;
289218799Snwhitehorn	struct ggeom *gp;
290218799Snwhitehorn	struct gprovider *pp;
291218799Snwhitehorn	intmax_t swapsize, available;
292218799Snwhitehorn	char swapsizestr[10], rootsizestr[10];
293218799Snwhitehorn	int retval;
294218799Snwhitehorn
295218799Snwhitehorn	LIST_FOREACH(classp, &mesh->lg_class, lg_class)
296218799Snwhitehorn		if (strcmp(classp->lg_name, "PART") == 0)
297218799Snwhitehorn			break;
298218799Snwhitehorn
299218799Snwhitehorn	LIST_FOREACH(gp, &classp->lg_geom, lg_geom)
300218799Snwhitehorn		if (strcmp(gp->lg_name, disk) == 0)
301218799Snwhitehorn			break;
302218799Snwhitehorn
303218799Snwhitehorn	pp = provider_for_name(mesh, disk);
304218799Snwhitehorn
305218799Snwhitehorn	available = gpart_max_free(gp, NULL)*pp->lg_sectorsize;
306248240Snwhitehorn	if (interactive && available < MIN_FREE_SPACE) {
307218799Snwhitehorn		char availablestr[10], neededstr[10], message[512];
308218799Snwhitehorn		humanize_number(availablestr, 7, available, "B", HN_AUTOSCALE,
309218799Snwhitehorn		    HN_DECIMAL);
310218799Snwhitehorn		humanize_number(neededstr, 7, MIN_FREE_SPACE, "B", HN_AUTOSCALE,
311218799Snwhitehorn		    HN_DECIMAL);
312218799Snwhitehorn		sprintf(message, "There is not enough free space on %s to "
313218799Snwhitehorn		    "install FreeBSD (%s free, %s required). Would you like "
314218799Snwhitehorn		    "to choose another disk or to open the partition editor?",
315218799Snwhitehorn		    disk, availablestr, neededstr);
316218799Snwhitehorn
317218799Snwhitehorn		dialog_vars.yes_label = "Another Disk";
318218799Snwhitehorn		dialog_vars.no_label = "Editor";
319218799Snwhitehorn		retval = dialog_yesno("Warning", message, 0, 0);
320218799Snwhitehorn		dialog_vars.yes_label = NULL;
321218799Snwhitehorn		dialog_vars.no_label = NULL;
322218799Snwhitehorn
323218799Snwhitehorn		return (!retval); /* Editor -> return 0 */
324218799Snwhitehorn	}
325218799Snwhitehorn
326218799Snwhitehorn	swapsize = SWAP_SIZE(available);
327218799Snwhitehorn	humanize_number(swapsizestr, 7, swapsize, "B", HN_AUTOSCALE,
328218799Snwhitehorn	    HN_NOSPACE | HN_DECIMAL);
329218799Snwhitehorn	humanize_number(rootsizestr, 7, available - swapsize - 1024*1024,
330218799Snwhitehorn	    "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL);
331218799Snwhitehorn
332218799Snwhitehorn	geom_gettree(&submesh);
333218799Snwhitehorn	pp = provider_for_name(&submesh, disk);
334218799Snwhitehorn	gpart_create(pp, "freebsd-ufs", rootsizestr, "/", NULL, 0);
335218799Snwhitehorn	geom_deletetree(&submesh);
336218799Snwhitehorn
337218799Snwhitehorn	geom_gettree(&submesh);
338218799Snwhitehorn	pp = provider_for_name(&submesh, disk);
339218799Snwhitehorn	gpart_create(pp, "freebsd-swap", swapsizestr, NULL, NULL, 0);
340218799Snwhitehorn	geom_deletetree(&submesh);
341218799Snwhitehorn
342218799Snwhitehorn	return (0);
343218799Snwhitehorn}
344218799Snwhitehorn
345