disks.c revision 8313
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: disks.c,v 1.3 1995/05/06 09:34:11 jkh Exp $
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#include <sys/disklabel.h>
47
48/*
49 * I make some pretty gross assumptions about having a max of 50 chunks
50 * total - 8 slices and 42 partitions.  I can't easily display many more
51 * than that on the screen at once!
52 *
53 * For 2.1 I'll revisit this and try to make it more dynamic, but since
54 * this will catch 99.99% of all possible cases, I'm not too worried.
55 */
56
57#define MAX_CHUNKS	50
58
59/* Where to start printing the freebsd slices */
60#define CHUNK_SLICE_START_ROW		2
61#define CHUNK_PART_START_ROW		10
62
63/* The smallest filesystem we're willing to create */
64#define FS_MIN_SIZE			2048
65
66static struct {
67    struct disk *d;
68    struct chunk *c;
69    PartInfo *p;
70    PartType type;
71} fbsd_chunk_info[MAX_CHUNKS + 1];
72static int current_chunk;
73
74
75/* If the given disk has a root partition on it, return TRUE */
76static Boolean
77contains_root_partition(struct disk *d)
78{
79    struct chunk *c1;
80
81    if (!d->chunks)
82	msgFatal("Disk %s has no chunks!", d->name);
83    c1 = d->chunks->part;
84    while (c1) {
85	if (c1->type == freebsd) {
86	    struct chunk *c2 = c1->part;
87
88	    while (c2) {
89		if (c2->flags & CHUNK_IS_ROOT)
90		    return TRUE;
91		c2 = c2->next;
92	    }
93	}
94	c1 = c1->next;
95    }
96    return FALSE;
97}
98
99static Boolean
100check_conflict(char *name)
101{
102    int i;
103
104    for (i = 0; fbsd_chunk_info[i].d; i++)
105	if (fbsd_chunk_info[i].type == PART_FILESYSTEM &&
106	    !strcmp(fbsd_chunk_info[i].c->name, name))
107	    return TRUE;
108    return FALSE;
109}
110
111static int
112space_free(struct chunk *c)
113{
114    struct chunk *c1 = c->part;
115    int sz = c->size;
116
117    while (c1) {
118	if (c1->type != unused)
119	    sz -= c1->size;
120	c1 = c1->next;
121    }
122    if (sz < 0)
123	msgFatal("Partitions are larger than actual chunk??");
124    return sz;
125}
126
127static void
128record_fbsd_chunks(struct disk **disks)
129{
130    int i, j, p;
131
132    j = p = 0;
133    for (i = 0; disks[i]; i++) {
134	struct chunk *c1;
135
136	if (!disks[i]->chunks)
137	    msgFatal("No chunk list found for %s!", disks[i]->name);
138	c1 = disks[i]->chunks->part;
139	while (c1) {
140	    if (c1->type == freebsd) {
141		struct chunk *c2 = c1->part;
142
143		fbsd_chunk_info[j].type = PART_SLICE;
144		fbsd_chunk_info[j].d = disks[i];
145		fbsd_chunk_info[j].c = c1;
146		fbsd_chunk_info[j++].p = NULL;
147		while (c2) {
148		    if (c2->type == part) {
149			if (c2->subtype == FS_SWAP)
150			    fbsd_chunk_info[j].type = PART_SWAP;
151			else
152			    fbsd_chunk_info[j].type = PART_FILESYSTEM;
153			fbsd_chunk_info[j].d = disks[i];
154			fbsd_chunk_info[j].c = c2;
155			fbsd_chunk_info[j++].p = c2->private;
156		    }
157		    c2 = c2->next;
158		}
159	    }
160	    c1 = c1->next;
161	}
162    }
163    fbsd_chunk_info[j].d = NULL;
164    fbsd_chunk_info[j].c = NULL;
165}
166
167int
168get_mountpoint(struct chunk *c)
169{
170    char *val;
171    PartInfo *part;
172
173    val = msgGetInput(c->private,
174		      "Please specify mount point for new partition");
175    if (val) {
176	if (!strcmp(val, "/")) {
177	    if (c->flags & CHUNK_PAST_1024) {
178msgConfirm("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!");
179		return 1;
180	    }
181	    c->flags |= CHUNK_IS_ROOT;
182	}
183	if (check_conflict(val)) {
184	    msgConfirm("You already have a mountpoint for %s assigned!", val);
185	    return 1;
186	}
187	safe_free(c->private);
188	part = (PartInfo *)malloc(sizeof(PartInfo));
189	strncpy(part->mountpoint, val, FILENAME_MAX);
190	part->newfs = TRUE;
191	c->private = (void *)part;
192	c->private_free = free;
193	return 0;
194    }
195    return 1;
196}
197
198static PartType
199get_partition_type(struct chunk *c)
200{
201    char selection[20];
202    static unsigned char *fs_types[] = {
203	"FS",
204	"A file system",
205	"Swap",
206	"A swap partition.",
207    };
208
209    if (!dialog_menu("Please choose a partition type",
210		    "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)) {
211	if (!strcmp(selection, "FS"))
212	    return PART_FILESYSTEM;
213	else if (!strcmp(selection, "Swap"))
214	    return PART_SWAP;
215    }
216    return PART_NONE;
217}
218
219#define MAX_MOUNT_NAME	12
220
221#define PART_PART_COL	0
222#define PART_MOUNT_COL	8
223#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 4)
224#define PART_NEWFS_COL	(PART_SIZE_COL + 8)
225#define PART_OFF	40
226
227static void
228print_fbsd_chunks(void)
229{
230    int i, srow, prow, pcol;
231    int sz;
232
233    attrset(A_REVERSE);
234    mvaddstr(0, 25, "FreeBSD Partition Editor");
235    attrset(A_NORMAL);
236
237    for (i = 0; i < 2; i++) {
238	attrset(A_UNDERLINE);
239	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF),
240		 "Part");
241	attrset(A_NORMAL);
242
243	attrset(A_UNDERLINE);
244	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF),
245		 "Mount");
246	attrset(A_NORMAL);
247
248	attrset(A_UNDERLINE);
249	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF),
250		 "Size");
251
252	attrset(A_UNDERLINE);
253	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF),
254		 "Newfs");
255	attrset(A_NORMAL);
256    }
257
258    srow = CHUNK_SLICE_START_ROW;
259    prow = CHUNK_PART_START_ROW;
260
261    for (i = 0; fbsd_chunk_info[i].d; i++) {
262	if (i == current_chunk)
263	    attrset(ColorDisplay ? A_BOLD : A_UNDERLINE);
264	/* Is it a slice entry displayed at the top? */
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	/* Otherwise it's a swap or filesystem entry, at the bottom */
273	else {
274	    char *mountpoint, *newfs;
275
276	    /* Go for two columns */
277	    if (prow == (CHUNK_PART_START_ROW + 9))
278		pcol = PART_OFF;
279	    else
280		pcol = 0;
281	    mvaddstr(prow, pcol + PART_PART_COL, fbsd_chunk_info[i].c->name);
282	    if (fbsd_chunk_info[i].type == PART_FILESYSTEM) {
283		if (fbsd_chunk_info[i].p) {
284		    mountpoint = fbsd_chunk_info[i].p->mountpoint;
285		    newfs = fbsd_chunk_info[i].p->newfs ? "Y" : "N";
286		}
287		else {
288		    mountpoint = " ";
289		    newfs = " ";
290		}
291	    }
292	    else {
293		mountpoint = "swap";
294		newfs = " ";
295	    }
296	    for (i = 0; i < MAX_MOUNT_NAME && mountpoint[i]; i++)
297		mvaddch(prow, pcol + PART_MOUNT_COL + i, mountpoint[i]);
298	    mvprintw(prow, pcol + PART_SIZE_COL, "%4dMB",
299		     fbsd_chunk_info[i].c->size ?
300		     fbsd_chunk_info[i].c->size / 2048 : 0);
301	    mvaddstr(prow, pcol + PART_NEWFS_COL, newfs);
302	    ++prow;
303	}
304	if (i == current_chunk)
305	    attrset(A_NORMAL);
306    }
307}
308
309static void
310print_command_summary()
311{
312    int attrs = ColorDisplay ? A_BOLD : A_UNDERLINE;
313
314    mvprintw(19, 0,
315	     "The following commands are valid here (upper or lower case):");
316    mvprintw(20, 0, "C = Create FreeBSD Partition      D = Delete Partition");
317    mvprintw(21, 0, "M = Mount Partition (no newfs)    ESC = Proceed to summary screen");
318    mvprintw(22, 0, "The default target will be displayed in ");
319
320    attrset(attrs);
321    addstr(ColorDisplay ? "bold" : "underline");
322    attrset(A_NORMAL);
323    move(0, 0);
324}
325
326void
327partition_disks(struct disk **disks)
328{
329    int sz, key = 0;
330    Boolean partitioning;
331    char *msg = NULL;
332
333    dialog_clear();
334    partitioning = TRUE;
335    keypad(stdscr, TRUE);
336    record_fbsd_chunks(disks);
337
338    while (partitioning) {
339	clear();
340	print_fbsd_chunks();
341	print_command_summary();
342	if (msg) {
343	    standout(); mvprintw(23, 0, msg); standend();
344	    beep();
345	    msg = NULL;
346	}
347	refresh();
348	key = toupper(getch());
349	switch (key) {
350	case KEY_UP:
351	case '-':
352	    if (current_chunk != 0)
353		--current_chunk;
354	    break;
355
356	case KEY_DOWN:
357	case '+':
358	case '\r':
359	case '\n':
360	    if (fbsd_chunk_info[current_chunk + 1].d)
361		++current_chunk;
362	    break;
363
364	case KEY_HOME:
365	    current_chunk = 0;
366	    break;
367
368	case KEY_END:
369	    while (fbsd_chunk_info[current_chunk + 1].d)
370		++current_chunk;
371	    break;
372
373	case KEY_F(1):
374	case '?':
375	    systemDisplayFile("partitioning.hlp");
376	    break;
377
378	case 'C':
379	    if (fbsd_chunk_info[current_chunk].type != PART_SLICE) {
380		msg = "Can only create sub-partitions in a master partition (at top)";
381		break;
382	    }
383	    sz = space_free(fbsd_chunk_info[current_chunk].c);
384	    if (sz <= FS_MIN_SIZE)
385		msg = "Not enough space to create additional FreeBSD partition";
386	    else {
387		char *val, tmp[20];
388		int size;
389
390		snprintf(tmp, 20, "%d", sz);
391		val = msgGetInput(tmp, "Please specify size for new FreeBSD partition");
392		if (val && (size = strtol(val, 0, 0)) > 0) {
393		    PartType type;
394
395		    if (get_mountpoint(fbsd_chunk_info[current_chunk].c))
396			break;
397		    type = get_partition_type(fbsd_chunk_info[current_chunk].c);
398		    if (type == PART_NONE)
399			break;
400		    Create_Chunk(fbsd_chunk_info[current_chunk].d,
401				 fbsd_chunk_info[current_chunk].c->offset +
402				 sz - size,
403				 size,
404				 part,
405				 type == PART_SWAP ? FS_SWAP : freebsd,
406				 fbsd_chunk_info[current_chunk].c->flags);
407		    record_fbsd_chunks(disks);
408		}
409	    }
410	    break;
411
412	case 'D':
413	    if (fbsd_chunk_info[current_chunk].type == PART_SLICE) {
414		msg = "Use the Master Partition Editor to delete one of these";
415		break;
416	    }
417	    Delete_Chunk(fbsd_chunk_info[current_chunk].d,
418			 fbsd_chunk_info[current_chunk].c);
419	    break;
420
421	case 27:	/* ESC */
422	    partitioning = FALSE;
423	    break;
424	}
425    }
426}
427
428int
429write_disks(struct disk **disks)
430{
431    int i;
432    extern u_char boot1[], boot2[];
433    extern u_char mbr[], bteasy17[];
434
435    dialog_clear();
436    if (!msgYesNo("Last Chance!  Are you sure you want to write out\nall your changes to disk?")) {
437	for (i = 0; disks[i]; i++) {
438	    if (contains_root_partition(disks[i]))
439		Set_Boot_Blocks(disks[i], boot1, boot2);
440	    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, or boot from a disk other than the first."))
441		Set_Boot_Mgr(disks[i], bteasy17);
442	    else if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?"))
443		Set_Boot_Mgr(disks[i], mbr);
444#if 0
445	    Write_Disk(disks[i]);
446#endif
447	}
448	return 0;
449    }
450    return 1;
451}
452