disks.c revision 8331
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.6 1995/05/07 05:58:56 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    PartType type;
70} fbsd_chunk_info[MAX_CHUNKS + 1];
71static int current_chunk;
72
73
74/* If the given disk has a root partition on it, return TRUE */
75static Boolean
76contains_root_partition(struct disk *d)
77{
78    struct chunk *c1;
79
80    if (!d->chunks)
81	msgFatal("Disk %s has no chunks!", d->name);
82    c1 = d->chunks->part;
83    while (c1) {
84	if (c1->type == freebsd) {
85	    struct chunk *c2 = c1->part;
86
87	    while (c2) {
88		if (c2->flags & CHUNK_IS_ROOT)
89		    return TRUE;
90		c2 = c2->next;
91	    }
92	}
93	c1 = c1->next;
94    }
95    return FALSE;
96}
97
98/* Locate a chunk by position on a specific disk somewhere */
99static struct chunk *
100find_chunk_by_loc(struct disk *d, u_long offset, u_long size)
101{
102    struct chunk *c1, *c2;
103
104    if (!d->chunks)
105	msgFatal("No chunk list for %s!", d->name);
106
107    for (c1 = d->chunks->part; c1; c1 = c1->next) {
108	if (c1->type == freebsd) {
109	    if (c1->offset == offset && c1->size == size)
110		return c1;
111	    for (c2 = c1->part; c2; c2 = c2->next) {
112		if (c2->type == part && c2->offset == offset &&
113		    c2->size == size)
114		    return c2;
115	    }
116	}
117    }
118    return NULL;
119}
120
121static Boolean
122check_conflict(char *name)
123{
124    int i;
125
126    for (i = 0; fbsd_chunk_info[i].d; i++)
127	if (fbsd_chunk_info[i].type == PART_FILESYSTEM &&
128	    !strcmp(fbsd_chunk_info[i].c->name, name))
129	    return TRUE;
130    return FALSE;
131}
132
133static int
134space_free(struct chunk *c)
135{
136    struct chunk *c1 = c->part;
137    int sz = c->size;
138
139    while (c1) {
140	if (c1->type != unused)
141	    sz -= c1->size;
142	c1 = c1->next;
143    }
144    if (sz < 0)
145	msgFatal("Partitions are larger than actual chunk??");
146    return sz;
147}
148
149static void
150record_fbsd_chunks(struct disk **disks)
151{
152    int i, j, p;
153    struct chunk *c1, *c2;
154
155    j = p = 0;
156    for (i = 0; disks[i]; i++) {
157	if (!disks[i]->chunks)
158	    msgFatal("No chunk list found for %s!", disks[i]->name);
159
160	/* Put the freebsd chunks first */
161	for (c1 = disks[i]->chunks->part; c1; c1 = c1->next) {
162	    if (c1->type == freebsd) {
163		fbsd_chunk_info[j].type = PART_SLICE;
164		fbsd_chunk_info[j].d = disks[i];
165		fbsd_chunk_info[j].c = c1;
166		++j;
167	    }
168	}
169    }
170    for (i = 0; disks[i]; i++) {
171	/* Then buzz through and pick up the partitions */
172	for (c1 = disks[i]->chunks->part; c1; c1 = c1->next) {
173	    if (c1->type == freebsd) {
174		for (c2 = c1->part; c2; c2 = c2->next) {
175		    if (c2->type == part) {
176			if (c2->subtype == FS_SWAP)
177			    fbsd_chunk_info[j].type = PART_SWAP;
178			else
179			    fbsd_chunk_info[j].type = PART_FILESYSTEM;
180			fbsd_chunk_info[j].d = disks[i];
181			fbsd_chunk_info[j].c = c2;
182			++j;
183		    }
184		}
185	    }
186	}
187    }
188    fbsd_chunk_info[j].d = NULL;
189    fbsd_chunk_info[j].c = NULL;
190    if (current_chunk >= j)
191	current_chunk = j  ? j - 1 : 0;
192}
193
194static PartInfo *
195new_part(char *mpoint, Boolean newfs)
196{
197    PartInfo *ret;
198
199    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
200    strncpy(ret->mountpoint, mpoint, FILENAME_MAX);
201    ret->newfs = newfs;
202    return ret;
203}
204
205PartInfo *
206get_mountpoint(struct chunk *c)
207{
208    char *val;
209    PartInfo *tmp;
210
211    val = msgGetInput(c && c->private ?
212		      ((PartInfo *)c->private)->mountpoint : NULL,
213		      "Please specify a mount point for the partition");
214    if (val) {
215	if (check_conflict(val)) {
216	    msgConfirm("You already have a mountpoint for %s assigned!", val);
217	    return NULL;
218	}
219	else if (!strcmp(val, "/")) {
220	    if (c && c->flags & CHUNK_PAST_1024) {
221msgConfirm("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!");
222		return NULL;
223	    }
224	    else if (c)
225		c->flags |= CHUNK_IS_ROOT;
226	}
227	else if (c)
228	    c->flags &= ~CHUNK_IS_ROOT;
229	safe_free(c ? c->private : NULL);
230	tmp = new_part(val, TRUE);
231	if (c) {
232	    c->private = tmp;
233	    c->private_free = safe_free;
234	}
235	return tmp;
236    }
237    return NULL;
238}
239
240static PartType
241get_partition_type(void)
242{
243    char selection[20];
244    static unsigned char *fs_types[] = {
245	"FS",
246	"A file system",
247	"Swap",
248	"A swap partition.",
249    };
250
251    if (!dialog_menu("Please choose a partition type",
252		    "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)) {
253	if (!strcmp(selection, "FS"))
254	    return PART_FILESYSTEM;
255	else if (!strcmp(selection, "Swap"))
256	    return PART_SWAP;
257    }
258    return PART_NONE;
259}
260
261#define MAX_MOUNT_NAME	12
262
263#define PART_PART_COL	0
264#define PART_MOUNT_COL	8
265#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 4)
266#define PART_NEWFS_COL	(PART_SIZE_COL + 8)
267#define PART_OFF	42
268
269/* How many mounted partitions to display in column before going to next */
270#define CHUNK_COLUMN_MAX	6
271
272static void
273print_fbsd_chunks(void)
274{
275    int i, j, srow, prow, pcol;
276    int sz;
277
278    attrset(A_REVERSE);
279    mvaddstr(0, 25, "FreeBSD Partition Editor");
280    attrset(A_NORMAL);
281
282    for (i = 0; i < 2; i++) {
283	attrset(A_UNDERLINE);
284	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF),
285		 "Part");
286	attrset(A_NORMAL);
287
288	attrset(A_UNDERLINE);
289	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF),
290		 "Mount");
291	attrset(A_NORMAL);
292
293	attrset(A_UNDERLINE);
294	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2,
295		 "Size");
296	attrset(A_NORMAL);
297
298	attrset(A_UNDERLINE);
299	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF),
300		 "Newfs");
301	attrset(A_NORMAL);
302    }
303
304    srow = CHUNK_SLICE_START_ROW;
305    prow = CHUNK_PART_START_ROW;
306
307    for (i = 0; fbsd_chunk_info[i].d; i++) {
308	if (i == current_chunk)
309	    attrset(A_REVERSE);
310	/* Is it a slice entry displayed at the top? */
311	if (fbsd_chunk_info[i].type == PART_SLICE) {
312	    sz = space_free(fbsd_chunk_info[i].c);
313	    mvprintw(srow++, 0,
314		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
315		     fbsd_chunk_info[i].d->name,
316		     fbsd_chunk_info[i].c->name, sz, (sz / 2048));
317	}
318	/* Otherwise it's a swap or filesystem entry, at the bottom */
319	else {
320	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
321
322	    memset(onestr, ' ', PART_OFF - 1);
323	    onestr[PART_OFF - 1] = '\0';
324	    /* Go for two columns */
325	    if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX))
326		pcol = PART_OFF;
327	    else
328		pcol = 0;
329	    memcpy(onestr + PART_PART_COL, fbsd_chunk_info[i].c->name,
330		   strlen(fbsd_chunk_info[i].c->name));
331	    if (fbsd_chunk_info[i].type == PART_FILESYSTEM) {
332		if (fbsd_chunk_info[i].c->private) {
333		    mountpoint = ((PartInfo *)fbsd_chunk_info[i].c->private)->mountpoint;
334		    newfs = ((PartInfo *)fbsd_chunk_info[i].c->private)->newfs ? "Y" : "N";
335		}
336		else {
337		    fbsd_chunk_info[i].c->private = new_part("", FALSE);
338		    fbsd_chunk_info[i].c->private_free = safe_free;
339		    mountpoint = " ";
340		    newfs = "N";
341		}
342	    }
343	    else {
344		mountpoint = "swap";
345		newfs = " ";
346	    }
347	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
348		onestr[PART_MOUNT_COL + j] = mountpoint[j];
349	    sprintf(num, "%4ldMB", fbsd_chunk_info[i].c->size ?
350		    fbsd_chunk_info[i].c->size / 2048 : 0);
351	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
352	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
353	    mvaddstr(prow, pcol, onestr);
354	    ++prow;
355	}
356	if (i == current_chunk)
357	    attrset(A_NORMAL);
358    }
359}
360
361static void
362print_command_summary()
363{
364    mvprintw(17, 0,
365	     "The following commands are valid here (upper or lower case):");
366    mvprintw(19, 0, "C = Create Partition   D = Delete Partition   M = Mount Partition");
367    mvprintw(20, 0, "N = Newfs Options      T = Toggle Newfs       ESC = Finish Partitioning");
368    mvprintw(21, 0, "The default target will be displayed in ");
369
370    attrset(A_REVERSE);
371    addstr("reverse video");
372    attrset(A_NORMAL);
373    mvprintw(22, 0, "Use F1 or ? to get more help");
374    move(0, 0);
375}
376
377void
378partition_disks(struct disk **disks)
379{
380    int sz, key = 0;
381    Boolean partitioning;
382    char *msg = NULL;
383    PartInfo *p;
384    PartType type;
385
386    dialog_clear();
387    partitioning = TRUE;
388    keypad(stdscr, TRUE);
389    record_fbsd_chunks(disks);
390
391    while (partitioning) {
392	clear();
393	print_fbsd_chunks();
394	print_command_summary();
395	if (msg) {
396	    attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL);
397	    beep();
398	    msg = NULL;
399	}
400	refresh();
401	key = toupper(getch());
402	switch (key) {
403
404	case KEY_UP:
405	case '-':
406	    if (current_chunk != 0)
407		--current_chunk;
408	    break;
409
410	case KEY_DOWN:
411	case '+':
412	case '\r':
413	case '\n':
414	    if (fbsd_chunk_info[current_chunk + 1].d)
415		++current_chunk;
416	    break;
417
418	case KEY_HOME:
419	    current_chunk = 0;
420	    break;
421
422	case KEY_END:
423	    while (fbsd_chunk_info[current_chunk + 1].d)
424		++current_chunk;
425	    break;
426
427	case KEY_F(1):
428	case '?':
429	    systemDisplayFile("partitioning.hlp");
430	    break;
431
432	case 'C':
433	    if (fbsd_chunk_info[current_chunk].type != PART_SLICE) {
434		msg = "Can't create a sub-partition here (that only works in master partitions)";
435		break;
436	    }
437	    sz = space_free(fbsd_chunk_info[current_chunk].c);
438	    if (sz <= FS_MIN_SIZE)
439		msg = "Not enough space to create additional FreeBSD partition";
440	    else {
441		char *val, *cp, tmp[20];
442		int size;
443
444		snprintf(tmp, 20, "%d", sz);
445		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M).");
446		if (val && (size = strtol(val, &cp, 0)) > 0) {
447		    struct chunk *tmp;
448		    u_long flags = 0;
449
450		    if (*cp && toupper(*cp) == 'M')
451			size *= 2048;
452
453		    type = get_partition_type();
454		    if (type == PART_NONE)
455			break;
456		    else if (type == PART_FILESYSTEM) {
457			if ((p = get_mountpoint(NULL)) == NULL)
458			    break;
459			else if (!strcmp(p->mountpoint, "/"))
460			    flags |= CHUNK_IS_ROOT;
461			else
462			    flags &= ~CHUNK_IS_ROOT;
463		    }
464		    else
465			p = NULL;
466		    Create_Chunk(fbsd_chunk_info[current_chunk].d,
467				 fbsd_chunk_info[current_chunk].c->offset +
468				 sz - size,
469				 size,
470				 part,
471				 (type == PART_SWAP) ? FS_SWAP : freebsd,
472				 flags);
473		    tmp = find_chunk_by_loc(fbsd_chunk_info[current_chunk].d,
474					    fbsd_chunk_info[current_chunk].c->offset + sz - size, size);
475		    if (!tmp)
476			msgConfirm("Unable to create the partition.  Too big?");
477		    else {
478			tmp->private = p;
479			tmp->private_free = safe_free;
480			record_fbsd_chunks(disks);
481		    }
482		}
483	    }
484	    break;
485
486	case 'D':	/* delete */
487	    if (fbsd_chunk_info[current_chunk].type == PART_SLICE) {
488		msg = "Use the Master Partition Editor to delete one of these";
489		break;
490	    }
491	    Delete_Chunk(fbsd_chunk_info[current_chunk].d,
492			 fbsd_chunk_info[current_chunk].c);
493	    record_fbsd_chunks(disks);
494	    break;
495
496	case 'M':	/* mount */
497	    switch(fbsd_chunk_info[current_chunk].type) {
498	    case PART_SLICE:
499		msg = "You can't mount one of these directly!";
500		break;
501
502	    case PART_SWAP:
503		msg = "You don't need to specify a mountpoint for a swap partition.";
504		break;
505
506	    case PART_FILESYSTEM:
507		p = get_mountpoint(fbsd_chunk_info[current_chunk].c);
508		if (p) {
509		    p->newfs = FALSE;
510		    record_fbsd_chunks(disks);
511		}
512		break;
513
514	    default:
515		msgFatal("Bogus partition under cursor???");
516		break;
517	    }
518	    break;
519
520	case 27:	/* ESC */
521	    partitioning = FALSE;
522	    break;
523	}
524    }
525}
526
527int
528write_disks(struct disk **disks)
529{
530    int i;
531    extern u_char boot1[], boot2[];
532    extern u_char mbr[], bteasy17[];
533
534    dialog_clear();
535    if (!msgYesNo("Last Chance!  Are you sure you want to write out\nall your changes to disk?")) {
536	for (i = 0; disks[i]; i++) {
537	    if (contains_root_partition(disks[i]))
538		Set_Boot_Blocks(disks[i], boot1, boot2);
539	    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."))
540		Set_Boot_Mgr(disks[i], bteasy17);
541	    else if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?"))
542		Set_Boot_Mgr(disks[i], mbr);
543	    Write_Disk(disks[i]);
544	}
545	return 0;
546    }
547    return 1;
548}
549