disks.c revision 8336
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.7 1995/05/07 22:07:51 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    strcpy(ret->newfs_cmd, "newfs");
202    ret->newfs = newfs;
203    return ret;
204}
205
206PartInfo *
207get_mountpoint(struct chunk *c)
208{
209    char *val;
210    PartInfo *tmp;
211
212    val = msgGetInput(c && c->private ?
213		      ((PartInfo *)c->private)->mountpoint : NULL,
214		      "Please specify a mount point for the partition");
215    if (val) {
216	if (check_conflict(val)) {
217	    msgConfirm("You already have a mountpoint for %s assigned!", val);
218	    return NULL;
219	}
220	else if (!strcmp(val, "/")) {
221	    if (c && c->flags & CHUNK_PAST_1024) {
222msgConfirm("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!");
223		return NULL;
224	    }
225	    else if (c)
226		c->flags |= CHUNK_IS_ROOT;
227	}
228	else if (c)
229	    c->flags &= ~CHUNK_IS_ROOT;
230	safe_free(c ? c->private : NULL);
231	tmp = new_part(val, TRUE);
232	if (c) {
233	    c->private = tmp;
234	    c->private_free = safe_free;
235	}
236	return tmp;
237    }
238    return NULL;
239}
240
241static PartType
242get_partition_type(void)
243{
244    char selection[20];
245    static unsigned char *fs_types[] = {
246	"FS",
247	"A file system",
248	"Swap",
249	"A swap partition.",
250    };
251
252    if (!dialog_menu("Please choose a partition type",
253		    "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)) {
254	if (!strcmp(selection, "FS"))
255	    return PART_FILESYSTEM;
256	else if (!strcmp(selection, "Swap"))
257	    return PART_SWAP;
258    }
259    return PART_NONE;
260}
261
262static void
263getNewfsCmd(PartInfo *p)
264{
265    char *val;
266
267    val = msgGetInput(p->newfs_cmd,
268		      "Please enter the newfs command and options you'd like to use in\ncreating this file system.");
269    if (val)
270	strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
271}
272
273
274#define MAX_MOUNT_NAME	12
275
276#define PART_PART_COL	0
277#define PART_MOUNT_COL	8
278#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 4)
279#define PART_NEWFS_COL	(PART_SIZE_COL + 8)
280#define PART_OFF	42
281
282/* How many mounted partitions to display in column before going to next */
283#define CHUNK_COLUMN_MAX	6
284
285static void
286print_fbsd_chunks(void)
287{
288    int i, j, srow, prow, pcol;
289    int sz;
290
291    attrset(A_REVERSE);
292    mvaddstr(0, 25, "FreeBSD Partition Editor");
293    attrset(A_NORMAL);
294
295    for (i = 0; i < 2; i++) {
296	attrset(A_UNDERLINE);
297	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF),
298		 "Part");
299	attrset(A_NORMAL);
300
301	attrset(A_UNDERLINE);
302	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF),
303		 "Mount");
304	attrset(A_NORMAL);
305
306	attrset(A_UNDERLINE);
307	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2,
308		 "Size");
309	attrset(A_NORMAL);
310
311	attrset(A_UNDERLINE);
312	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF),
313		 "Newfs");
314	attrset(A_NORMAL);
315    }
316
317    srow = CHUNK_SLICE_START_ROW;
318    prow = CHUNK_PART_START_ROW;
319
320    for (i = 0; fbsd_chunk_info[i].d; i++) {
321	if (i == current_chunk)
322	    attrset(A_REVERSE);
323	/* Is it a slice entry displayed at the top? */
324	if (fbsd_chunk_info[i].type == PART_SLICE) {
325	    sz = space_free(fbsd_chunk_info[i].c);
326	    mvprintw(srow++, 0,
327		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
328		     fbsd_chunk_info[i].d->name,
329		     fbsd_chunk_info[i].c->name, sz, (sz / 2048));
330	}
331	/* Otherwise it's a swap or filesystem entry, at the bottom */
332	else {
333	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
334
335	    memset(onestr, ' ', PART_OFF - 1);
336	    onestr[PART_OFF - 1] = '\0';
337	    /* Go for two columns */
338	    if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) {
339		pcol = PART_OFF;
340		prow = CHUNK_PART_START_ROW;
341	    }
342	    else
343		pcol = 0;
344	    memcpy(onestr + PART_PART_COL, fbsd_chunk_info[i].c->name,
345		   strlen(fbsd_chunk_info[i].c->name));
346	    if (fbsd_chunk_info[i].type == PART_FILESYSTEM) {
347		if (fbsd_chunk_info[i].c->private) {
348		    mountpoint = ((PartInfo *)fbsd_chunk_info[i].c->private)->mountpoint;
349		    newfs = ((PartInfo *)fbsd_chunk_info[i].c->private)->newfs ? "Y" : "N";
350		}
351		else {
352		    fbsd_chunk_info[i].c->private = new_part("", FALSE);
353		    fbsd_chunk_info[i].c->private_free = safe_free;
354		    mountpoint = " ";
355		    newfs = "N";
356		}
357	    }
358	    else {
359		mountpoint = "swap";
360		newfs = " ";
361	    }
362	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
363		onestr[PART_MOUNT_COL + j] = mountpoint[j];
364	    sprintf(num, "%4ldMB", fbsd_chunk_info[i].c->size ?
365		    fbsd_chunk_info[i].c->size / 2048 : 0);
366	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
367	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
368	    mvaddstr(prow, pcol, onestr);
369	    ++prow;
370	}
371	if (i == current_chunk)
372	    attrset(A_NORMAL);
373    }
374}
375
376static void
377print_command_summary()
378{
379    mvprintw(17, 0,
380	     "The following commands are valid here (upper or lower case):");
381    mvprintw(19, 0, "C = Create Partition   D = Delete Partition   M = Mount Partition");
382    mvprintw(20, 0, "N = Newfs Options      T = Toggle Newfs       ESC = Finish Partitioning");
383    mvprintw(21, 0, "The default target will be displayed in ");
384
385    attrset(A_REVERSE);
386    addstr("reverse video");
387    attrset(A_NORMAL);
388    mvprintw(22, 0, "Use F1 or ? to get more help");
389    move(0, 0);
390}
391
392void
393partition_disks(struct disk **disks)
394{
395    int sz, key = 0;
396    Boolean partitioning;
397    char *msg = NULL;
398    PartInfo *p;
399    PartType type;
400
401    dialog_clear();
402    partitioning = TRUE;
403    keypad(stdscr, TRUE);
404    record_fbsd_chunks(disks);
405
406    while (partitioning) {
407	clear();
408	print_fbsd_chunks();
409	print_command_summary();
410	if (msg) {
411	    attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL);
412	    beep();
413	    msg = NULL;
414	}
415	refresh();
416	key = toupper(getch());
417	switch (key) {
418
419	case KEY_UP:
420	case '-':
421	    if (current_chunk != 0)
422		--current_chunk;
423	    break;
424
425	case KEY_DOWN:
426	case '+':
427	case '\r':
428	case '\n':
429	    if (fbsd_chunk_info[current_chunk + 1].d)
430		++current_chunk;
431	    break;
432
433	case KEY_HOME:
434	    current_chunk = 0;
435	    break;
436
437	case KEY_END:
438	    while (fbsd_chunk_info[current_chunk + 1].d)
439		++current_chunk;
440	    break;
441
442	case KEY_F(1):
443	case '?':
444	    systemDisplayFile("partitioning.hlp");
445	    break;
446
447	case 'C':
448	    if (fbsd_chunk_info[current_chunk].type != PART_SLICE) {
449		msg = "Can't create a sub-partition here (that only works in master partitions)";
450		break;
451	    }
452	    sz = space_free(fbsd_chunk_info[current_chunk].c);
453	    if (sz <= FS_MIN_SIZE)
454		msg = "Not enough space to create additional FreeBSD partition";
455	    else {
456		char *val, *cp, tmp[20];
457		int size;
458
459		snprintf(tmp, 20, "%d", sz);
460		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M).");
461		if (val && (size = strtol(val, &cp, 0)) > 0) {
462		    struct chunk *tmp;
463		    u_long flags = 0;
464
465		    if (*cp && toupper(*cp) == 'M')
466			size *= 2048;
467
468		    type = get_partition_type();
469		    if (type == PART_NONE)
470			break;
471		    else if (type == PART_FILESYSTEM) {
472			if ((p = get_mountpoint(NULL)) == NULL)
473			    break;
474			else if (!strcmp(p->mountpoint, "/"))
475			    flags |= CHUNK_IS_ROOT;
476			else
477			    flags &= ~CHUNK_IS_ROOT;
478		    }
479		    else
480			p = NULL;
481		    Create_Chunk(fbsd_chunk_info[current_chunk].d,
482				 fbsd_chunk_info[current_chunk].c->offset +
483				 sz - size,
484				 size,
485				 part,
486				 (type == PART_SWAP) ? FS_SWAP : freebsd,
487				 flags);
488		    tmp = find_chunk_by_loc(fbsd_chunk_info[current_chunk].d,
489					    fbsd_chunk_info[current_chunk].c->offset + sz - size, size);
490		    if (!tmp)
491			msgConfirm("Unable to create the partition.  Too big?");
492		    else {
493			tmp->private = p;
494			tmp->private_free = safe_free;
495			record_fbsd_chunks(disks);
496		    }
497		}
498	    }
499	    break;
500
501	case 'D':	/* delete */
502	    if (fbsd_chunk_info[current_chunk].type == PART_SLICE) {
503		msg = "Use the Master Partition Editor to delete one of these";
504		break;
505	    }
506	    Delete_Chunk(fbsd_chunk_info[current_chunk].d,
507			 fbsd_chunk_info[current_chunk].c);
508	    record_fbsd_chunks(disks);
509	    break;
510
511	case 'M':	/* mount */
512	    switch(fbsd_chunk_info[current_chunk].type) {
513	    case PART_SLICE:
514		msg = "You can't mount one of these directly!";
515		break;
516
517	    case PART_SWAP:
518		msg = "You don't need to specify a mountpoint for a swap partition.";
519		break;
520
521	    case PART_FILESYSTEM:
522		p = get_mountpoint(fbsd_chunk_info[current_chunk].c);
523		if (p) {
524		    p->newfs = FALSE;
525		    record_fbsd_chunks(disks);
526		}
527		break;
528
529	    default:
530		msgFatal("Bogus partition under cursor???");
531		break;
532	    }
533	    break;
534
535	case 'N':	/* Set newfs options */
536	    if (fbsd_chunk_info[current_chunk].c->private &&
537		((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs)
538		getNewfsCmd(fbsd_chunk_info[current_chunk].c->private);
539	    else
540		msg = "newfs options not applicable for this partition";
541	    break;
542
543	case 'T':	/* Toggle newfs state */
544	    if (fbsd_chunk_info[current_chunk].c->private)
545		((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs = !((PartInfo *)fbsd_chunk_info[current_chunk].c->private)->newfs;
546	    else
547		msg = "Set a mount point first.";
548	    break;
549
550	case 27:	/* ESC */
551	    partitioning = FALSE;
552	    break;
553
554	default:
555	    beep();
556	    msg = "Type F1 or ? for help";
557	    break;
558	}
559    }
560}
561
562int
563write_disks(struct disk **disks)
564{
565    int i;
566    extern u_char boot1[], boot2[];
567    extern u_char mbr[], bteasy17[];
568
569    dialog_clear();
570    if (!msgYesNo("Last Chance!  Are you sure you want to write out\nall your changes to disk?")) {
571	for (i = 0; disks[i]; i++) {
572	    if (contains_root_partition(disks[i]))
573		Set_Boot_Blocks(disks[i], boot1, boot2);
574	    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."))
575		Set_Boot_Mgr(disks[i], bteasy17);
576	    else if (i == 0 && !msgYesNo("Would you like to remove an existing boot manager?"))
577		Set_Boot_Mgr(disks[i], mbr);
578	    Write_Disk(disks[i]);
579	}
580	return 0;
581    }
582    return 1;
583}
584