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