disks.c revision 8307
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.2 1995/05/05 23:47:40 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 PART_PART_COL	0
220#define PART_MOUNT_COL	8
221#define PART_NEWFS_COL	32
222#define PART_OFF	40
223
224static void
225print_fbsd_chunks(void)
226{
227    int i, srow, prow, pcol;
228    int sz;
229
230    attrset(A_REVERSE);
231    mvaddstr(0, 25, "FreeBSD Partition Editor");
232    attrset(A_NORMAL);
233
234    for (i = 0; i < 2; i++) {
235	attrset(A_UNDERLINE);
236	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF),
237		 "Part");
238	attrset(A_NORMAL);
239
240	attrset(A_UNDERLINE);
241	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF),
242		 "Mount");
243	attrset(A_NORMAL);
244
245	attrset(A_UNDERLINE);
246	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF),
247		 "Newfs");
248	attrset(A_NORMAL);
249    }
250
251    srow = CHUNK_SLICE_START_ROW;
252    prow = CHUNK_PART_START_ROW;
253
254    for (i = 0; fbsd_chunk_info[i].d; i++) {
255	if (i == current_chunk)
256	    attrset(A_BOLD);
257	if (fbsd_chunk_info[i].type == PART_SLICE) {
258	    sz = space_free(fbsd_chunk_info[i].c);
259	    mvprintw(srow++, 0,
260		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
261		     fbsd_chunk_info[i].d->name,
262		     fbsd_chunk_info[i].c->name, sz, (sz / 2048));
263	}
264	else {
265	    /* Go for two columns */
266	    if (prow == (CHUNK_PART_START_ROW + 9))
267		pcol = PART_OFF;
268	    else
269		pcol = 0;
270	    mvaddstr(prow, pcol + PART_PART_COL, fbsd_chunk_info[i].c->name);
271	    if (fbsd_chunk_info[i].type == PART_FILESYSTEM) {
272		char *mountpoint, *newfs;
273
274		if (fbsd_chunk_info[i].c->private) {
275		    mountpoint = ((PartInfo *)fbsd_chunk_info[i].c->private)->mountpoint;
276		    newfs = ((PartInfo *)fbsd_chunk_info[i].c->private)->newfs ? "Y" : "N";
277		}
278		else {
279		    mountpoint = "?";
280		    newfs = "";
281		}
282		mvaddstr(prow, pcol + PART_MOUNT_COL, mountpoint);
283		mvaddstr(prow, pcol + PART_NEWFS_COL, newfs);
284	    }
285	    else
286		mvaddstr(prow, pcol + PART_MOUNT_COL, "swap");
287	    ++prow;
288	}
289	if (i == current_chunk)
290	    attrset(A_NORMAL);
291    }
292}
293
294static void
295print_command_summary()
296{
297    int attrs = ColorDisplay ? A_BOLD : A_UNDERLINE;
298
299    mvprintw(19, 0,
300	     "The following commands are valid here (upper or lower case):");
301    mvprintw(20, 0, "C = Create FreeBSD Partition      D = Delete Partition");
302    mvprintw(21, 0, "M = Mount Partition (no newfs)    ESC = Proceed to summary screen");
303    mvprintw(22, 0, "The default target will be displayed in ");
304
305    attrset(attrs);
306    addstr(ColorDisplay ? "bold" : "underline");
307    attrset(A_NORMAL);
308    move(0, 0);
309}
310
311void
312partition_disks(struct disk **disks)
313{
314    int sz, key = 0;
315    Boolean partitioning;
316    char *msg = NULL;
317
318    dialog_clear();
319    partitioning = TRUE;
320    keypad(stdscr, TRUE);
321    record_fbsd_chunks(disks);
322
323    while (partitioning) {
324	clear();
325	print_fbsd_chunks();
326	print_command_summary();
327	if (msg) {
328	    standout(); mvprintw(23, 0, msg); standend();
329	    beep();
330	    msg = NULL;
331	}
332	refresh();
333	key = toupper(getch());
334	switch (key) {
335	case KEY_UP:
336	case '-':
337	    if (current_chunk != 0)
338		--current_chunk;
339	    break;
340
341	case KEY_DOWN:
342	case '+':
343	case '\r':
344	case '\n':
345	    if (fbsd_chunk_info[current_chunk + 1].d)
346		++current_chunk;
347	    break;
348
349	case KEY_HOME:
350	    current_chunk = 0;
351	    break;
352
353	case KEY_END:
354	    while (fbsd_chunk_info[current_chunk + 1].d)
355		++current_chunk;
356	    break;
357
358	case KEY_F(1):
359	case '?':
360	    systemDisplayFile("partitioning.hlp");
361	    break;
362
363	case 'C':
364	    if (fbsd_chunk_info[current_chunk].type != PART_SLICE) {
365		msg = "Can only create sub-partitions in a master partition (at top)";
366		break;
367	    }
368	    sz = space_free(fbsd_chunk_info[current_chunk].c);
369	    if (sz <= FS_MIN_SIZE)
370		msg = "Not enough space to create additional FreeBSD partition";
371	    else {
372		char *val, tmp[20];
373		int size;
374
375		snprintf(tmp, 20, "%d", sz);
376		val = msgGetInput(tmp, "Please specify size for new FreeBSD partition");
377		if (val && (size = strtol(val, 0, 0)) > 0) {
378		    PartType type;
379
380		    if (get_mountpoint(fbsd_chunk_info[current_chunk].c))
381			break;
382		    type = get_partition_type(fbsd_chunk_info[current_chunk].c);
383		    if (type == PART_NONE)
384			break;
385		    Create_Chunk(fbsd_chunk_info[current_chunk].d,
386				 fbsd_chunk_info[current_chunk].c->offset +
387				 sz - size,
388				 size,
389				 part,
390				 type == PART_SWAP ? FS_SWAP : freebsd,
391				 fbsd_chunk_info[current_chunk].c->flags);
392		    record_fbsd_chunks(disks);
393		}
394	    }
395	    break;
396
397	case 'D':
398	    if (fbsd_chunk_info[current_chunk].type == PART_SLICE) {
399		msg = "Use the Master Partition Editor to delete one of these";
400		break;
401	    }
402	    Delete_Chunk(fbsd_chunk_info[current_chunk].d,
403			 fbsd_chunk_info[current_chunk].c);
404	    break;
405
406	case 27:	/* ESC */
407	    partitioning = FALSE;
408	    break;
409	}
410    }
411}
412
413int
414write_disks(struct disk **disks)
415{
416    int i;
417    extern u_char boot1[], boot2[];
418    extern u_char mbr[], bteasy17[];
419
420    dialog_clear();
421    if (!msgYesNo("Last Chance!  Are you sure you want to write out\nall your changes to disk?")) {
422	for (i = 0; disks[i]; i++) {
423	    if (contains_root_partition(disks[i]))
424		Set_Boot_Blocks(disks[i], boot1, boot2);
425	    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."))
426		Set_Boot_Mgr(disks[i], bteasy17);
427	    else if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?"))
428		Set_Boot_Mgr(disks[i], mbr);
429#if 0
430	    Write_Disk(disks[i]);
431#endif
432	}
433	return 0;
434    }
435    return 1;
436}
437