Deleted Added
full compact
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.19 1995/05/21 17:53:27 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 11
68
69/* The smallest filesystem we're willing to create */
70#define FS_MIN_SIZE 2048
71
72/* The smallest root filesystem we're willing to create */
73#define ROOT_MIN_SIZE 40960 /* 20MB */
74
75/* All the chunks currently displayed on the screen */
76static struct {
77 struct disk *d;
78 struct chunk *c;
79 PartType type;
80} label_chunk_info[MAX_CHUNKS + 1];
81static int here;
82
83/* See if we're already using a desired partition name */
84static Boolean
85check_conflict(char *name)
86{
87 int i;
88
89 for (i = 0; label_chunk_info[i].d; i++)
90 if (label_chunk_info[i].type == PART_FILESYSTEM
91 && label_chunk_info[i].c->private
92 && !strcmp(((PartInfo *)label_chunk_info[i].c->private)->mountpoint, name))
93 return TRUE;
94 return FALSE;
95}
96
97/* How much space is in this FreeBSD slice? */
98static int
99space_free(struct chunk *c)
100{
101 struct chunk *c1 = c->part;
102 int sz = c->size;
103
104 while (c1) {
105 if (c1->type != unused)
106 sz -= c1->size;
107 c1 = c1->next;
108 }
109 if (sz < 0)
110 msgFatal("Partitions are larger than actual chunk??");
111 return sz;
112}
113
114/* Snapshot the current situation into the displayed chunks structure */
115static void
116record_label_chunks()
117{
118 int i, j, p;
119 struct chunk *c1, *c2;
120 Device **devs;
121 Disk *d;
122
123 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
124 if (!devs) {
125 msgConfirm("No disks found!");
126 return;
127 }
128
129 j = p = 0;
130 /* First buzz through and pick up the FreeBSD slices */
131 for (i = 0; devs[i]; i++) {
132 if (!devs[i]->enabled)
133 continue;
134 d = (Disk *)devs[i]->private;
135 if (!d->chunks)
136 msgFatal("No chunk list found for %s!", d->name);
137
138 /* Put the slice entries first */
139 for (c1 = d->chunks->part; c1; c1 = c1->next) {
140 if (c1->type == freebsd) {
141 label_chunk_info[j].type = PART_SLICE;
142 label_chunk_info[j].d = d;
143 label_chunk_info[j].c = c1;
144 ++j;
145 }
146 }
147 }
148 /* Now run through again and get the FreeBSD partition entries */
149 for (i = 0; devs[i]; i++) {
150 if (!devs[i]->enabled)
151 continue;
152 d = (Disk *)devs[i]->private;
153 /* Then buzz through and pick up the partitions */
154 for (c1 = d->chunks->part; c1; c1 = c1->next) {
155 if (c1->type == freebsd) {
156 for (c2 = c1->part; c2; c2 = c2->next) {
157 if (c2->type == part) {
158 if (c2->subtype == FS_SWAP)
159 label_chunk_info[j].type = PART_SWAP;
160 else
161 label_chunk_info[j].type = PART_FILESYSTEM;
162 label_chunk_info[j].d = d;
163 label_chunk_info[j].c = c2;
164 ++j;
165 }
166 }
167 }
168 else if (c1->type == fat) {
169 label_chunk_info[j].type = PART_FAT;
170 label_chunk_info[j].d = d;
171 label_chunk_info[j].c = c1;
172 }
173 }
174 }
175 label_chunk_info[j].d = NULL;
176 label_chunk_info[j].c = NULL;
177 if (here >= j)
178 here = j ? j - 1 : 0;
179}
180
181/* A new partition entry */
182static PartInfo *
183new_part(char *mpoint, Boolean newfs, u_long size)
184{
185 PartInfo *ret;
186 u_long target,divisor;
187
188 ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
189 strncpy(ret->mountpoint, mpoint, FILENAME_MAX);
190 strcpy(ret->newfs_cmd, "newfs");
191 ret->newfs = newfs;
192 if (!size)
193 return ret;
194 for(target = size; target; target--) {
195 for(divisor = 4096 ; divisor > 1023; divisor--) {
196 if (!(target % divisor)) {
197 sprintf(ret->newfs_cmd+strlen(ret->newfs_cmd),
198 " -u %ld",divisor);
199 return ret;
200 }
201 }
202 }
203 return ret;
204}
205
206/* Get the mountpoint for a partition and save it away */
207PartInfo *
208get_mountpoint(struct chunk *old)
209{
210 char *val;
211 PartInfo *tmp;
212
213 dialog_clear(); clear();
214 val = msgGetInput(old && old->private ? ((PartInfo *)old->private)->mountpoint : NULL,
215 "Please specify a mount point for the partition");
216 if (!val)
217 return NULL;
218
219 /* Is it just the same value? */
220 if (old && old->private && !strcmp(((PartInfo *)old->private)->mountpoint, val))
221 return NULL;
222 if (check_conflict(val)) {
223 msgConfirm("You already have a mount point for %s assigned!", val);
224 return NULL;
225 }
226 if (*val != '/') {
227 msgConfirm("Mount point must start with a / character");
228 return NULL;
229 }
230 if (!strcmp(val, "/")) {
231 if (old)
232 old->flags |= CHUNK_IS_ROOT;
233 } else if (old) {
234 old->flags &= ~CHUNK_IS_ROOT;
235 }
236 safe_free(old ? old->private : NULL);
237 tmp = new_part(val, TRUE, 0);
238 if (old) {
239 old->private = tmp;
240 old->private_free = safe_free;
241 }
242 return tmp;
243}
244
245/* Get the type of the new partiton */
246static PartType
247get_partition_type(void)
248{
249 char selection[20];
250 int i;
251
252 static unsigned char *fs_types[] = {
253 "FS",
254 "A file system",
255 "Swap",
256 "A swap partition.",
257 };
258 dialog_clear(); clear();
259 i = dialog_menu("Please choose a partition type",
260 "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);
261 if (!i) {
262 if (!strcmp(selection, "FS"))
263 return PART_FILESYSTEM;
264 else if (!strcmp(selection, "Swap"))
265 return PART_SWAP;
266 }
267 return PART_NONE;
268}
269
270/* If the user wants a special newfs command for this, set it */
271static void
272getNewfsCmd(PartInfo *p)
273{
274 char *val;
275
276 val = msgGetInput(p->newfs_cmd,
277 "Please enter the newfs command and options you'd like to use in\ncreating this file system.");
278 if (val)
279 strncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
280}
281
282
283#define MAX_MOUNT_NAME 12
284
285#define PART_PART_COL 0
286#define PART_MOUNT_COL 8
287#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
288#define PART_NEWFS_COL (PART_SIZE_COL + 7)
289#define PART_OFF 38
290
291/* How many mounted partitions to display in column before going to next */
292#define CHUNK_COLUMN_MAX 5
293
294/* stick this all up on the screen */
295static void
296print_label_chunks(void)
297{
298 int i, j, srow, prow, pcol;
299 int sz;
300
301 attrset(A_REVERSE);
302 mvaddstr(0, 25, "FreeBSD Disklabel Editor");
303 clrtobot();
304 attrset(A_NORMAL);
305
306 for (i = 0; i < 2; i++) {
307 mvaddstr(CHUNK_PART_START_ROW - 2, PART_PART_COL + (i * PART_OFF), "Part");
308 mvaddstr(CHUNK_PART_START_ROW - 1, PART_PART_COL + (i * PART_OFF), "----");
309
310 mvaddstr(CHUNK_PART_START_ROW - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
311 mvaddstr(CHUNK_PART_START_ROW - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
312
313 mvaddstr(CHUNK_PART_START_ROW - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
314 mvaddstr(CHUNK_PART_START_ROW - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
315
316 mvaddstr(CHUNK_PART_START_ROW - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
317 mvaddstr(CHUNK_PART_START_ROW - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
318 }
319 srow = CHUNK_SLICE_START_ROW;
320 prow = CHUNK_PART_START_ROW;
321 pcol = 0;
322
323 for (i = 0; label_chunk_info[i].d; i++) {
324 if (i == here)
325 attrset(A_REVERSE);
326 /* Is it a slice entry displayed at the top? */
327 if (label_chunk_info[i].type == PART_SLICE) {
328 sz = space_free(label_chunk_info[i].c);
329 mvprintw(srow++, 0,
330 "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
331 label_chunk_info[i].d->name,
332 label_chunk_info[i].c->name, sz, (sz / 2048));
333 }
334 /* Otherwise it's a DOS, swap or filesystem entry, at the bottom */
335 else {
336 char onestr[PART_OFF], num[10], *mountpoint, *newfs;
337
338 /*
339 * We copy this into a blank-padded string so that it looks like
340 * a solid bar in reverse-video
341 */
342 memset(onestr, ' ', PART_OFF - 1);
343 onestr[PART_OFF - 1] = '\0';
344 /* Go for two columns */
345 if (prow == (CHUNK_PART_START_ROW + CHUNK_COLUMN_MAX)) {
346 pcol = PART_OFF;
347 prow = CHUNK_PART_START_ROW;
348 }
349 memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name,
350 strlen(label_chunk_info[i].c->name));
351 /* If it's a filesystem, display the mountpoint */
352 if (label_chunk_info[i].type == PART_FILESYSTEM) {
353 if (label_chunk_info[i].c->private == NULL) {
354 static int mnt = 0;
355 char foo[10];
356
357 /*
358 * Hmm! A partition that must have already been here.
359 * Fill in a fake mountpoint and register it
360 */
361 sprintf(foo, "/mnt%d", mnt++);
362 label_chunk_info[i].c->private =
363 new_part(foo, FALSE,label_chunk_info[i].c->size);
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 clear();
430 while (labeling) {
431 print_label_chunks();
432 print_command_summary();
433 if (msg) {
434 attrset(A_REVERSE); mvprintw(23, 0, msg); attrset(A_NORMAL);
435 beep();
436 msg = NULL;
437 }
438 refresh();
439 key = toupper(getch());
440 switch (key) {
441
442 case KEY_UP:
443 case '-':
444 if (here != 0)
445 --here;
446 break;
447
448 case KEY_DOWN:
449 case '+':
450 case '\r':
451 case '\n':
452 if (label_chunk_info[here + 1].d)
453 ++here;
454 break;
455
456 case KEY_HOME:
457 here = 0;
458 break;
459
460 case KEY_END:
461 while (label_chunk_info[here + 1].d)
462 ++here;
463 break;
464
465 case KEY_F(1):
466 case '?':
467 systemDisplayFile("disklabel.hlp");
468 break;
469
470 case 'C':
471 if (label_chunk_info[here].type != PART_SLICE) {
472 msg = "You can only do this in a master partition (see top of screen)";
473 break;
474 }
475 sz = space_free(label_chunk_info[here].c);
476 if (sz <= FS_MIN_SIZE) {
477 msg = "Not enough space to create additional FreeBSD partition";
478 break;
479 }
480 {
481 char *val, *cp, tmpb[20];
482 int size;
483 struct chunk *tmp;
484 u_long flags = 0;
485
486 snprintf(tmpb, 20, "%d", sz);
487 val = msgGetInput(tmpb, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M).");
488 if (!val || (size = strtol(val, &cp, 0)) <= 0)
489 break;
490
491 if (*cp && toupper(*cp) == 'M')
492 size *= 2048;
493
494 type = get_partition_type();
495 if (type == PART_NONE)
496 break;
497
498 if (type == PART_FILESYSTEM) {
499 if ((p = get_mountpoint(NULL)) == NULL)
500 break;
501 else if (!strcmp(p->mountpoint, "/"))
502 flags |= CHUNK_IS_ROOT;
503 else
504 flags &= ~CHUNK_IS_ROOT;
505 } else
506 p = NULL;
507
508 if ((flags & CHUNK_IS_ROOT)) {
509 if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
510 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 location. Please choose another location for your root\npartition and try again!");
511 break;
512 }
513 if (size < ROOT_MIN_SIZE) {
514 msgConfirm("This is too small a size for a root partition. For a variety of\nreasons, root partitions should be at least %dMB in size", ROOT_MIN_SIZE / 2048);
515 break;
516 }
517 }
518 tmp = Create_Chunk_DWIM(label_chunk_info[here].d,
519 label_chunk_info[here].c,
520 size, part,
521 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
522 flags);
523 if (!tmp) {
524 msgConfirm("Unable to create the partition. Too big?");
525 break;
526 }
527 if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
528 msgConfirm("This region cannot be used for your root partition as it starts\nor extends past the 1024'th cylinder mark and is thus a\npoor location to boot from. Please choose another\nlocation for your root partition and try again!");
529 Delete_Chunk(label_chunk_info[here].d, tmp);
530 break;
531 }
532 if (type != PART_SWAP) {
533 /* This is needed to tell the newfs -u about the size */
534 tmp->private = new_part(p->mountpoint,p->newfs,tmp->size);
535 safe_free(p);
536 } else {
537 tmp->private = p;
538 }
539 tmp->private_free = safe_free;
540 record_label_chunks();
541 }
542 break;
543
544 case 'D': /* delete */
545 if (label_chunk_info[here].type == PART_SLICE) {
546 msg = MSG_NOT_APPLICABLE;
547 break;
548 }
549 else if (label_chunk_info[here].type == PART_FAT) {
550 msg = "Use the Disk Partition Editor to delete this";
551 break;
552 }
553 Delete_Chunk(label_chunk_info[here].d, label_chunk_info[here].c);
554 record_label_chunks();
555 break;
556
557 case 'M': /* mount */
558 switch(label_chunk_info[here].type) {
559 case PART_SLICE:
560 msg = MSG_NOT_APPLICABLE;
561 break;
562
563 case PART_SWAP:
564 msg = "You don't need to specify a mountpoint for a swap partition.";
565 break;
566
567 case PART_FAT:
568 case PART_FILESYSTEM:
569 p = get_mountpoint(label_chunk_info[here].c);
570 if (p) {
571 p->newfs = FALSE;
572 record_label_chunks();
573 }
574 break;
575
576 default:
577 msgFatal("Bogus partition under cursor???");
578 break;
579 }
580 break;
581
582 case 'N': /* Set newfs options */
583 if (label_chunk_info[here].c->private &&
584 ((PartInfo *)label_chunk_info[here].c->private)->newfs)
585 getNewfsCmd(label_chunk_info[here].c->private);
586 else
587 msg = MSG_NOT_APPLICABLE;
588 break;
589
590 case 'T': /* Toggle newfs state */
591 if (label_chunk_info[here].type == PART_FILESYSTEM &&
592 label_chunk_info[here].c->private) {
593 PartInfo *pi = ((PartInfo *)
594 label_chunk_info[here].c->private);
595 label_chunk_info[here].c->private = new_part(
596 pi->mountpoint,
597 !pi->newfs,
598 label_chunk_info[here].c->size);
599 safe_free(pi);
600 }
601 else
602 msg = MSG_NOT_APPLICABLE;
603 break;
604
605 case 'W':
606 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!")) {
607 int i;
608 Device **devs;
609
610 dialog_clear();
611 end_dialog();
612 DialogActive = FALSE;
613 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
614 if (!devs) {
615 msgConfirm("Can't find any disk devicse!");
616 break;
617 }
618 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
619 if (devs[i]->enabled)
620 slice_wizard(((Disk *)devs[i]->private));
621 }
622 DialogActive = TRUE;
623 dialog_clear();
624 record_label_chunks();
625 }
626 else
627 msg = "A most prudent choice!";
628 break;
629
630 case 27: /* ESC */
631 labeling = FALSE;
632 break;
633
634 default:
635 beep();
636 msg = "Type F1 or ? for help";
637 break;
638 }
639 }
640 variable_set2(DISK_LABELLED, "yes");
641 dialog_clear();
642 refresh();
643 return 0;
644}
645
646
647