disks.c revision 8314
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.4 1995/05/07 02:04:25 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
139	/* Put the freebsd chunks first */
140	for (c1 = disks[i]->chunks->part; c1; c1 = c1->next) {
141	    if (c1->type == freebsd) {
142		fbsd_chunk_info[j].type = PART_SLICE;
143		fbsd_chunk_info[j].d = disks[i];
144		fbsd_chunk_info[j].c = c1;
145		fbsd_chunk_info[j++].p = NULL;
146	    }
147	}
148
149	/* Then buzz through and pick up the partitions */
150	for (c1 = disks[i]->chunks->part; c1; c1 = c1->next) {
151	    if (c1->type == freebsd) {
152		struct chunk *c2 = c1->part;
153
154		while (c2) {
155		    if (c2->type == part) {
156			if (c2->subtype == FS_SWAP)
157			    fbsd_chunk_info[j].type = PART_SWAP;
158			else
159			    fbsd_chunk_info[j].type = PART_FILESYSTEM;
160			fbsd_chunk_info[j].d = disks[i];
161			fbsd_chunk_info[j].c = c2;
162			fbsd_chunk_info[j++].p = c2->private;
163		    }
164		    c2 = c2->next;
165		}
166	    }
167	}
168    }
169    fbsd_chunk_info[j].d = NULL;
170    fbsd_chunk_info[j].c = NULL;
171}
172
173int
174get_mountpoint(struct chunk *c)
175{
176    char *val;
177    PartInfo *part;
178
179    val = msgGetInput(c->private,
180		      "Please specify mount point for new partition");
181    if (val) {
182	if (!strcmp(val, "/")) {
183	    if (c->flags & CHUNK_PAST_1024) {
184msgConfirm("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!");
185		return 1;
186	    }
187	    c->flags |= CHUNK_IS_ROOT;
188	}
189	if (check_conflict(val)) {
190	    msgConfirm("You already have a mountpoint for %s assigned!", val);
191	    return 1;
192	}
193	safe_free(c->private);
194	part = (PartInfo *)malloc(sizeof(PartInfo));
195	strncpy(part->mountpoint, val, FILENAME_MAX);
196	part->newfs = TRUE;
197	c->private = (void *)part;
198	c->private_free = free;
199	return 0;
200    }
201    return 1;
202}
203
204static PartType
205get_partition_type(struct chunk *c)
206{
207    char selection[20];
208    static unsigned char *fs_types[] = {
209	"FS",
210	"A file system",
211	"Swap",
212	"A swap partition.",
213    };
214
215    if (!dialog_menu("Please choose a partition type",
216		    "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)) {
217	if (!strcmp(selection, "FS"))
218	    return PART_FILESYSTEM;
219	else if (!strcmp(selection, "Swap"))
220	    return PART_SWAP;
221    }
222    return PART_NONE;
223}
224
225#define MAX_MOUNT_NAME	12
226
227#define PART_PART_COL	0
228#define PART_MOUNT_COL	8
229#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 4)
230#define PART_NEWFS_COL	(PART_SIZE_COL + 8)
231#define PART_OFF	40
232
233static void
234print_fbsd_chunks(void)
235{
236    int i, j, srow, prow, pcol;
237    int sz;
238
239    attrset(A_REVERSE);
240    mvaddstr(0, 25, "FreeBSD Partition Editor");
241    attrset(A_NORMAL);
242
243    for (i = 0; i < 2; i++) {
244	attrset(A_UNDERLINE);
245	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF),
246		 "Part");
247	attrset(A_NORMAL);
248
249	attrset(A_UNDERLINE);
250	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF),
251		 "Mount");
252	attrset(A_NORMAL);
253
254	attrset(A_UNDERLINE);
255	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2,
256		 "Size");
257	attrset(A_NORMAL);
258
259	attrset(A_UNDERLINE);
260	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF),
261		 "Newfs");
262	attrset(A_NORMAL);
263    }
264
265    srow = CHUNK_SLICE_START_ROW;
266    prow = CHUNK_PART_START_ROW;
267
268    for (i = 0; fbsd_chunk_info[i].d; i++) {
269	if (i == current_chunk)
270	    attrset(A_REVERSE);
271	/* Is it a slice entry displayed at the top? */
272	if (fbsd_chunk_info[i].type == PART_SLICE) {
273	    sz = space_free(fbsd_chunk_info[i].c);
274	    mvprintw(srow++, 0,
275		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
276		     fbsd_chunk_info[i].d->name,
277		     fbsd_chunk_info[i].c->name, sz, (sz / 2048));
278	}
279	/* Otherwise it's a swap or filesystem entry, at the bottom */
280	else {
281	    char *mountpoint, *newfs;
282
283	    /* Go for two columns */
284	    if (prow == (CHUNK_PART_START_ROW + 9))
285		pcol = PART_OFF;
286	    else
287		pcol = 0;
288	    mvaddstr(prow, pcol + PART_PART_COL, fbsd_chunk_info[i].c->name);
289	    if (fbsd_chunk_info[i].type == PART_FILESYSTEM) {
290		if (fbsd_chunk_info[i].p) {
291		    mountpoint = fbsd_chunk_info[i].p->mountpoint;
292		    newfs = fbsd_chunk_info[i].p->newfs ? "Y" : "N";
293		}
294		else {
295		    mountpoint = " ";
296		    newfs = " ";
297		}
298	    }
299	    else {
300		mountpoint = "swap";
301		newfs = " ";
302	    }
303	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
304		mvaddch(prow, pcol + PART_MOUNT_COL + j, mountpoint[j]);
305	    mvprintw(prow, pcol + PART_SIZE_COL, "%4dMB",
306		     fbsd_chunk_info[i].c->size ?
307		     fbsd_chunk_info[i].c->size / 2048 : 0);
308	    mvaddstr(prow, pcol + PART_NEWFS_COL, newfs);
309	    ++prow;
310	}
311	if (i == current_chunk)
312	    attrset(A_NORMAL);
313    }
314}
315
316static void
317print_command_summary()
318{
319    mvprintw(19, 0,
320	     "The following commands are valid here (upper or lower case):");
321    mvprintw(20, 0, "C = Create FreeBSD Partition      D = Delete Partition");
322    mvprintw(21, 0, "M = Mount Partition (no newfs)    ESC = Proceed to summary screen");
323    mvprintw(22, 0, "The default target will be displayed in ");
324
325    attrset(A_REVERSE);
326    addstr("reverse video");
327    attrset(A_NORMAL);
328    move(0, 0);
329}
330
331void
332partition_disks(struct disk **disks)
333{
334    int sz, key = 0;
335    Boolean partitioning;
336    char *msg = NULL;
337
338    dialog_clear();
339    partitioning = TRUE;
340    keypad(stdscr, TRUE);
341    record_fbsd_chunks(disks);
342
343    while (partitioning) {
344	clear();
345	print_fbsd_chunks();
346	print_command_summary();
347	if (msg) {
348	    standout(); mvprintw(23, 0, msg); standend();
349	    beep();
350	    msg = NULL;
351	}
352	refresh();
353	key = toupper(getch());
354	switch (key) {
355	case KEY_UP:
356	case '-':
357	    if (current_chunk != 0)
358		--current_chunk;
359	    break;
360
361	case KEY_DOWN:
362	case '+':
363	case '\r':
364	case '\n':
365	    if (fbsd_chunk_info[current_chunk + 1].d)
366		++current_chunk;
367	    break;
368
369	case KEY_HOME:
370	    current_chunk = 0;
371	    break;
372
373	case KEY_END:
374	    while (fbsd_chunk_info[current_chunk + 1].d)
375		++current_chunk;
376	    break;
377
378	case KEY_F(1):
379	case '?':
380	    systemDisplayFile("partitioning.hlp");
381	    break;
382
383	case 'C':
384	    if (fbsd_chunk_info[current_chunk].type != PART_SLICE) {
385		msg = "Can only create sub-partitions in a master partition (at top)";
386		break;
387	    }
388	    sz = space_free(fbsd_chunk_info[current_chunk].c);
389	    if (sz <= FS_MIN_SIZE)
390		msg = "Not enough space to create additional FreeBSD partition";
391	    else {
392		char *val, tmp[20];
393		int size;
394
395		snprintf(tmp, 20, "%d", sz);
396		val = msgGetInput(tmp, "Please specify size for new FreeBSD partition");
397		if (val && (size = strtol(val, 0, 0)) > 0) {
398		    PartType type;
399
400		    if (get_mountpoint(fbsd_chunk_info[current_chunk].c))
401			break;
402		    type = get_partition_type(fbsd_chunk_info[current_chunk].c);
403		    if (type == PART_NONE)
404			break;
405		    Create_Chunk(fbsd_chunk_info[current_chunk].d,
406				 fbsd_chunk_info[current_chunk].c->offset +
407				 sz - size,
408				 size,
409				 part,
410				 type == PART_SWAP ? FS_SWAP : freebsd,
411				 fbsd_chunk_info[current_chunk].c->flags);
412		    record_fbsd_chunks(disks);
413		}
414	    }
415	    break;
416
417	case 'D':
418	    if (fbsd_chunk_info[current_chunk].type == PART_SLICE) {
419		msg = "Use the Master Partition Editor to delete one of these";
420		break;
421	    }
422	    Delete_Chunk(fbsd_chunk_info[current_chunk].d,
423			 fbsd_chunk_info[current_chunk].c);
424	    break;
425
426	case 27:	/* ESC */
427	    partitioning = FALSE;
428	    break;
429	}
430    }
431}
432
433int
434write_disks(struct disk **disks)
435{
436    int i;
437    extern u_char boot1[], boot2[];
438    extern u_char mbr[], bteasy17[];
439
440    dialog_clear();
441    if (!msgYesNo("Last Chance!  Are you sure you want to write out\nall your changes to disk?")) {
442	for (i = 0; disks[i]; i++) {
443	    if (contains_root_partition(disks[i]))
444		Set_Boot_Blocks(disks[i], boot1, boot2);
445	    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."))
446		Set_Boot_Mgr(disks[i], bteasy17);
447	    else if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?"))
448		Set_Boot_Mgr(disks[i], mbr);
449#if 0
450	    Write_Disk(disks[i]);
451#endif
452	}
453	return 0;
454    }
455    return 1;
456}
457