label.c revision 8576
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: label.c,v 1.2 1995/05/16 11:37:16 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 * Everything to do with editing the contents of disk labels.
50 */
51
52/* A nice message we use a lot in the disklabel editor */
53#define MSG_NOT_APPLICABLE	"That option is not applicable here"
54
55/*
56 * I make some pretty gross assumptions about having a max of 50 chunks
57 * total - 8 slices and 42 partitions.  I can't easily display many more
58 * than that on the screen at once!
59 *
60 * For 2.1 I'll revisit this and try to make it more dynamic, but since
61 * this will catch 99.99% of all possible cases, I'm not too worried.
62 */
63#define MAX_CHUNKS	50
64
65/* Where to start printing the freebsd slices */
66#define CHUNK_SLICE_START_ROW		2
67#define CHUNK_PART_START_ROW		10
68
69/* The smallest filesystem we're willing to create */
70#define FS_MIN_SIZE			2048
71
72
73/* All the chunks currently displayed on the screen */
74static struct {
75    struct disk *d;
76    struct chunk *c;
77    PartType type;
78} label_chunk_info[MAX_CHUNKS + 1];
79static int here;
80
81/* See if we're already using a desired partition name */
82static Boolean
83check_conflict(char *name)
84{
85    int i;
86
87    for (i = 0; label_chunk_info[i].d; i++)
88	if (label_chunk_info[i].type == PART_FILESYSTEM
89	    && label_chunk_info[i].c->private
90	    && !strcmp(((PartInfo *)label_chunk_info[i].c->private)->mountpoint, name))
91	    return TRUE;
92    return FALSE;
93}
94
95/* How much space is in this FreeBSD slice? */
96static int
97space_free(struct chunk *c)
98{
99    struct chunk *c1 = c->part;
100    int sz = c->size;
101
102    while (c1) {
103	if (c1->type != unused)
104	    sz -= c1->size;
105	c1 = c1->next;
106    }
107    if (sz < 0)
108	msgFatal("Partitions are larger than actual chunk??");
109    return sz;
110}
111
112/* Snapshot the current situation into the displayed chunks structure */
113static void
114record_label_chunks()
115{
116    int i, j, p;
117    struct chunk *c1, *c2;
118    Device **devs;
119    Disk *d;
120
121    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
122    if (!devs) {
123	msgConfirm("No disks found!");
124	return;
125    }
126
127    j = p = 0;
128    /* First buzz through and pick up the FreeBSD slices */
129    for (i = 0; devs[i]; i++) {
130	if (!devs[i]->enabled)
131	    continue;
132	d = (Disk *)devs[i]->private;
133	if (!d->chunks)
134	    msgFatal("No chunk list found for %s!", d->name);
135
136	/* Put the slice entries first */
137	for (c1 = d->chunks->part; c1; c1 = c1->next) {
138	    if (c1->type == freebsd) {
139		label_chunk_info[j].type = PART_SLICE;
140		label_chunk_info[j].d = d;
141		label_chunk_info[j].c = c1;
142		++j;
143	    }
144	}
145    }
146    /* Now run through again and get the FreeBSD partition entries */
147    for (i = 0; devs[i]; i++) {
148	if (!devs[i]->enabled)
149	    continue;
150	d = (Disk *)devs[i]->private;
151	/* Then buzz through and pick up the partitions */
152	for (c1 = d->chunks->part; c1; c1 = c1->next) {
153	    if (c1->type == freebsd) {
154		for (c2 = c1->part; c2; c2 = c2->next) {
155		    if (c2->type == part) {
156			if (c2->subtype == FS_SWAP)
157			    label_chunk_info[j].type = PART_SWAP;
158			else
159			    label_chunk_info[j].type = PART_FILESYSTEM;
160			label_chunk_info[j].d = d;
161			label_chunk_info[j].c = c2;
162			++j;
163		    }
164		}
165	    }
166	    else if (c1->type == fat) {
167		label_chunk_info[j].type = PART_FAT;
168		label_chunk_info[j].d = d;
169		label_chunk_info[j].c = c1;
170	    }
171	}
172    }
173    label_chunk_info[j].d = NULL;
174    label_chunk_info[j].c = NULL;
175    if (here >= j)
176	here = j  ? j - 1 : 0;
177}
178
179/* A new partition entry */
180static PartInfo *
181new_part(char *mpoint, Boolean newfs)
182{
183    PartInfo *ret;
184
185    ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
186    strncpy(ret->mountpoint, mpoint, FILENAME_MAX);
187    strcpy(ret->newfs_cmd, "newfs");
188    ret->newfs = newfs;
189    return ret;
190}
191
192/* Get the mountpoint for a partition and save it away */
193PartInfo *
194get_mountpoint(struct chunk *parent, struct chunk *me)
195{
196    char *val;
197    PartInfo *tmp;
198
199    val = msgGetInput(me && me->private ? ((PartInfo *)me->private)->mountpoint : NULL,
200		      "Please specify a mount point for the partition");
201    if (val) {
202	/* Is it just the same value? */
203	if (me && me->private && !strcmp(((PartInfo *)me->private)->mountpoint, val))
204	    return NULL;
205	if (check_conflict(val)) {
206	    msgConfirm("You already have a mount point for %s assigned!", val);
207	    return NULL;
208	}
209	else if (*val != '/') {
210	    msgConfirm("Mount point must start with a / character");
211	    return NULL;
212	}
213	else if (!strcmp(val, "/")) {
214	    if (parent) {
215	    	if (parent->flags & CHUNK_PAST_1024) {
216		    msgConfirm("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!");
217		    return NULL;
218		}
219		else if (!(parent->flags & CHUNK_BSD_COMPAT)) {
220		    msgConfirm("This region cannot be used for your root partition as\nthe FreeBSD boot code cannot deal with a root partition created in\nsuch a region.  Please choose another partition for this.");
221		    return NULL;
222		}
223	    }
224	    if (me)
225		me->flags |= CHUNK_IS_ROOT;
226	}
227	else if (me)
228	    me->flags &= ~CHUNK_IS_ROOT;
229	safe_free(me ? me->private : NULL);
230	tmp = new_part(val, TRUE);
231	if (me) {
232	    me->private = tmp;
233	    me->private_free = safe_free;
234	}
235	return tmp;
236    }
237    return NULL;
238}
239
240/* Get the type of the new partiton */
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
262/* If the user wants a special newfs command for this, set it */
263static void
264getNewfsCmd(PartInfo *p)
265{
266    char *val;
267
268    val = msgGetInput(p->newfs_cmd,
269		      "Please enter the newfs command and options you'd like to use in\ncreating this file system.");
270    if (val)
271	strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
272}
273
274
275#define MAX_MOUNT_NAME	12
276
277#define PART_PART_COL	0
278#define PART_MOUNT_COL	8
279#define PART_SIZE_COL	(PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
280#define PART_NEWFS_COL	(PART_SIZE_COL + 7)
281#define PART_OFF	38
282
283/* How many mounted partitions to display in column before going to next */
284#define CHUNK_COLUMN_MAX	6
285
286/* stick this all up on the screen */
287static void
288print_label_chunks(void)
289{
290    int i, j, srow, prow, pcol;
291    int sz;
292
293    clear();
294    attrset(A_REVERSE);
295    mvaddstr(0, 25, "FreeBSD Disklabel Editor");
296    attrset(A_NORMAL);
297
298    for (i = 0; i < 2; i++) {
299	attrset(A_UNDERLINE);
300	mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF),
301		 "Part");
302	attrset(A_NORMAL);
303
304	attrset(A_UNDERLINE);
305	mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF),
306		 "Mount");
307	attrset(A_NORMAL);
308
309	attrset(A_UNDERLINE);
310	mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2,
311		 "Size");
312	attrset(A_NORMAL);
313
314	attrset(A_UNDERLINE);
315	mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF),
316		 "Newfs");
317	attrset(A_NORMAL);
318    }
319
320    srow = CHUNK_SLICE_START_ROW;
321    prow = CHUNK_PART_START_ROW;
322    pcol = 0;
323
324    for (i = 0; label_chunk_info[i].d; i++) {
325	if (i == here)
326	    attrset(A_REVERSE);
327	/* Is it a slice entry displayed at the top? */
328	if (label_chunk_info[i].type == PART_SLICE) {
329	    sz = space_free(label_chunk_info[i].c);
330	    mvprintw(srow++, 0,
331		     "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
332		     label_chunk_info[i].d->name,
333		     label_chunk_info[i].c->name, sz, (sz / 2048));
334	}
335	/* Otherwise it's a DOS, swap or filesystem entry, at the bottom */
336	else {
337	    char onestr[PART_OFF], num[10], *mountpoint, *newfs;
338
339	    /*
340	     * We copy this into a blank-padded string so that it looks like
341	     * a solid bar in reverse-video
342	     */
343	    memset(onestr, ' ', PART_OFF - 1);
344	    onestr[PART_OFF - 1] = '\0';
345	    /* Go for two columns */
346	    if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) {
347		pcol = PART_OFF;
348		prow = CHUNK_PART_START_ROW;
349	    }
350	    memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name,
351		   strlen(label_chunk_info[i].c->name));
352	    /* If it's a filesystem, display the mountpoint */
353	    if (label_chunk_info[i].type == PART_FILESYSTEM) {
354		if (label_chunk_info[i].c->private == NULL) {
355		    static int mnt = 0;
356		    char foo[10];
357
358		    /*
359		     * Hmm!  A partition that must have already been here.
360		     * Fill in a fake mountpoint and register it
361		     */
362		    sprintf(foo, "/mnt%d", mnt++);
363		    label_chunk_info[i].c->private = new_part(foo, FALSE);
364		    label_chunk_info[i].c->private_free = safe_free;
365		}
366		mountpoint = ((PartInfo *)label_chunk_info[i].c->private)->mountpoint;
367		newfs = ((PartInfo *)label_chunk_info[i].c->private)->newfs ? "Y" : "N";
368	    }
369	    else if (label_chunk_info[i].type == PART_SWAP) {
370		mountpoint = "swap";
371		newfs = " ";
372	    }
373	    else if (label_chunk_info[i].type == PART_FAT) {
374		mountpoint = "DOS FAT";
375		newfs = "*";
376	    }
377	    else {
378		mountpoint = "<unknown>";
379		newfs = "*";
380	    }
381	    for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
382		onestr[PART_MOUNT_COL + j] = mountpoint[j];
383	    snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ?
384		    label_chunk_info[i].c->size / 2048 : 0);
385	    memcpy(onestr + PART_SIZE_COL, num, strlen(num));
386	    memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
387	    onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
388	    mvaddstr(prow, pcol, onestr);
389	    ++prow;
390	}
391	if (i == here)
392	    attrset(A_NORMAL);
393    }
394}
395
396static void
397print_command_summary()
398{
399    mvprintw(17, 0,
400	     "The following commands are valid here (upper or lower case):");
401    mvprintw(19, 0, "C = Create Partition   D = Delete Partition   M = Mount Partition");
402    mvprintw(20, 0, "N = Newfs Options      T = Toggle Newfs       ESC = Exit this screen");
403    mvprintw(21, 0, "The default target will be displayed in ");
404
405    attrset(A_REVERSE);
406    addstr("reverse video.");
407    attrset(A_NORMAL);
408    mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to move.");
409    move(0, 0);
410}
411
412int
413diskLabelEditor(char *str)
414{
415    int sz, key = 0;
416    Boolean labeling;
417    char *msg = NULL;
418    PartInfo *p;
419    PartType type;
420
421    labeling = TRUE;
422    keypad(stdscr, TRUE);
423    record_label_chunks();
424
425    if (!getenv(DISK_PARTITIONED)) {
426	msgConfirm("You need to partition your disk(s) before you can assign disk labels.");
427	return 0;
428    }
429    while (labeling) {
430	print_label_chunks();
431	print_command_summary();
432	if (msg) {
433	    attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL);
434	    beep();
435	    msg = NULL;
436	}
437	refresh();
438	key = toupper(getch());
439	switch (key) {
440
441	case KEY_UP:
442	case '-':
443	    if (here != 0)
444		--here;
445	    break;
446
447	case KEY_DOWN:
448	case '+':
449	case '\r':
450	case '\n':
451	    if (label_chunk_info[here + 1].d)
452		++here;
453	    break;
454
455	case KEY_HOME:
456	    here = 0;
457	    break;
458
459	case KEY_END:
460	    while (label_chunk_info[here + 1].d)
461		++here;
462	    break;
463
464	case KEY_F(1):
465	case '?':
466	    systemDisplayFile("disklabel.hlp");
467	    break;
468
469	case 'C':
470	    if (label_chunk_info[here].type != PART_SLICE) {
471		msg = "You can only do this in a master partition (see top of screen)";
472		break;
473	    }
474	    sz = space_free(label_chunk_info[here].c);
475	    if (sz <= FS_MIN_SIZE)
476		msg = "Not enough space to create additional FreeBSD partition";
477	    else {
478		char *val, *cp, tmp[20];
479		int size;
480
481		snprintf(tmp, 20, "%d", sz);
482		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M).");
483		if (val && (size = strtol(val, &cp, 0)) > 0) {
484		    struct chunk *tmp;
485		    u_long flags = 0;
486
487		    if (*cp && toupper(*cp) == 'M')
488			size *= 2048;
489
490		    type = get_partition_type();
491		    if (type == PART_NONE)
492			break;
493		    else if (type == PART_FILESYSTEM) {
494			if ((p = get_mountpoint(label_chunk_info[here].c, NULL)) == NULL)
495			    break;
496			else if (!strcmp(p->mountpoint, "/"))
497			    flags |= CHUNK_IS_ROOT;
498			else
499			    flags &= ~CHUNK_IS_ROOT;
500		    }
501		    else
502			p = NULL;
503
504		    tmp = Create_Chunk_DWIM(label_chunk_info[here].d,
505					    label_chunk_info[here].c,
506					    size, part,
507					    (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
508					    flags);
509		    if (!tmp)
510			msgConfirm("Unable to create the partition. Too big?");
511		    else {
512			tmp->private = p;
513			tmp->private_free = safe_free;
514			record_label_chunks();
515		    }
516		}
517	    }
518	    break;
519
520	case 'D':	/* delete */
521	    if (label_chunk_info[here].type == PART_SLICE) {
522		msg = MSG_NOT_APPLICABLE;
523		break;
524	    }
525	    else if (label_chunk_info[here].type == PART_FAT) {
526		msg = "Use the Disk Partition Editor to delete this";
527		break;
528	    }
529	    Delete_Chunk(label_chunk_info[here].d, label_chunk_info[here].c);
530	    record_label_chunks();
531	    break;
532
533	case 'M':	/* mount */
534	    switch(label_chunk_info[here].type) {
535	    case PART_SLICE:
536		msg = MSG_NOT_APPLICABLE;
537		break;
538
539	    case PART_SWAP:
540		msg = "You don't need to specify a mountpoint for a swap partition.";
541		break;
542
543	    case PART_FAT:
544	    case PART_FILESYSTEM:
545		p = get_mountpoint(NULL, label_chunk_info[here].c);
546		if (p) {
547		    p->newfs = FALSE;
548		    record_label_chunks();
549		}
550		break;
551
552	    default:
553		msgFatal("Bogus partition under cursor???");
554		break;
555	    }
556	    break;
557
558	case 'N':	/* Set newfs options */
559	    if (label_chunk_info[here].c->private &&
560		((PartInfo *)label_chunk_info[here].c->private)->newfs)
561		getNewfsCmd(label_chunk_info[here].c->private);
562	    else
563		msg = MSG_NOT_APPLICABLE;
564	    break;
565
566	case 'T':	/* Toggle newfs state */
567	    if (label_chunk_info[here].type == PART_FILESYSTEM &&
568		label_chunk_info[here].c->private)
569		((PartInfo *)label_chunk_info[here].c->private)->newfs =
570		    !((PartInfo *)label_chunk_info[here].c->private)->newfs;
571	    else
572		msg = MSG_NOT_APPLICABLE;
573	    break;
574
575	case 'W':
576	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\nThis is an entirely undocumented feature which you are not\nexpected to understand!")) {
577		int i;
578		Device **devs;
579
580		dialog_clear();
581		end_dialog();
582		DialogActive = FALSE;
583		devs = deviceFind(NULL, DEVICE_TYPE_DISK);
584		if (!devs) {
585		    msgConfirm("Can't find any disk devicse!");
586		    break;
587		}
588		for (i = 0; ((Disk *)devs[i]->private); i++)
589		    slice_wizard(((Disk *)devs[i]->private));
590		dialog_clear();
591		DialogActive = TRUE;
592		record_label_chunks();
593	    }
594	    else
595		msg = "A most prudent choice!";
596	    break;
597
598	case 27:	/* ESC */
599	    labeling = FALSE;
600	    break;
601
602	default:
603	    beep();
604	    msg = "Type F1 or ? for help";
605	    break;
606	}
607    }
608    variable_set2(DISK_LABELLED, "yes");
609    clear();
610    refresh();
611    return 0;
612}
613
614
615
616