]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - release/sysinstall/label.c
Oh bollocks, I really screwed up the "auto" check here. Time
[FreeBSD/FreeBSD.git] / release / sysinstall / label.c
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  * $FreeBSD$
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  *
23  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include "sysinstall.h"
38 #include <ctype.h>
39 #include <sys/disklabel.h>
40 #include <sys/param.h>
41 #include <sys/sysctl.h>
42
43 /*
44  * Everything to do with editing the contents of disk labels.
45  */
46
47 /* A nice message we use a lot in the disklabel editor */
48 #define MSG_NOT_APPLICABLE      "That option is not applicable here"
49
50 /* Where to start printing the freebsd slices */
51 #define CHUNK_SLICE_START_ROW           2
52 #define CHUNK_PART_START_ROW            11
53
54 /* The smallest filesystem we're willing to create */
55 #define FS_MIN_SIZE                     ONE_MEG
56
57 /* The smallest root filesystem we're willing to create */
58 #ifdef __alpha__
59 #define ROOT_MIN_SIZE                   40
60 #else
61 #define ROOT_MIN_SIZE                   30
62 #endif
63
64 /* The default root filesystem size */
65 #ifdef __alpha__
66 #define ROOT_DEFAULT_SIZE               60
67 #else
68 #define ROOT_DEFAULT_SIZE               40
69 #endif
70
71 /* The smallest swap partition we want to create by default */
72 #define SWAP_MIN_SIZE                   16
73
74 /* The smallest /usr partition we're willing to create by default */
75 #define USR_MIN_SIZE                    80
76
77 /* The smallest /var partition we're willing to create by default */
78 #define VAR_MIN_SIZE                    20
79
80 /* The bottom-most row we're allowed to scribble on */
81 #define CHUNK_ROW_MAX                   16
82
83
84 /* All the chunks currently displayed on the screen */
85 static struct {
86     struct chunk *c;
87     PartType type;
88 } label_chunk_info[MAX_CHUNKS + 1];
89 static int here;
90
91 /*** with this value we try to track the most recently added label ***/
92 static int label_focus = 0, pslice_focus = 0;
93
94 static int diskLabel(Device *dev);
95 static int diskLabelNonInteractive(Device *dev);
96
97 static int
98 labelHook(dialogMenuItem *selected)
99 {
100     Device **devs = NULL;
101
102     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
103     if (!devs) {
104         msgConfirm("Unable to find disk %s!", selected->prompt);
105         return DITEM_FAILURE;
106     }
107     /* Toggle enabled status? */
108     if (!devs[0]->enabled) {
109         devs[0]->enabled = TRUE;
110         diskLabel(devs[0]);
111     }
112     else
113         devs[0]->enabled = FALSE;
114     return DITEM_SUCCESS | DITEM_RESTORE;
115 }
116
117 static int
118 labelCheck(dialogMenuItem *selected)
119 {
120     Device **devs = NULL;
121
122     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
123     if (!devs || devs[0]->enabled == FALSE)
124         return FALSE;
125     return TRUE;
126 }
127
128 int
129 diskLabelEditor(dialogMenuItem *self)
130 {
131     DMenu *menu;
132     Device **devs;
133     int i, cnt;
134
135     i = 0;
136     cnt = diskGetSelectCount(&devs);
137     if (cnt == -1) {
138         msgConfirm("No disks found!  Please verify that your disk controller is being\n"
139                    "properly probed at boot time.  See the Hardware Guide on the\n"
140                    "Documentation menu for clues on diagnosing this type of problem.");
141         return DITEM_FAILURE;
142     }
143     else if (cnt) {
144         /* Some are already selected */
145         if (variable_get(VAR_NONINTERACTIVE))
146             i = diskLabelNonInteractive(NULL);
147         else
148             i = diskLabel(NULL);
149     }
150     else {
151         /* No disks are selected, fall-back case now */
152         cnt = deviceCount(devs);
153         if (cnt == 1) {
154             devs[0]->enabled = TRUE;
155             if (variable_get(VAR_NONINTERACTIVE))
156                 i = diskLabelNonInteractive(devs[0]);
157             else
158                 i = diskLabel(devs[0]);
159         }
160         else {
161             menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
162             if (!menu) {
163                 msgConfirm("No devices suitable for installation found!\n\n"
164                            "Please verify that your disk controller (and attached drives)\n"
165                            "were detected properly.  This can be done by pressing the\n"
166                            "[Scroll Lock] key and using the Arrow keys to move back to\n"
167                            "the boot messages.  Press [Scroll Lock] again to return.");
168                 i = DITEM_FAILURE;
169             }
170             else {
171                 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
172                 free(menu);
173             }
174             i |= DITEM_RESTORE;
175         }
176     }
177     if (DITEM_STATUS(i) != DITEM_FAILURE) {
178         char *cp;
179
180         if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
181             variable_set2(DISK_LABELLED, "yes", 0);
182     }
183     return i;
184 }
185
186 int
187 diskLabelCommit(dialogMenuItem *self)
188 {
189     char *cp;
190     int i;
191
192     /* Already done? */
193     if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
194         i = DITEM_SUCCESS;
195     else if (!cp) {
196         msgConfirm("You must assign disk labels before this option can be used.");
197         i = DITEM_FAILURE;
198     }
199     /* The routine will guard against redundant writes, just as this one does */
200     else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
201         i = DITEM_FAILURE;
202     else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
203         i = DITEM_FAILURE;
204     else {
205         msgInfo("All filesystem information written successfully.");
206         variable_set2(DISK_LABELLED, "written", 0);
207         i = DITEM_SUCCESS;
208     }
209     return i;
210 }
211
212 /* See if we're already using a desired partition name */
213 static Boolean
214 check_conflict(char *name)
215 {
216     int i;
217
218     for (i = 0; label_chunk_info[i].c; i++)
219         if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)
220             && label_chunk_info[i].c->private_data
221             && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
222             return TRUE;
223     return FALSE;
224 }
225
226 /* How much space is in this FreeBSD slice? */
227 static int
228 space_free(struct chunk *c)
229 {
230     struct chunk *c1;
231     int sz = c->size;
232
233     for (c1 = c->part; c1; c1 = c1->next) {
234         if (c1->type != unused)
235             sz -= c1->size;
236     }
237     if (sz < 0)
238         msgFatal("Partitions are larger than actual chunk??");
239     return sz;
240 }
241
242 /* Snapshot the current situation into the displayed chunks structure */
243 static void
244 record_label_chunks(Device **devs, Device *dev)
245 {
246     int i, j, p;
247     struct chunk *c1, *c2;
248     Disk *d;
249
250     j = p = 0;
251     /* First buzz through and pick up the FreeBSD slices */
252     for (i = 0; devs[i]; i++) {
253         if ((dev && devs[i] != dev) || !devs[i]->enabled)
254             continue;
255         d = (Disk *)devs[i]->private;
256         if (!d->chunks)
257             msgFatal("No chunk list found for %s!", d->name);
258
259         /* Put the slice entries first */
260         for (c1 = d->chunks->part; c1; c1 = c1->next) {
261             if (c1->type == freebsd) {
262                 label_chunk_info[j].type = PART_SLICE;
263                 label_chunk_info[j].c = c1;
264                 ++j;
265             }
266         }
267     }
268
269     /* Now run through again and get the FreeBSD partition entries */
270     for (i = 0; devs[i]; i++) {
271         if (!devs[i]->enabled)
272             continue;
273         d = (Disk *)devs[i]->private;
274         /* Then buzz through and pick up the partitions */
275         for (c1 = d->chunks->part; c1; c1 = c1->next) {
276             if (c1->type == freebsd) {
277                 for (c2 = c1->part; c2; c2 = c2->next) {
278                     if (c2->type == part) {
279                         if (c2->subtype == FS_SWAP)
280                             label_chunk_info[j].type = PART_SWAP;
281                         else
282                             label_chunk_info[j].type = PART_FILESYSTEM;
283                         label_chunk_info[j].c = c2;
284                         ++j;
285                     }
286                 }
287             }
288             else if (c1->type == fat) {
289                 label_chunk_info[j].type = PART_FAT;
290                 label_chunk_info[j].c = c1;
291                 ++j;
292             }
293         }
294     }
295     label_chunk_info[j].c = NULL;
296     if (here >= j) {
297         here = j  ? j - 1 : 0;
298     }
299 }
300
301 /* A new partition entry */
302 static PartInfo *
303 new_part(char *mpoint, Boolean newfs, u_long size)
304 {
305     PartInfo *ret;
306
307     if (!mpoint)
308         mpoint = "/change_me";
309
310     ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
311     sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
312     strcpy(ret->newfs_cmd, "newfs ");
313     strcat(ret->newfs_cmd, variable_get(VAR_NEWFS_ARGS));
314     ret->newfs = newfs;
315     if (!size)
316         return ret;
317     return ret;
318 }
319
320 /* Get the mountpoint for a partition and save it away */
321 static PartInfo *
322 get_mountpoint(struct chunk *old)
323 {
324     char *val;
325     PartInfo *tmp;
326
327     if (old && old->private_data)
328         tmp = old->private_data;
329     else
330         tmp = NULL;
331     if (!old) {
332         DialogX = 14;
333         DialogY = 16;
334     }
335     val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
336     DialogX = DialogY = 0;
337     if (!val || !*val) {
338         if (!old)
339             return NULL;
340         else {
341             free(old->private_data);
342             old->private_data = NULL;
343         }
344         return NULL;
345     }
346
347     /* Is it just the same value? */
348     if (tmp && !strcmp(tmp->mountpoint, val))
349         return NULL;
350
351     /* Did we use it already? */
352     if (check_conflict(val)) {
353         msgConfirm("You already have a mount point for %s assigned!", val);
354         return NULL;
355     }
356
357     /* Is it bogus? */
358     if (*val != '/') {
359         msgConfirm("Mount point must start with a / character");
360         return NULL;
361     }
362
363     /* Is it going to be mounted on root? */
364     if (!strcmp(val, "/")) {
365         if (old)
366             old->flags |= CHUNK_IS_ROOT;
367     }
368     else if (old)
369         old->flags &= ~CHUNK_IS_ROOT;
370
371     safe_free(tmp);
372     val = string_skipwhite(string_prune(val));
373     tmp = new_part(val, TRUE, 0);
374     if (old) {
375         old->private_data = tmp;
376         old->private_free = safe_free;
377     }
378     return tmp;
379 }
380
381 /* Get the type of the new partiton */
382 static PartType
383 get_partition_type(void)
384 {
385     char selection[20];
386     int i;
387
388     static unsigned char *fs_types[] = {
389         "FS",
390         "A file system",
391         "Swap",
392         "A swap partition.",
393     };
394     DialogX = 7;
395     DialogY = 8;
396     i = dialog_menu("Please choose a partition type",
397                     "If you want to use this partition for swap space, select Swap.\n"
398                     "If you want to put a filesystem on it, choose FS.",
399                     -1, -1, 2, 2, fs_types, selection, NULL, NULL);
400     DialogX = DialogY = 0;
401     if (!i) {
402         if (!strcmp(selection, "FS"))
403             return PART_FILESYSTEM;
404         else if (!strcmp(selection, "Swap"))
405             return PART_SWAP;
406     }
407     return PART_NONE;
408 }
409
410 /* If the user wants a special newfs command for this, set it */
411 static void
412 getNewfsCmd(PartInfo *p)
413 {
414     char *val;
415
416     val = msgGetInput(p->newfs_cmd,
417                       "Please enter the newfs command and options you'd like to use in\n"
418                       "creating this file system.");
419     if (val)
420         sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
421 }
422
423 #define MAX_MOUNT_NAME  12
424
425 #define PART_PART_COL   0
426 #define PART_MOUNT_COL  8
427 #define PART_SIZE_COL   (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
428 #define PART_NEWFS_COL  (PART_SIZE_COL + 8)
429 #define PART_OFF        38
430
431 #define TOTAL_AVAIL_LINES       (10)
432 #define PSLICE_SHOWABLE          (4)
433
434
435 /* stick this all up on the screen */
436 static void
437 print_label_chunks(void)
438 {
439     int  i, j, srow, prow, pcol;
440     int  sz;
441     char clrmsg[80];
442     int ChunkPartStartRow;
443     WINDOW *ChunkWin;
444
445     /********************************************************/
446     /*** These values are for controling screen resources ***/
447     /*** Each label line holds up to 2 labels, so beware! ***/
448     /*** strategy will be to try to always make sure the  ***/
449     /*** highlighted label is in the active display area. ***/
450     /********************************************************/
451     int  pslice_max, label_max;
452     int  pslice_count, label_count, label_focus_found, pslice_focus_found;
453
454     attrset(A_REVERSE);
455     mvaddstr(0, 25, "FreeBSD Disklabel Editor");
456     attrset(A_NORMAL);
457
458     /*** Count the number of parition slices ***/
459     pslice_count = 0;
460     for (i = 0; label_chunk_info[i].c ; i++) {
461         if (label_chunk_info[i].type == PART_SLICE)
462             ++pslice_count;
463     }
464     pslice_max = pslice_count;
465   
466     /*** 4 line max for partition slices ***/
467     if (pslice_max > PSLICE_SHOWABLE) {
468         pslice_max = PSLICE_SHOWABLE;
469     }
470     ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
471     
472     /*** View partition slices modulo pslice_max ***/
473     label_max = TOTAL_AVAIL_LINES - pslice_max;
474
475     for (i = 0; i < 2; i++) {
476         mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
477         mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
478
479         mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
480         mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
481
482         mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
483         mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
484
485         mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
486         mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
487     }
488     srow = CHUNK_SLICE_START_ROW;
489     prow = 0;
490     pcol = 0;
491
492     /*** these variables indicate that the focused item is shown currently ***/
493     label_focus_found = 0;
494     pslice_focus_found = 0;
495    
496     label_count = 0;
497     pslice_count = 0;
498     mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "          ");
499     mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "          ");
500
501     ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
502
503     wclear(ChunkWin);
504     /*** wrefresh(ChunkWin); ***/
505
506     for (i = 0; label_chunk_info[i].c; i++) {
507         /* Is it a slice entry displayed at the top? */
508         if (label_chunk_info[i].type == PART_SLICE) {
509             /*** This causes the new pslice to replace the previous display ***/
510             /*** focus must remain on the most recently active pslice       ***/
511             if (pslice_count == pslice_max) {
512                 if (pslice_focus_found) {
513                     /*** This is where we can mark the more following ***/
514                     attrset(A_BOLD);
515                     mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
516                     attrset(A_NORMAL);
517                     continue;
518                 }
519                 else {
520                     /*** this is where we set the more previous ***/
521                     attrset(A_BOLD);
522                     mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
523                     attrset(A_NORMAL);
524                     pslice_count = 0;
525                     srow = CHUNK_SLICE_START_ROW;
526                 }
527             }
528
529             sz = space_free(label_chunk_info[i].c);
530             if (i == here)
531                 attrset(ATTR_SELECTED);
532             if (i == pslice_focus)
533                 pslice_focus_found = -1;
534
535             mvprintw(srow++, 0, 
536                      "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
537                      label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name, 
538                      sz, (sz / ONE_MEG));
539             attrset(A_NORMAL);
540             clrtoeol();
541             move(0, 0);
542             /*** refresh(); ***/
543             ++pslice_count;
544         }
545         /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
546         else {
547             char onestr[PART_OFF], num[10], *mountpoint, *newfs;
548
549             /*
550              * We copy this into a blank-padded string so that it looks like
551              * a solid bar in reverse-video
552              */
553             memset(onestr, ' ', PART_OFF - 1);
554             onestr[PART_OFF - 1] = '\0';
555
556             /*** Track how many labels have been displayed ***/
557             if (label_count == ((label_max - 1 ) * 2)) {
558                 if (label_focus_found) {
559                     continue;
560                 }
561                 else {
562                     label_count = 0;
563                     prow = 0;
564                     pcol = 0;
565                 }
566             }
567
568             /* Go for two columns if we've written one full columns worth */
569             /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
570             if (label_count == label_max - 1) {
571                 pcol = PART_OFF;
572                 prow = 0;
573             }
574             memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
575             /* If it's a filesystem, display the mountpoint */
576             if (label_chunk_info[i].c->private_data
577                 && (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
578                 mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
579             else if (label_chunk_info[i].type == PART_SWAP)
580                 mountpoint = "swap";
581             else
582                 mountpoint = "<none>";
583
584             /* Now display the newfs field */
585             if (label_chunk_info[i].type == PART_FAT)
586                 newfs = "DOS";
587             else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
588                 newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
589             else if (label_chunk_info[i].type == PART_SWAP)
590                 newfs = "SWAP";
591             else
592                 newfs = "*";
593             for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
594                 onestr[PART_MOUNT_COL + j] = mountpoint[j];
595             snprintf(num, 10, "%5ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
596             memcpy(onestr + PART_SIZE_COL, num, strlen(num));
597             memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
598             onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
599             if (i == label_focus) {
600                 label_focus_found = -1;
601                 wattrset(ChunkWin, A_BOLD);
602             }
603             if (i == here)
604                 wattrset(ChunkWin, ATTR_SELECTED);
605
606             /*** lazy man's way of padding this string ***/
607             while (strlen( onestr ) < 37)
608                 strcat(onestr, " ");
609
610             mvwaddstr(ChunkWin, prow, pcol, onestr);
611             wattrset(ChunkWin, A_NORMAL);
612             move(0, 0);
613             ++prow;
614             ++label_count;
615         }
616     }
617     
618     /*** this will erase all the extra stuff ***/
619     memset(clrmsg, ' ', 37);
620     clrmsg[37] = '\0';
621    
622     while (pslice_count < pslice_max) {
623         mvprintw(srow++, 0, clrmsg);
624         clrtoeol();
625         ++pslice_count;
626     }
627     while (label_count < (2 * (label_max - 1))) {
628         mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
629         ++label_count;
630         if (prow == (label_max - 1)) {
631             prow = 0;
632             pcol = PART_OFF;
633         }
634     }
635     refresh();
636     wrefresh(ChunkWin);
637 }
638
639 static void
640 print_command_summary(void)
641 {
642     mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
643     mvprintw(18, 0, "C = Create      D = Delete         M = Mount pt.");
644     if (!RunningAsInit)
645         mvprintw(18, 49, "W = Write");
646     mvprintw(19, 0, "N = Newfs Opts  T = Newfs Toggle   U = Undo      Q = Finish");
647     mvprintw(20, 0, "A = Auto Defaults for all!");
648     mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
649     move(0, 0);
650 }
651
652 static void
653 clear_wins(void)
654 {
655     extern void print_label_chunks();
656     clear();
657     print_label_chunks();
658 }
659
660 #ifdef __alpha__
661
662 /*
663  * If there isn't a freebsd chunk already (i.e. there is no label),
664  * dedicate the disk.
665  */
666 static void
667 maybe_dedicate(Disk* d)
668 {
669     struct chunk *c;
670
671     for (c = d->chunks->part; c; c = c->next) {
672         if (c->type == freebsd)
673             break;
674     }
675
676     if (!c) {
677         msgDebug("dedicating disk");
678         All_FreeBSD(d, 1);
679     }
680 }
681
682 #endif
683
684 static int
685 diskLabel(Device *dev)
686 {
687     int sz, key = 0;
688     Boolean labeling;
689     char *msg = NULL;
690     PartInfo *p, *oldp;
691     PartType type;
692     Device **devs;
693 #ifdef __alpha__
694     int i;
695 #endif
696
697     label_focus = 0;
698     pslice_focus = 0;
699     here = 0;
700
701     devs = deviceFind(NULL, DEVICE_TYPE_DISK);
702     if (!devs) {
703         msgConfirm("No disks found!");
704         return DITEM_FAILURE;
705     }
706     labeling = TRUE;
707     keypad(stdscr, TRUE);
708 #ifdef __alpha__
709     for (i = 0; devs[i]; i++) {
710         maybe_dedicate((Disk*) devs[i]->private);
711     }
712 #endif
713     record_label_chunks(devs, dev);
714
715     clear();
716     while (labeling) {
717         char *cp;
718
719         print_label_chunks();
720         print_command_summary();
721         if (msg) {
722             attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
723             clrtoeol();
724             beep();
725             msg = NULL;
726         }
727         else {
728             move(23, 0);
729             clrtoeol();
730         }
731
732         refresh();
733         key = getch();
734         switch (toupper(key)) {
735             int i;
736             static char _msg[40];
737
738         case '\014':    /* ^L */
739             clear_wins();
740             break;
741
742         case '\020':    /* ^P */
743         case KEY_UP:
744         case '-':
745             if (here != 0)
746                 --here;
747             else
748                 while (label_chunk_info[here + 1].c)
749                     ++here;
750             break;
751
752         case '\016':    /* ^N */
753         case KEY_DOWN:
754         case '+':
755         case '\r':
756         case '\n':
757             if (label_chunk_info[here + 1].c)
758                 ++here;
759             else
760                 here = 0;
761             break;
762
763         case KEY_HOME:
764             here = 0;
765             break;
766
767         case KEY_END:
768             while (label_chunk_info[here + 1].c)
769                 ++here;
770             break;
771
772         case KEY_F(1):
773         case '?':
774             systemDisplayHelp("partition");
775             clear_wins();
776             break;
777
778         case 'A':
779             if (label_chunk_info[here].type != PART_SLICE) {
780                 msg = "You can only do this in a disk slice (at top of screen)";
781                 break;
782             }
783             sz = space_free(label_chunk_info[here].c);
784             if (sz <= FS_MIN_SIZE)
785                 msg = "Not enough free space to create a new partition in the slice";
786             else {
787                 struct chunk *tmp;
788                 int mib[2];
789                 int physmem;
790                 size_t size, swsize;
791                 char *cp;
792                 Chunk *rootdev, *swapdev, *usrdev, *vardev;
793
794                 (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
795                 if (!rootdev) {
796                     cp = variable_get(VAR_ROOT_SIZE);
797                     tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
798                                             (cp ? atoi(cp) : ROOT_DEFAULT_SIZE) * ONE_MEG, part, FS_BSDFFS,  CHUNK_IS_ROOT);
799                     if (!tmp) {
800                         msgConfirm("Unable to create the root partition. Too big?");
801                         clear_wins();
802                         break;
803                     }
804                     tmp->private_data = new_part("/", TRUE, tmp->size);
805                     tmp->private_free = safe_free;
806                     record_label_chunks(devs, dev);
807                 }
808
809                 if (!swapdev) {
810                     cp = variable_get(VAR_SWAP_SIZE);
811                     if (cp)
812                         swsize = atoi(cp) * ONE_MEG;
813                     else {
814                         mib[0] = CTL_HW;
815                         mib[1] = HW_PHYSMEM;
816                         size = sizeof physmem;
817                         sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
818                         swsize = 16 * ONE_MEG + (physmem * 2 / 512);
819                     }
820                     tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
821                                             swsize, part, FS_SWAP, 0);
822                     if (!tmp) {
823                         msgConfirm("Unable to create the swap partition. Too big?");
824                         clear_wins();
825                         break;
826                     }
827                     tmp->private_data = 0;
828                     tmp->private_free = safe_free;
829                     record_label_chunks(devs, dev);
830                 }
831
832                 if (!vardev) {
833                     cp = variable_get(VAR_VAR_SIZE);
834                     if (cp)
835                         sz = atoi(cp) * ONE_MEG;
836                     else
837                         sz = variable_get(VAR_NO_USR)
838                                 ?  space_free(label_chunk_info[here].c)
839                                 :  VAR_MIN_SIZE * ONE_MEG;
840
841                     tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
842                                             sz, part, FS_BSDFFS, 0);
843                     if (!tmp) {
844                         msgConfirm("Less than %dMB free for /var - you will need to\n"
845                                    "partition your disk manually with a custom install!",
846                                    (cp ? atoi(cp) : VAR_MIN_SIZE));
847                         clear_wins();
848                         break;
849                     }
850                     tmp->private_data = new_part("/var", TRUE, tmp->size);
851                     tmp->private_free = safe_free;
852                     record_label_chunks(devs, dev);
853                 }
854
855                 if (!usrdev && !variable_get(VAR_NO_USR)) {
856                     cp = variable_get(VAR_USR_SIZE);
857                     if (cp)
858                         sz = atoi(cp) * ONE_MEG;
859                     else
860                         sz = space_free(label_chunk_info[here].c);
861                     if (sz) {
862                         if (sz < (USR_MIN_SIZE * ONE_MEG)) {
863                             msgConfirm("Less than %dMB free for /usr - you will need to\n"
864                                        "partition your disk manually with a custom install!", USR_MIN_SIZE);
865                             clear_wins();
866                             break;
867                         }
868
869                         tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
870                                                 label_chunk_info[here].c,
871                                                 sz, part, FS_BSDFFS, 0);
872                         if (!tmp) {
873                             msgConfirm("Unable to create the /usr partition.  Not enough space?\n"
874                                        "You will need to partition your disk manually with a custom install!");
875                             clear_wins();
876                             break;
877                         }
878                         tmp->private_data = new_part("/usr", TRUE, tmp->size);
879                         tmp->private_free = safe_free;
880                         record_label_chunks(devs, dev);
881                     }
882                 }
883
884                 /* At this point, we're reasonably "labelled" */
885                 if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
886                     variable_set2(DISK_LABELLED, "yes", 0);
887             }
888             break;
889             
890         case 'C':
891             if (label_chunk_info[here].type != PART_SLICE) {
892                 msg = "You can only do this in a master partition (see top of screen)";
893                 break;
894             }
895             sz = space_free(label_chunk_info[here].c);
896             if (sz <= FS_MIN_SIZE) {
897                 msg = "Not enough space to create an additional FreeBSD partition";
898                 break;
899             }
900             else {
901                 char *val;
902                 int size;
903                 struct chunk *tmp;
904                 char osize[80];
905                 u_long flags = 0;
906
907                 sprintf(osize, "%d", sz);
908                 DialogX = 3;
909                 DialogY = 2;
910                 val = msgGetInput(osize,
911                                   "Please specify the partition size in blocks or append a trailing M for\n"
912                                   "megabytes or C for cylinders.  %d blocks (%dMB) are free.",
913                                   sz, sz / ONE_MEG);
914                 DialogX = DialogY = 0;
915                 if (!val || (size = strtol(val, &cp, 0)) <= 0) {
916                     clear_wins();
917                     break;
918                 }
919
920                 if (*cp) {
921                     if (toupper(*cp) == 'M')
922                         size *= ONE_MEG;
923                     else if (toupper(*cp) == 'C')
924                         size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
925                 }
926                 if (size <= FS_MIN_SIZE) {
927                     msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
928                     clear_wins();
929                     break;
930                 }
931                 type = get_partition_type();
932                 if (type == PART_NONE) {
933                     clear_wins();
934                     beep();
935                     break;
936                 }
937
938                 if (type == PART_FILESYSTEM) {
939                     if ((p = get_mountpoint(NULL)) == NULL) {
940                         clear_wins();
941                         beep();
942                         break;
943                     }
944                     else if (!strcmp(p->mountpoint, "/"))
945                         flags |= CHUNK_IS_ROOT;
946                     else
947                         flags &= ~CHUNK_IS_ROOT;
948                 }
949                 else
950                     p = NULL;
951
952                 if ((flags & CHUNK_IS_ROOT)) {
953                     if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
954                         msgConfirm("This region cannot be used for your root partition as the\n"
955                                    "FreeBSD boot code cannot deal with a root partition created\n"
956                                    "in that location.  Please choose another location or smaller\n"
957                                    "size for your root partition and try again!");
958                         clear_wins();
959                         break;
960                     }
961                     if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
962                         msgConfirm("Warning: This is smaller than the recommended size for a\n"
963                                    "root partition.  For a variety of reasons, root\n"
964                                    "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
965                     }
966                 }
967                 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
968                                         label_chunk_info[here].c,
969                                         size, part,
970                                         (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
971                                         flags);
972                 if (!tmp) {
973                     msgConfirm("Unable to create the partition. Too big?");
974                     clear_wins();
975                     break;
976                 }
977                 if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
978                     msgConfirm("This region cannot be used for your root partition as it starts\n"
979                                "or extends past the 1024'th cylinder mark and is thus a\n"
980                                "poor location to boot from.  Please choose another\n"
981                                "location (or smaller size) for your root partition and try again!");
982                     Delete_Chunk(label_chunk_info[here].c->disk, tmp);
983                     clear_wins();
984                     break;
985                 }
986                 if (type != PART_SWAP) {
987                     /* This is needed to tell the newfs -u about the size */
988                     tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
989                     safe_free(p);
990                 }
991                 else
992                     tmp->private_data = p;
993                 tmp->private_free = safe_free;
994                 if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
995                     variable_set2(DISK_LABELLED, "yes", 0);
996                 record_label_chunks(devs, dev);
997                 clear_wins();
998                 /*** This is where we assign focus to new label so it shows ***/
999                 {
1000                     int i;
1001                     label_focus = -1;
1002                     for (i = 0; label_chunk_info[i].c; ++i) {
1003                         if (label_chunk_info[i].c == tmp) {
1004                             label_focus = i;
1005                             break;
1006                         }
1007                     }
1008                     if (label_focus == -1)
1009                         label_focus = i - 1;
1010                 }
1011             }
1012             break;
1013
1014         case KEY_DC:
1015         case 'D':       /* delete */
1016             if (label_chunk_info[here].type == PART_SLICE) {
1017                 msg = MSG_NOT_APPLICABLE;
1018                 break;
1019             }
1020             else if (label_chunk_info[here].type == PART_FAT) {
1021                 msg = "Use the Disk Partition Editor to delete DOS partitions";
1022                 break;
1023             }
1024             Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
1025             if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1026                 variable_set2(DISK_LABELLED, "yes", 0);
1027             record_label_chunks(devs, dev);
1028             break;
1029
1030         case 'M':       /* mount */
1031             switch(label_chunk_info[here].type) {
1032             case PART_SLICE:
1033                 msg = MSG_NOT_APPLICABLE;
1034                 break;
1035
1036             case PART_SWAP:
1037                 msg = "You don't need to specify a mountpoint for a swap partition.";
1038                 break;
1039
1040             case PART_FAT:
1041             case PART_FILESYSTEM:
1042                 oldp = label_chunk_info[here].c->private_data;
1043                 p = get_mountpoint(label_chunk_info[here].c);
1044                 if (p) {
1045                     if (!oldp)
1046                         p->newfs = FALSE;
1047                     if (label_chunk_info[here].type == PART_FAT
1048                         && (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
1049                             || !strcmp(p->mountpoint, "/var"))) {
1050                         msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1051                         strcpy(p->mountpoint, "/bogus");
1052                     }
1053                 }
1054                 if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1055                     variable_set2(DISK_LABELLED, "yes", 0);
1056                 record_label_chunks(devs, dev);
1057                 clear_wins();
1058                 break;
1059
1060             default:
1061                 msgFatal("Bogus partition under cursor???");
1062                 break;
1063             }
1064             break;
1065
1066         case 'N':       /* Set newfs options */
1067             if (label_chunk_info[here].c->private_data &&
1068                 ((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
1069                 getNewfsCmd(label_chunk_info[here].c->private_data);
1070             else
1071                 msg = MSG_NOT_APPLICABLE;
1072             clear_wins();
1073             break;
1074
1075         case 'T':       /* Toggle newfs state */
1076             if (label_chunk_info[here].type == PART_FILESYSTEM) {
1077                 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1078                 label_chunk_info[here].c->private_data =
1079                     new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
1080                 safe_free(pi);
1081                 label_chunk_info[here].c->private_free = safe_free;
1082                 if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1083                     variable_set2(DISK_LABELLED, "yes", 0);
1084             }
1085             else
1086                 msg = MSG_NOT_APPLICABLE;
1087             break;
1088
1089         case 'U':
1090             clear();
1091             if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1092                 msgConfirm("You've already written out your changes -\n"
1093                            "it's too late to undo!");
1094             }
1095             else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
1096                 variable_unset(DISK_PARTITIONED);
1097                 variable_unset(DISK_LABELLED);
1098                 for (i = 0; devs[i]; i++) {
1099                     Disk *d;
1100
1101                     if (!devs[i]->enabled)
1102                         continue;
1103                     else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1104                         Free_Disk(devs[i]->private);
1105                         devs[i]->private = d;
1106                         diskPartition(devs[i]);
1107                     }
1108                 }
1109                 record_label_chunks(devs, dev);
1110             }
1111             clear_wins();
1112             break;
1113
1114         case 'W':
1115             if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
1116                 msgConfirm("You've already written out your changes - if you\n"
1117                            "wish to overwrite them, you'll have to start this\n"
1118                            "procedure again from the beginning.");
1119             }
1120             else if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
1121                           "installation.  If you are installing FreeBSD for the first time\n"
1122                           "then you should simply type Q when you're finished here and your\n"
1123                           "changes will be committed in one batch automatically at the end of\n"
1124                           "these questions.\n\n"
1125                           "Are you absolutely sure you want to do this now?")) {
1126                 variable_set2(DISK_LABELLED, "yes", 0);
1127                 diskLabelCommit(NULL);
1128             }
1129             clear_wins();
1130             break;
1131
1132         case '|':
1133             if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
1134                           "This is an entirely undocumented feature which you are not\n"
1135                           "expected to understand!")) {
1136                 int i;
1137                 Device **devs;
1138
1139                 dialog_clear();
1140                 end_dialog();
1141                 DialogActive = FALSE;
1142                 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1143                 if (!devs) {
1144                     msgConfirm("Can't find any disk devices!");
1145                     break;
1146                 }
1147                 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1148                     if (devs[i]->enabled)
1149                         slice_wizard(((Disk *)devs[i]->private));
1150                 }
1151                 if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
1152                     variable_set2(DISK_LABELLED, "yes", 0);
1153                 DialogActive = TRUE;
1154                 record_label_chunks(devs, dev);
1155                 clear_wins();
1156             }
1157             else
1158                 msg = "A most prudent choice!";
1159             break;
1160
1161         case '\033':    /* ESC */
1162         case 'Q':
1163             labeling = FALSE;
1164             break;
1165
1166         default:
1167             beep();
1168             sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1169             msg = _msg;
1170             break;
1171         }
1172         if (label_chunk_info[here].type == PART_SLICE)
1173             pslice_focus = here;
1174         else
1175             label_focus = here;
1176     }
1177     return DITEM_SUCCESS | DITEM_RESTORE;
1178 }
1179
1180 static int
1181 diskLabelNonInteractive(Device *dev)
1182 {
1183     char *cp;
1184     PartType type;
1185     PartInfo *p;
1186     u_long flags = 0;
1187     int i, status;
1188     Device **devs;
1189     Disk *d;
1190
1191     status = DITEM_SUCCESS;
1192
1193     cp = variable_get(VAR_DISK);
1194     if (!cp) {
1195         dialog_clear();
1196         msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1197         return DITEM_FAILURE;
1198     }
1199     devs = deviceFind(cp, DEVICE_TYPE_DISK);
1200     if (!devs) {
1201         msgConfirm("diskLabel: No disk device %s found!", cp);
1202         return DITEM_FAILURE;
1203     }
1204     if (dev)
1205         d = dev->private;
1206     else
1207         d = devs[0]->private;
1208 #ifdef __alpha__
1209     maybe_dedicate(d);
1210 #endif
1211     record_label_chunks(devs, dev);
1212     for (i = 0; label_chunk_info[i].c; i++) {
1213         Chunk *c1 = label_chunk_info[i].c;
1214
1215         if (label_chunk_info[i].type == PART_SLICE) {
1216             char name[512];
1217             int entries = 1;
1218
1219             while (entries) {
1220                 snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1221                 if ((cp = variable_get(name)) != NULL) {
1222                     int sz;
1223                     char typ[10], mpoint[50];
1224
1225                     if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
1226                         msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1227                         status = DITEM_FAILURE;
1228                         continue;
1229                     }
1230                     else {
1231                         Chunk *tmp;
1232
1233                         if (!strcmp(typ, "swap")) {
1234                             type = PART_SWAP;
1235                             strcpy(mpoint, "SWAP");
1236                         }
1237                         else {
1238                             type = PART_FILESYSTEM;
1239                             if (!strcmp(mpoint, "/"))
1240                                 flags |= CHUNK_IS_ROOT;
1241                             else
1242                                 flags &= ~CHUNK_IS_ROOT;
1243                         }
1244                         if (!sz)
1245                             sz = space_free(c1);
1246                         if (sz > space_free(c1)) {
1247                             msgConfirm("Not enough free space to create partition: %s", mpoint);
1248                             status = DITEM_FAILURE;
1249                             continue;
1250                         }
1251                         if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1252                                                       (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1253                             msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1254                             status = DITEM_FAILURE;
1255                             break;
1256                         }
1257                         else {
1258                             tmp->private_data = new_part(mpoint, TRUE, sz);
1259                             tmp->private_free = safe_free;
1260                             status = DITEM_SUCCESS;
1261                         }
1262                     }
1263                     entries++;
1264                 }
1265                 else {
1266                     /* No more matches, leave the loop */
1267                     entries = 0;
1268                 }
1269             }
1270         }
1271         else {
1272             /* Must be something we can set a mountpoint for */
1273             cp = variable_get(c1->name);
1274             if (cp) {
1275                 char mpoint[50], do_newfs[8];
1276                 Boolean newfs = FALSE;
1277
1278                 do_newfs[0] = '\0';
1279                 if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1280                     dialog_clear();
1281                     msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1282                     status = DITEM_FAILURE;
1283                     continue;
1284                 }
1285                 newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1286                 if (c1->private_data) {
1287                     p = c1->private_data;
1288                     p->newfs = newfs;
1289                     strcpy(p->mountpoint, mpoint);
1290                 }
1291                 else {
1292                     c1->private_data = new_part(mpoint, newfs, 0);
1293                     c1->private_free = safe_free;
1294                 }
1295                 if (!strcmp(mpoint, "/"))
1296                     c1->flags |= CHUNK_IS_ROOT;
1297                 else
1298                     c1->flags &= ~CHUNK_IS_ROOT;
1299             }
1300         }
1301     }
1302     if (status == DITEM_SUCCESS)
1303         variable_set2(DISK_LABELLED, "yes", 0);
1304     return status;
1305 }