disks.c revision 8302
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $Id$
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 *    must display the following acknowledgement:
24 *	This product includes software developed by Jordan Hubbard
25 *	for the FreeBSD Project.
26 * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to
27 *    endorse or promote products derived from this software without specific
28 *    prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 */
43
44#include "sysinstall.h"
45#include <ctype.h>
46
47/*
48 * I make some pretty gross assumptions about having a max of 50 chunks
49 * total - 8 slices and 42 partitions.  I can't easily display many more
50 * than that on the screen at once!
51 *
52 * For 2.1 I'll revisit this and try to make it more dynamic, but since
53 * this will catch 99.99% of all possible cases, I'm not too worried.
54 */
55
56#define MAX_CHUNKS	50
57
58#define FS_SWAP		1
59
60/* Where to start printing the freebsd slices */
61#define CHUNK_SLICE_START_ROW		2
62#define CHUNK_PART_START_ROW		10
63
64/* The smallest filesystem we're willing to create */
65#define FS_MIN_SIZE			2048
66
67typedef enum { PART_NONE, PART_SLICE, PART_SWAP, PART_FILESYSTEM } part_type;
68
69struct part_info {
70    Boolean newfs;
71    char mountpoint[FILENAME_MAX];
72};
73
74static struct {
75    struct disk *d;
76    struct chunk *c;
77    struct part_info *p;
78    part_type type;
79} fbsd_chunk_info[MAX_CHUNKS + 1];
80static int current_chunk;
81
82
83/* If the given disk has a root partition on it, return TRUE */
84static Boolean
85contains_root_partition(struct disk *d)
86{
87    struct chunk *c1;
88
89    if (!d->chunks)
90	msgFatal("Disk %s has no chunks!", d->name);
91    c1 = d->chunks->part;
92    while (c1) {
93	if (c1->type == freebsd) {
94	    struct chunk *c2 = c1->part;
95
96	    while (c2) {
97		if (c2->flags & CHUNK_IS_ROOT)
98		    return TRUE;
99		c2 = c2->next;
100	    }
101	}
102	c1 = c1->next;
103    }
104    return FALSE;
105}
106
107static Boolean
108check_conflict(char *name)
109{
110    int i;
111
112    for (i = 0; fbsd_chunk_info[i].d; i++)
113	if (fbsd_chunk_info[i].type == PART_FILESYSTEM &&
114	    !strcmp(fbsd_chunk_info[i].c->name, name))
115	    return TRUE;
116    return FALSE;
117}
118
119static int
120space_free(struct chunk *c)
121{
122    struct chunk *c1 = c->part;
123    int sz = c->size;
124
125    while (c1) {
126	if (c1->type != unused)
127	    sz -= c1->size;
128	c1 = c1->next;
129    }
130    if (sz < 0)
131	msgFatal("Partitions are larger than actual chunk??");
132    return sz;
133}
134
135static void
136record_fbsd_chunks(struct disk **disks)
137{
138    int i, j, p;
139
140    j = p = 0;
141    for (i = 0; disks[i]; i++) {
142	struct chunk *c1;
143
144	if (!disks[i]->chunks)
145	    msgFatal("No chunk list found for %s!", disks[i]->name);
146	c1 = disks[i]->chunks->part;
147	while (c1) {
148	    if (c1->type == freebsd) {
149		struct chunk *c2 = c1->part;
150
151		fbsd_chunk_info[j].type = PART_SLICE;
152		fbsd_chunk_info[j].d = disks[i];
153		fbsd_chunk_info[j].c = c1;
154		fbsd_chunk_info[j++].p = NULL;
155		while (c2) {
156		    if (c2->type == part) {
157			if (c2->subtype == FS_SWAP)
158			    fbsd_chunk_info[j].type = PART_SWAP;
159			else
160			    fbsd_chunk_info[j].type = PART_FILESYSTEM;
161			fbsd_chunk_info[j].d = disks[i];
162			fbsd_chunk_info[j].c = c2;
163			fbsd_chunk_info[j++].p = c2->private;
164		    }
165		    c2 = c2->next;
166		}
167	    }
168	    c1 = c1->next;
169	}
170    }
171    fbsd_chunk_info[j].d = NULL;
172    fbsd_chunk_info[j].c = NULL;
173}
174
175int
176get_mountpoint(struct chunk *c)
177{
178    char *val;
179    struct part_info *part;
180
181    val = msgGetInput(c->private,
182		      "Please specify mount point for new partition");
183    if (val) {
184	if (!strcmp(val, "/")) {
185	    if (c->flags & CHUNK_PAST_1024) {
186msgConfirm("This region cannot be used for your root partition as\nit is past the 1024'th cylinder mark and the system would not be\nable to boot from it.  Please pick another location for your\nroot partition and try again!");
187		return 1;
188	    }
189	    c->flags |= CHUNK_IS_ROOT;
190	}
191	if (check_conflict(val)) {
192	    msgConfirm("You already have a mountpoint for %s assigned!", val);
193	    return 1;
194	}
195	safe_free(c->private);
196	part = (struct part_info *)malloc(sizeof(struct part_info));
197	strncpy(part->mountpoint, val, FILENAME_MAX);
198	part->newfs = TRUE;
199	c->private = (void *)part;
200	c->private_free = free;
201	return 0;
202    }
203    return 1;
204}
205
206static part_type
207get_partition_type(struct chunk *c)
208{
209    char selection[20];
210    static unsigned char *fs_types[] = {
211	"Swap",
212	"A swap partition.",
213	"FS",
214	"A file system",
215    };
216
217    if (!dialog_menu("Please choose a partition type",
218		    "If you want to use this partition for swap space, select Swap.\nIf you want to put a filesystem on it, choose FS.", -1, -1, 2, 2, fs_types, selection, NULL, NULL)) {
219	if (!strcmp(selection, "FS"))
220	    return PART_FILESYSTEM;
221	else if (!strcmp(selection, "Swap"))
222	    return PART_SWAP;
223    }
224    return PART_NONE;
225}
226
227#define PART_PART_COL	0
228#define PART_MOUNT_COL	8
229#define PART_NEWFS_COL	32
230#define PART_OFF	40
231
232static void
233print_fbsd_chunks(void)
234{
235    int i, srow, prow, pcol;
236    int sz;
237
238    attrset(A_REVERSE);
239    mvaddstr(0, 25, "FreeBSD Partition Editor");
240    attrset(A_NORMAL);
241
242    for (i = 0; i < 2; i++) {
243	attrset(A_UNDERLINE);
244	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF),
245		 "Part");
246	attrset(A_NORMAL);
247
248	attrset(A_UNDERLINE);
249	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF),
250		 "Mount");
251	attrset(A_NORMAL);
252
253	attrset(A_UNDERLINE);
254	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF),
255		 "Newfs");
256	attrset(A_NORMAL);
257    }
258
259    srow = CHUNK_SLICE_START_ROW;
260    prow = CHUNK_PART_START_ROW;
261
262    for (i = 0; fbsd_chunk_info[i].d; i++) {
263	if (i == current_chunk)
264	    attrset(A_BOLD);
265	if (fbsd_chunk_info[i].type == PART_SLICE) {
266	    sz = space_free(fbsd_chunk_info[i].c);
267	    mvprintw(srow++, 0,
268		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
269		     fbsd_chunk_info[i].d->name,
270		     fbsd_chunk_info[i].c->name, sz, (sz / 2048));
271	}
272	else {
273	    /* Go for two columns */
274	    if (prow == (CHUNK_PART_START_ROW + 9))
275		pcol = PART_OFF;
276	    else
277		pcol = 0;
278	    mvaddstr(prow, pcol + PART_PART_COL, fbsd_chunk_info[i].c->name);
279	    if (fbsd_chunk_info[i].type == PART_FILESYSTEM) {
280		char *mountpoint, *newfs;
281
282		if (fbsd_chunk_info[i].c->private) {
283		    mountpoint = ((struct part_info *)fbsd_chunk_info[i].c->private)->mountpoint;
284		    newfs = ((struct part_info *)fbsd_chunk_info[i].c->private)->newfs ? "Y" : "N";
285		}
286		else {
287		    mountpoint = "?";
288		    newfs = "";
289		}
290		mvaddstr(prow, pcol + PART_MOUNT_COL, mountpoint);
291		mvaddstr(prow, pcol + PART_NEWFS_COL, newfs);
292	    }
293	    else
294		mvaddstr(prow, pcol + PART_MOUNT_COL, "swap");
295	    ++prow;
296	}
297	if (i == current_chunk)
298	    attrset(A_NORMAL);
299    }
300}
301
302static void
303print_command_summary()
304{
305    int attrs = ColorDisplay ? A_BOLD : A_UNDERLINE;
306
307    mvprintw(19, 0,
308	     "The following commands are valid here (upper or lower case):");
309    mvprintw(20, 0, "C = Create FreeBSD Partition      D = Delete Partition");
310    mvprintw(21, 0, "M = Mount Partition (no newfs)    ESC = Proceed to summary screen");
311    mvprintw(22, 0, "The default target will be displayed in ");
312
313    attrset(attrs);
314    addstr(ColorDisplay ? "bold" : "underline");
315    attrset(A_NORMAL);
316    move(0, 0);
317}
318
319void
320partition_disks(struct disk **disks)
321{
322    int sz, key = 0;
323    Boolean partitioning;
324    char *msg = NULL;
325
326    dialog_clear();
327    partitioning = TRUE;
328    keypad(stdscr, TRUE);
329    record_fbsd_chunks(disks);
330
331    while (partitioning) {
332	clear();
333	print_fbsd_chunks();
334	print_command_summary();
335	if (msg) {
336	    standout(); mvprintw(23, 0, msg); standend();
337	    beep();
338	    msg = NULL;
339	}
340	refresh();
341	key = toupper(getch());
342	switch (key) {
343	case KEY_UP:
344	case '-':
345	    if (current_chunk != 0)
346		--current_chunk;
347	    break;
348
349	case KEY_DOWN:
350	case '+':
351	case '\r':
352	case '\n':
353	    if (fbsd_chunk_info[current_chunk + 1].d)
354		++current_chunk;
355	    break;
356
357	case KEY_HOME:
358	    current_chunk = 0;
359	    break;
360
361	case KEY_END:
362	    while (fbsd_chunk_info[current_chunk + 1].d)
363		++current_chunk;
364	    break;
365
366	case KEY_F(1):
367	case '?':
368	    systemDisplayFile("partitioning.hlp");
369	    break;
370
371	case 'C':
372	    if (fbsd_chunk_info[current_chunk].type != PART_SLICE) {
373		msg = "Can only create sub-partitions in a master partition (at top)";
374		break;
375	    }
376	    sz = space_free(fbsd_chunk_info[current_chunk].c);
377	    if (sz <= FS_MIN_SIZE)
378		msg = "Not enough space to create additional FreeBSD partition";
379	    else {
380		char *val, tmp[20];
381		int size;
382
383		snprintf(tmp, 20, "%d", sz);
384		val = msgGetInput(tmp, "Please specify size for new FreeBSD partition");
385		if (val && (size = strtol(val, 0, 0)) > 0) {
386		    part_type type;
387
388		    if (get_mountpoint(fbsd_chunk_info[current_chunk].c))
389			break;
390		    type = get_partition_type(fbsd_chunk_info[current_chunk].c);
391		    if (type == PART_NONE)
392			break;
393		    Create_Chunk(fbsd_chunk_info[current_chunk].d,
394				 fbsd_chunk_info[current_chunk].c->offset,
395				 size,
396				 part,
397				 type == PART_SWAP ? FS_SWAP : freebsd,
398				 fbsd_chunk_info[current_chunk].c->flags);
399		    record_fbsd_chunks(disks);
400		}
401	    }
402	    break;
403
404	case 27:	/* ESC */
405	    partitioning = FALSE;
406	    break;
407	}
408    }
409}
410
411int
412write_disks(struct disk **disks)
413{
414    int i;
415    extern u_char boot1[], boot2[];
416    extern u_char mbr[], bteasy17[];
417
418    dialog_clear();
419    if (!msgYesNo("Last Chance!  Are you sure you want to write your changes to disk?")) {
420	for (i = 0; disks[i]; i++) {
421	    if (contains_root_partition(disks[i]))
422		Set_Boot_Blocks(disks[i], boot1, boot2);
423	    if (i == 0 && !msgYesNo("Would you like to install a boot manager?\n\nThis will allow you to easily select between other operating systems\non the first disk, as well as boot from a driver other than the first."))
424		Set_Boot_Mgr(disks[i], bteasy17);
425	    else if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?"))
426		Set_Boot_Mgr(disks[i], mbr);
427#if 0
428	    Write_Disk(disks[i]);
429#endif
430	}
431	return 0;
432    }
433    return 1;
434}
435
436void
437make_filesystems(struct disk **disks)
438{
439}
440
441void
442cpio_extract(struct disk **disks)
443{
444}
445
446void
447extract_dists(struct disk **disks)
448{
449}
450
451void
452do_final_setup(struct disk **disks)
453{
454}
455
456