]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/sade/label.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / sade / label.c
1 /*
2  * $FreeBSD$
3  *
4  * Copyright (c) 1995
5  *      Jordan Hubbard.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer,
12  *    verbatim and that no modifications are made prior to this
13  *    point in the file.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31
32 #include "sade.h"
33 #include <ctype.h>
34 #include <inttypes.h>
35 #include <libdisk.h>
36 #include <sys/disklabel.h>
37 #include <sys/param.h>
38 #include <sys/sysctl.h>
39
40 #define AUTO_HOME       0       /* do not create /home automatically */
41
42 /*
43  * Everything to do with editing the contents of disk labels.
44  */
45
46 /* A nice message we use a lot in the disklabel editor */
47 #define MSG_NOT_APPLICABLE      "That option is not applicable here"
48
49 /* Where to start printing the freebsd slices */
50 #define CHUNK_SLICE_START_ROW           2
51 #define CHUNK_PART_START_ROW            11
52
53 /* The smallest filesystem we're willing to create */
54 #define FS_MIN_SIZE                     ONE_MEG
55
56 /*
57  * Minimum partition sizes
58  */
59 #if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
60 #define ROOT_MIN_SIZE                   128
61 #else
62 #define ROOT_MIN_SIZE                   118
63 #endif
64 #define SWAP_MIN_SIZE                   32
65 #define USR_MIN_SIZE                    128
66 #define VAR_MIN_SIZE                    20
67 #define TMP_MIN_SIZE                    20
68 #define HOME_MIN_SIZE                   20
69
70 /*
71  * Swap size limit for auto-partitioning (4G).
72  */
73 #define SWAP_AUTO_LIMIT_SIZE            4096
74
75 /*
76  * Default partition sizes.  If we do not have sufficient disk space
77  * for this configuration we scale things relative to the NOM vs DEFAULT
78  * sizes.  If the disk is larger then /home will get any remaining space.
79  */
80 #define ROOT_DEFAULT_SIZE               512
81 #define USR_DEFAULT_SIZE                8192
82 #define VAR_DEFAULT_SIZE                1024
83 #define TMP_DEFAULT_SIZE                512
84 #define HOME_DEFAULT_SIZE               USR_DEFAULT_SIZE
85
86 /*
87  * Nominal partition sizes.  These are used to scale the default sizes down
88  * when we have insufficient disk space.  If this isn't sufficient we scale
89  * down using the MIN sizes instead.
90  */
91 #define ROOT_NOMINAL_SIZE               256
92 #define USR_NOMINAL_SIZE                1536
93 #define VAR_NOMINAL_SIZE                128
94 #define TMP_NOMINAL_SIZE                128
95 #define HOME_NOMINAL_SIZE               USR_NOMINAL_SIZE
96
97 /* The bottom-most row we're allowed to scribble on */
98 #define CHUNK_ROW_MAX                   16
99
100
101 /* All the chunks currently displayed on the screen */
102 static struct {
103     struct chunk *c;
104     PartType type;
105 } label_chunk_info[MAX_CHUNKS + 1];
106 static int here;
107
108 /*** with this value we try to track the most recently added label ***/
109 static int label_focus = 0, pslice_focus = 0;
110
111 static int diskLabel(Device *dev);
112 static int diskLabelNonInteractive(Device *dev);
113 static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
114
115 static int
116 labelHook(dialogMenuItem *selected)
117 {
118     Device **devs = NULL;
119
120     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
121     if (!devs) {
122         msgConfirm("Unable to find disk %s!", selected->prompt);
123         return DITEM_FAILURE;
124     }
125     /* Toggle enabled status? */
126     if (!devs[0]->enabled) {
127         devs[0]->enabled = TRUE;
128         diskLabel(devs[0]);
129     }
130     else
131         devs[0]->enabled = FALSE;
132     return DITEM_SUCCESS;
133 }
134
135 static int
136 labelCheck(dialogMenuItem *selected)
137 {
138     Device **devs = NULL;
139
140     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
141     if (!devs || devs[0]->enabled == FALSE)
142         return FALSE;
143     return TRUE;
144 }
145
146 int
147 diskLabelEditor(dialogMenuItem *self)
148 {
149     DMenu *menu;
150     Device **devs;
151     int i, cnt;
152
153     i = 0;
154     cnt = diskGetSelectCount(&devs);
155     if (cnt == -1) {
156         msgConfirm("No disks found!  Please verify that your disk controller is being\n"
157                    "properly probed at boot time.  See the Hardware Guide on the\n"
158                    "Documentation menu for clues on diagnosing this type of problem.");
159         return DITEM_FAILURE;
160     }
161     else if (cnt) {
162         /* Some are already selected */
163         if (variable_get(VAR_NONINTERACTIVE) &&
164           !variable_get(VAR_DISKINTERACTIVE))
165             i = diskLabelNonInteractive(NULL);
166         else
167             i = diskLabel(NULL);
168     }
169     else {
170         /* No disks are selected, fall-back case now */
171         cnt = deviceCount(devs);
172         if (cnt == 1) {
173             devs[0]->enabled = TRUE;
174             if (variable_get(VAR_NONINTERACTIVE) &&
175               !variable_get(VAR_DISKINTERACTIVE))
176                 i = diskLabelNonInteractive(devs[0]);
177             else
178                 i = diskLabel(devs[0]);
179         }
180         else {
181             menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
182             if (!menu) {
183                 msgConfirm("No devices suitable for installation found!\n\n"
184                            "Please verify that your disk controller (and attached drives)\n"
185                            "were detected properly.  This can be done by pressing the\n"
186                            "[Scroll Lock] key and using the Arrow keys to move back to\n"
187                            "the boot messages.  Press [Scroll Lock] again to return.");
188                 i = DITEM_FAILURE;
189             }
190             else {
191                 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
192                 free(menu);
193             }
194         }
195     }
196     if (DITEM_STATUS(i) != DITEM_FAILURE) {
197         if (variable_cmp(DISK_LABELLED, "written"))
198             variable_set2(DISK_LABELLED, "yes", 0);
199     }
200     return i;
201 }
202
203 int
204 diskLabelCommit(dialogMenuItem *self)
205 {
206     char *cp;
207     int i;
208
209     /* Already done? */
210     if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
211         i = DITEM_SUCCESS;
212     else if (!cp) {
213         msgConfirm("You must assign disk labels before this option can be used.");
214         i = DITEM_FAILURE;
215     }
216     /* The routine will guard against redundant writes, just as this one does */
217     else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
218         i = DITEM_FAILURE;
219     else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
220         i = DITEM_FAILURE;
221     else {
222         msgInfo("All filesystem information written successfully.");
223         variable_set2(DISK_LABELLED, "written", 0);
224         i = DITEM_SUCCESS;
225     }
226     return i;
227 }
228
229 /* See if we're already using a desired partition name */
230 static Boolean
231 check_conflict(char *name)
232 {
233     int i;
234
235     for (i = 0; label_chunk_info[i].c; i++)
236         if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT
237             || label_chunk_info[i].type == PART_EFI) && label_chunk_info[i].c->private_data
238             && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
239             return TRUE;
240     return FALSE;
241 }
242
243 /* How much space is in this FreeBSD slice? */
244 static daddr_t
245 space_free(struct chunk *c)
246 {
247     struct chunk *c1;
248     daddr_t sz = c->size;
249
250     for (c1 = c->part; c1; c1 = c1->next) {
251         if (c1->type != unused)
252             sz -= c1->size;
253     }
254     if (sz < 0)
255         msgFatal("Partitions are larger than actual chunk??");
256     return sz;
257 }
258
259 /* Snapshot the current situation into the displayed chunks structure */
260 static void
261 record_label_chunks(Device **devs, Device *dev)
262 {
263     int i, j, p;
264     struct chunk *c1, *c2;
265     Disk *d;
266
267     j = p = 0;
268     /* First buzz through and pick up the FreeBSD slices */
269     for (i = 0; devs[i]; i++) {
270         if ((dev && devs[i] != dev) || !devs[i]->enabled)
271             continue;
272         d = (Disk *)devs[i]->private;
273         if (!d->chunks)
274             msgFatal("No chunk list found for %s!", d->name);
275
276 #ifdef __ia64__
277         label_chunk_info[j].type = PART_SLICE;
278         label_chunk_info[j].c = d->chunks;
279         j++;
280 #endif
281
282         /* Put the slice entries first */
283         for (c1 = d->chunks->part; c1; c1 = c1->next) {
284             if (c1->type == freebsd) {
285                 label_chunk_info[j].type = PART_SLICE;
286                 label_chunk_info[j].c = c1;
287                 ++j;
288             }
289 #ifdef __powerpc__
290             if (c1->type == apple) {
291                 label_chunk_info[j].type = PART_SLICE;
292                 label_chunk_info[j].c = c1;
293                 ++j;
294             }
295 #endif
296         }
297     }
298
299     /* Now run through again and get the FreeBSD partition entries */
300     for (i = 0; devs[i]; i++) {
301         if (!devs[i]->enabled)
302             continue;
303         d = (Disk *)devs[i]->private;
304         /* Then buzz through and pick up the partitions */
305         for (c1 = d->chunks->part; c1; c1 = c1->next) {
306             if (c1->type == freebsd) {
307                 for (c2 = c1->part; c2; c2 = c2->next) {
308                     if (c2->type == part) {
309                         if (c2->subtype == FS_SWAP)
310                             label_chunk_info[j].type = PART_SWAP;
311                         else
312                             label_chunk_info[j].type = PART_FILESYSTEM;
313                         label_chunk_info[j].c = c2;
314                         ++j;
315                     }
316                 }
317             }
318             else if (c1->type == fat) {
319                 label_chunk_info[j].type = PART_FAT;
320                 label_chunk_info[j].c = c1;
321                 ++j;
322             }
323 #ifdef __ia64__
324             else if (c1->type == efi) {
325                 label_chunk_info[j].type = PART_EFI;
326                 label_chunk_info[j].c = c1;
327                 ++j;
328             }
329             else if (c1->type == part) {
330                 if (c1->subtype == FS_SWAP)
331                     label_chunk_info[j].type = PART_SWAP;
332                 else
333                     label_chunk_info[j].type = PART_FILESYSTEM;
334                 label_chunk_info[j].c = c1;
335                 ++j;
336             }
337 #endif
338 #ifdef __powerpc__
339             else if (c1->type == apple) {
340                 for (c2 = c1->part; c2; c2 = c2->next) {
341                     if (c2->type == part) {
342                         if (c2->subtype == FS_SWAP)
343                             label_chunk_info[j].type = PART_SWAP;
344                         else
345                             label_chunk_info[j].type = PART_FILESYSTEM;
346                         label_chunk_info[j].c = c2;
347                         ++j;
348                     }
349                 }
350             }
351 #endif
352         }
353     }
354     label_chunk_info[j].c = NULL;
355     if (here >= j) {
356         here = j  ? j - 1 : 0;
357     }
358 }
359
360 /* A new partition entry */
361 static PartInfo *
362 new_part(PartType type, char *mpoint, Boolean newfs)
363 {
364     PartInfo *pi;
365
366     if (!mpoint)
367         mpoint = (type == PART_EFI) ? "/efi" : "/change_me";
368
369     pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
370     sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
371
372     pi->do_newfs = newfs;
373
374     if (type == PART_EFI) {
375         pi->newfs_type = NEWFS_MSDOS;
376     } else {
377         pi->newfs_type = NEWFS_UFS;
378         strcpy(pi->newfs_data.newfs_ufs.user_options, "");
379         pi->newfs_data.newfs_ufs.acls = FALSE;
380         pi->newfs_data.newfs_ufs.multilabel = FALSE;
381         pi->newfs_data.newfs_ufs.softupdates = strcmp(mpoint, "/");
382 #ifdef PC98
383         pi->newfs_data.newfs_ufs.ufs1 = TRUE;
384 #else
385         pi->newfs_data.newfs_ufs.ufs1 = FALSE;
386 #endif
387     }
388
389     return pi;
390 }
391
392 /* Get the mountpoint for a partition and save it away */
393 static PartInfo *
394 get_mountpoint(PartType type, struct chunk *old)
395 {
396     char *val;
397     PartInfo *tmp;
398     Boolean newfs;
399
400     if (old && old->private_data)
401         tmp = old->private_data;
402     else
403         tmp = NULL;
404     val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
405     val = msgGetInput(val, "Please specify a mount point for the partition");
406     if (!val || !*val) {
407         if (!old)
408             return NULL;
409         else {
410             free(old->private_data);
411             old->private_data = NULL;
412         }
413         return NULL;
414     }
415
416     /* Is it just the same value? */
417     if (tmp && !strcmp(tmp->mountpoint, val))
418         return NULL;
419
420     /* Did we use it already? */
421     if (check_conflict(val)) {
422         msgConfirm("You already have a mount point for %s assigned!", val);
423         return NULL;
424     }
425
426     /* Is it bogus? */
427     if (*val != '/') {
428         msgConfirm("Mount point must start with a / character");
429         return NULL;
430     }
431
432     /* Is it going to be mounted on root? */
433     if (!strcmp(val, "/")) {
434         if (old)
435             old->flags |= CHUNK_IS_ROOT;
436     }
437     else if (old)
438         old->flags &= ~CHUNK_IS_ROOT;
439
440     newfs = TRUE;
441     if (tmp) {
442         newfs = tmp->do_newfs;
443         safe_free(tmp);
444     }
445     val = string_skipwhite(string_prune(val));
446     tmp = new_part(type, val, newfs);
447     if (old) {
448         old->private_data = tmp;
449         old->private_free = safe_free;
450     }
451     return tmp;
452 }
453
454 /* Get the type of the new partiton */
455 static PartType
456 get_partition_type(void)
457 {
458     char selection[20];
459     int i;
460     static unsigned char *fs_types[] = {
461 #ifdef __ia64__
462         "EFI",  "An EFI system partition",
463 #endif
464         "FS",   "A file system",
465         "Swap", "A swap partition.",
466     };
467     WINDOW *w = savescr();
468
469     i = dialog_menu("Please choose a partition type",
470         "If you want to use this partition for swap space, select Swap.\n"
471         "If you want to put a filesystem on it, choose FS.",
472         -1, -1,
473 #ifdef __ia64__
474         3, 3,
475 #else
476         2, 2,
477 #endif
478         fs_types, selection, NULL, NULL);
479     restorescr(w);
480     if (!i) {
481 #ifdef __ia64__
482         if (!strcmp(selection, "EFI"))
483             return PART_EFI;
484 #endif
485         if (!strcmp(selection, "FS"))
486             return PART_FILESYSTEM;
487         else if (!strcmp(selection, "Swap"))
488             return PART_SWAP;
489     }
490     return PART_NONE;
491 }
492
493 /* If the user wants a special newfs command for this, set it */
494 static void
495 getNewfsCmd(PartInfo *p)
496 {
497     char buffer[NEWFS_CMD_ARGS_MAX];
498     char *val;
499
500     switch (p->newfs_type) {
501     case NEWFS_UFS:
502         snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
503             NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ?  "-U" : "",
504             p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
505             p->newfs_data.newfs_ufs.user_options);
506         break;
507     case NEWFS_MSDOS:
508         snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
509         break;
510     case NEWFS_CUSTOM:
511         strcpy(buffer, p->newfs_data.newfs_custom.command);
512         break;
513     }
514
515     val = msgGetInput(buffer,
516         "Please enter the newfs command and options you'd like to use in\n"
517         "creating this file system.");
518     if (val != NULL) {
519         p->newfs_type = NEWFS_CUSTOM;
520         strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
521     }
522 }
523
524 static void
525 getNewfsOptionalArguments(PartInfo *p)
526 {
527         char buffer[NEWFS_CMD_ARGS_MAX];
528         char *val;
529
530         /* Must be UFS, per argument checking in I/O routines. */
531
532         strlcpy(buffer,  p->newfs_data.newfs_ufs.user_options,
533             NEWFS_CMD_ARGS_MAX);
534         val = msgGetInput(buffer,
535             "Please enter any additional UFS newfs options you'd like to\n"
536             "use in creating this file system.");
537         if (val != NULL)
538                 strlcpy(p->newfs_data.newfs_ufs.user_options, val,
539                     NEWFS_CMD_ARGS_MAX);
540 }
541
542 #define MAX_MOUNT_NAME  9
543
544 #define PART_PART_COL   0
545 #define PART_MOUNT_COL  10
546 #define PART_SIZE_COL   (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
547 #define PART_NEWFS_COL  (PART_SIZE_COL + 8)
548 #define PART_OFF        38
549
550 #define TOTAL_AVAIL_LINES       (10)
551 #define PSLICE_SHOWABLE          (4)
552
553
554 /* stick this all up on the screen */
555 static void
556 print_label_chunks(void)
557 {
558     int  i, j, srow, prow, pcol;
559     daddr_t sz;
560     char clrmsg[80];
561     int ChunkPartStartRow;
562     WINDOW *ChunkWin;
563
564     /********************************************************/
565     /*** These values are for controling screen resources ***/
566     /*** Each label line holds up to 2 labels, so beware! ***/
567     /*** strategy will be to try to always make sure the  ***/
568     /*** highlighted label is in the active display area. ***/
569     /********************************************************/
570     int  pslice_max, label_max;
571     int  pslice_count, label_count, label_focus_found, pslice_focus_found;
572
573     attrset(A_REVERSE);
574     mvaddstr(0, 25, "FreeBSD Disklabel Editor");
575     attrset(A_NORMAL);
576
577     /*** Count the number of parition slices ***/
578     pslice_count = 0;
579     for (i = 0; label_chunk_info[i].c ; i++) {
580         if (label_chunk_info[i].type == PART_SLICE)
581             ++pslice_count;
582     }
583     pslice_max = pslice_count;
584
585     /*** 4 line max for partition slices ***/
586     if (pslice_max > PSLICE_SHOWABLE) {
587         pslice_max = PSLICE_SHOWABLE;
588     }
589     ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
590     
591     /*** View partition slices modulo pslice_max ***/
592     label_max = TOTAL_AVAIL_LINES - pslice_max;
593
594     for (i = 0; i < 2; i++) {
595         mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
596         mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
597
598         mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
599         mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
600
601         mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
602         mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
603
604         mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
605         mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
606     }
607     srow = CHUNK_SLICE_START_ROW;
608     prow = 0;
609     pcol = 0;
610
611     /*** these variables indicate that the focused item is shown currently ***/
612     label_focus_found = 0;
613     pslice_focus_found = 0;
614    
615     label_count = 0;
616     pslice_count = 0;
617     mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "          ");
618     mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "          ");
619
620     ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
621
622     wclear(ChunkWin);
623     /*** wrefresh(ChunkWin); ***/
624
625     for (i = 0; label_chunk_info[i].c; i++) {
626         /* Is it a slice entry displayed at the top? */
627         if (label_chunk_info[i].type == PART_SLICE) {
628             /*** This causes the new pslice to replace the previous display ***/
629             /*** focus must remain on the most recently active pslice       ***/
630             if (pslice_count == pslice_max) {
631                 if (pslice_focus_found) {
632                     /*** This is where we can mark the more following ***/
633                     attrset(A_BOLD);
634                     mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
635                     attrset(A_NORMAL);
636                     continue;
637                 }
638                 else {
639                     /*** this is where we set the more previous ***/
640                     attrset(A_BOLD);
641                     mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
642                     attrset(A_NORMAL);
643                     pslice_count = 0;
644                     srow = CHUNK_SLICE_START_ROW;
645                 }
646             }
647
648             sz = space_free(label_chunk_info[i].c);
649             if (i == here)
650                 attrset(ATTR_SELECTED);
651             if (i == pslice_focus)
652                 pslice_focus_found = -1;
653
654             if (label_chunk_info[i].c->type == whole) {
655                 if (sz >= 100 * ONE_GIG)
656                     mvprintw(srow++, 0,
657                         "Disk: %s\t\tFree: %jd blocks (%jdGB)",
658                         label_chunk_info[i].c->disk->name, (intmax_t)sz,
659                         (intmax_t)(sz / ONE_GIG));
660                 else
661                     mvprintw(srow++, 0,
662                         "Disk: %s\t\tFree: %jd blocks (%jdMB)",
663                         label_chunk_info[i].c->disk->name, (intmax_t)sz,
664                         (intmax_t)(sz / ONE_MEG));
665             } else {
666                 if (sz >= 100 * ONE_GIG)
667                     mvprintw(srow++, 0, 
668                         "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
669                         label_chunk_info[i].c->disk->name,
670                         label_chunk_info[i].c->name, 
671                         (intmax_t)sz, (intmax_t)(sz / ONE_GIG));
672                 else
673                     mvprintw(srow++, 0, 
674                         "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
675                         label_chunk_info[i].c->disk->name,
676                         label_chunk_info[i].c->name, 
677                         (intmax_t)sz, (intmax_t)(sz / ONE_MEG));
678             }
679             attrset(A_NORMAL);
680             clrtoeol();
681             move(0, 0);
682             /*** refresh(); ***/
683             ++pslice_count;
684         }
685         /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
686         else {
687             char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
688
689             /*
690              * We copy this into a blank-padded string so that it looks like
691              * a solid bar in reverse-video
692              */
693             memset(onestr, ' ', PART_OFF - 1);
694             onestr[PART_OFF - 1] = '\0';
695
696             /*** Track how many labels have been displayed ***/
697             if (label_count == ((label_max - 1 ) * 2)) {
698                 if (label_focus_found) {
699                     continue;
700                 }
701                 else {
702                     label_count = 0;
703                     prow = 0;
704                     pcol = 0;
705                 }
706             }
707
708             /* Go for two columns if we've written one full columns worth */
709             /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
710             if (label_count == label_max - 1) {
711                 pcol = PART_OFF;
712                 prow = 0;
713             }
714             memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
715             /* If it's a filesystem, display the mountpoint */
716             if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
717                 || label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
718                 mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
719             else if (label_chunk_info[i].type == PART_SWAP)
720                 mountpoint = "swap";
721             else
722                 mountpoint = "<none>";
723
724             /* Now display the newfs field */
725             if (label_chunk_info[i].type == PART_FAT)
726                 strcpy(newfs, "DOS");
727 #if defined(__ia64__)
728             else if (label_chunk_info[i].type == PART_EFI) {
729                 strcpy(newfs, "EFI");
730                 if (label_chunk_info[i].c->private_data) {
731                     strcat(newfs, "  ");
732                     PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
733                     strcat(newfs, pi->do_newfs ? " Y" : " N");
734                 }
735             }
736 #endif
737             else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
738                 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
739
740                 switch (pi->newfs_type) {
741                 case NEWFS_UFS:
742                         strcpy(newfs, NEWFS_UFS_STRING);
743                         if (pi->newfs_data.newfs_ufs.ufs1)
744                                 strcat(newfs, "1");
745                         else
746                                 strcat(newfs, "2");
747                         if (pi->newfs_data.newfs_ufs.softupdates)
748                                 strcat(newfs, "+S");
749                         else
750                                 strcat(newfs, "  ");
751
752                         break;
753                 case NEWFS_MSDOS:
754                         strcpy(newfs, "FAT");
755                         break;
756                 case NEWFS_CUSTOM:
757                         strcpy(newfs, "CUST");
758                         break;
759                 }
760                 strcat(newfs, pi->do_newfs ? " Y" : " N ");
761             }
762             else if (label_chunk_info[i].type == PART_SWAP)
763                 strcpy(newfs, "SWAP");
764             else
765                 strcpy(newfs, "*");
766             for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
767                 onestr[PART_MOUNT_COL + j] = mountpoint[j];
768             if (label_chunk_info[i].c->size == 0)
769                 snprintf(num, 10, "%5dMB", 0);
770             else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
771                 snprintf(num, 10, "%5jdMB",
772                     (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
773             else
774                 snprintf(num, 10, "%5jdGB",
775                     (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
776             memcpy(onestr + PART_SIZE_COL, num, strlen(num));
777             memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
778             onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
779             if (i == label_focus) {
780                 label_focus_found = -1;
781                 wattrset(ChunkWin, A_BOLD);
782             }
783             if (i == here)
784                 wattrset(ChunkWin, ATTR_SELECTED);
785
786             /*** lazy man's way of expensively padding this string ***/
787             while (strlen(onestr) < 37)
788                 strcat(onestr, " ");
789
790             mvwaddstr(ChunkWin, prow, pcol, onestr);
791             wattrset(ChunkWin, A_NORMAL);
792             move(0, 0);
793             ++prow;
794             ++label_count;
795         }
796     }
797     
798     /*** this will erase all the extra stuff ***/
799     memset(clrmsg, ' ', 37);
800     clrmsg[37] = '\0';
801    
802     while (pslice_count < pslice_max) {
803         mvprintw(srow++, 0, clrmsg);
804         clrtoeol();
805         ++pslice_count;
806     }
807     while (label_count < (2 * (label_max - 1))) {
808         mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
809         ++label_count;
810         if (prow == (label_max - 1)) {
811             prow = 0;
812             pcol = PART_OFF;
813         }
814     }
815     refresh();
816     wrefresh(ChunkWin);
817 }
818
819 static void
820 print_command_summary(void)
821 {
822     mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
823     mvprintw(18, 0, "C = Create        D = Delete   M = Mount pt.");
824     mvprintw(18, 56, "W = Write");
825     mvprintw(19, 0, "N = Newfs Opts    Q = Finish   S = Toggle SoftUpdates   Z = Custom Newfs");
826     mvprintw(20, 0, "T = Toggle Newfs  U = Undo     A = Auto Defaults        R = Delete+Merge");
827     mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
828     move(0, 0);
829 }
830
831 static void
832 clear_wins(void)
833 {
834
835     clear();
836     print_label_chunks();
837 }
838
839 static int
840 diskLabel(Device *dev)
841 {
842     daddr_t sz;
843     int  key = 0;
844     Boolean labeling;
845     char *msg = NULL;
846     PartInfo *p, *oldp;
847     PartType type;
848     Device **devs;
849     WINDOW *w = savescr();
850
851     label_focus = 0;
852     pslice_focus = 0;
853     here = 0;
854
855     devs = deviceFind(NULL, DEVICE_TYPE_DISK);
856     if (!devs) {
857         msgConfirm("No disks found!");
858         restorescr(w);
859         return DITEM_FAILURE;
860     }
861     labeling = TRUE;
862     keypad(stdscr, TRUE);
863     record_label_chunks(devs, dev);
864
865     clear();
866     while (labeling) {
867         char *cp;
868         int rflags = DELCHUNK_NORMAL;
869
870         print_label_chunks();
871         print_command_summary();
872         if (msg) {
873             attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
874             clrtoeol();
875             beep();
876             msg = NULL;
877         }
878         else {
879             move(23, 0);
880             clrtoeol();
881         }
882
883         refresh();
884         key = getch();
885         switch (toupper(key)) {
886             int i;
887             static char _msg[40];
888
889         case '\014':    /* ^L */
890             clear_wins();
891             break;
892
893         case '\020':    /* ^P */
894         case KEY_UP:
895         case '-':
896             if (here != 0)
897                 --here;
898             else
899                 while (label_chunk_info[here + 1].c)
900                     ++here;
901             break;
902
903         case '\016':    /* ^N */
904         case KEY_DOWN:
905         case '+':
906         case '\r':
907         case '\n':
908             if (label_chunk_info[here + 1].c)
909                 ++here;
910             else
911                 here = 0;
912             break;
913
914         case KEY_HOME:
915             here = 0;
916             break;
917
918         case KEY_END:
919             while (label_chunk_info[here + 1].c)
920                 ++here;
921             break;
922
923         case KEY_F(1):
924         case '?':
925             systemDisplayHelp("partition");
926             clear_wins();
927             break;
928
929         case '1':
930             if (label_chunk_info[here].type == PART_FILESYSTEM) {
931                 PartInfo *pi =
932                     ((PartInfo *)label_chunk_info[here].c->private_data);
933
934                 if ((pi != NULL) &&
935                     (pi->newfs_type == NEWFS_UFS)) {
936                         pi->newfs_data.newfs_ufs.ufs1 = true;
937                 } else
938                     msg = MSG_NOT_APPLICABLE;
939             } else
940                 msg = MSG_NOT_APPLICABLE;
941             break;
942                 break;
943
944         case '2':
945             if (label_chunk_info[here].type == PART_FILESYSTEM) {
946                 PartInfo *pi =
947                     ((PartInfo *)label_chunk_info[here].c->private_data);
948
949                 if ((pi != NULL) &&
950                     (pi->newfs_type == NEWFS_UFS)) {
951                         pi->newfs_data.newfs_ufs.ufs1 = false;
952                 } else
953                     msg = MSG_NOT_APPLICABLE;
954             } else
955                 msg = MSG_NOT_APPLICABLE;
956             break;
957                 break;
958
959         case 'A':
960             if (label_chunk_info[here].type != PART_SLICE) {
961                 msg = "You can only do this in a disk slice (at top of screen)";
962                 break;
963             }
964             /*
965              * Generate standard partitions automatically.  If we do not
966              * have sufficient space we attempt to scale-down the size
967              * of the partitions within certain bounds.
968              */
969             {
970                 int perc;
971                 int req = 0;
972
973                 for (perc = 100; perc > 0; perc -= 5) {
974                     req = 0;    /* reset for each loop */
975                     if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
976                         break;
977                 }
978                 if (msg) {
979                     if (req) {
980                         msgConfirm("%s", msg);
981                         clear_wins();
982                         msg = NULL;
983                     }
984                 }
985             }
986             break;
987             
988         case 'C':
989             if (label_chunk_info[here].type != PART_SLICE) {
990                 msg = "You can only do this in a master partition (see top of screen)";
991                 break;
992             }
993             sz = space_free(label_chunk_info[here].c);
994             if (sz <= FS_MIN_SIZE) {
995                 msg = "Not enough space to create an additional FreeBSD partition";
996                 break;
997             }
998             else {
999                 char *val;
1000                 daddr_t size;
1001                 struct chunk *tmp;
1002                 char osize[80];
1003                 u_long flags = 0;
1004
1005 #ifdef __powerpc__
1006                 /* Always use the maximum size for apple partitions */
1007                 if (label_chunk_info[here].c->type == apple)
1008                     size = sz;
1009                 else {
1010 #endif
1011                 sprintf(osize, "%jd", (intmax_t)sz);
1012                 val = msgGetInput(osize,
1013                                   "Please specify the partition size in blocks or append a trailing G for\n"
1014 #ifdef __ia64__
1015                                   "gigabytes, M for megabytes.\n"
1016 #else
1017                                   "gigabytes, M for megabytes, or C for cylinders.\n"
1018 #endif
1019                                   "%jd blocks (%jdMB) are free.",
1020                                   (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1021                 if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
1022                     clear_wins();
1023                     break;
1024                 }
1025
1026                 if (*cp) {
1027                     if (toupper(*cp) == 'M')
1028                         size *= ONE_MEG;
1029                     else if (toupper(*cp) == 'G')
1030                         size *= ONE_GIG;
1031 #ifndef __ia64__
1032                     else if (toupper(*cp) == 'C')
1033                         size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1034 #endif
1035                 }
1036                 if (size <= FS_MIN_SIZE) {
1037                     msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1038                     clear_wins();
1039                     break;
1040                 }
1041 #ifdef __powerpc__
1042                 }
1043 #endif
1044                 type = get_partition_type();
1045                 if (type == PART_NONE) {
1046                     clear_wins();
1047                     beep();
1048                     break;
1049                 }
1050
1051                 if (type == PART_FILESYSTEM || type == PART_EFI) {
1052                     if ((p = get_mountpoint(type, NULL)) == NULL) {
1053                         clear_wins();
1054                         beep();
1055                         break;
1056                     }
1057                     else if (!strcmp(p->mountpoint, "/")) {
1058                         if (type != PART_FILESYSTEM) {
1059                             clear_wins();
1060                             beep();
1061                             break;
1062                         }
1063                         else
1064                             flags |= CHUNK_IS_ROOT;
1065                     }
1066                     else
1067                         flags &= ~CHUNK_IS_ROOT;
1068                 }
1069                 else
1070                     p = NULL;
1071
1072                 if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1073                     msgConfirm("Warning: This is smaller than the recommended size for a\n"
1074                                "root partition.  For a variety of reasons, root\n"
1075                                "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1076                 }
1077                 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1078                     label_chunk_info[here].c, size,
1079 #ifdef __ia64__
1080                     (type == PART_EFI) ? efi : part,
1081                     (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1082 #else
1083                     part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1084 #endif
1085                     flags);
1086                 if (!tmp) {
1087                     msgConfirm("Unable to create the partition. Too big?");
1088                     clear_wins();
1089                     break;
1090                 }
1091
1092 #ifdef __alpha__
1093                 /*
1094                  * SRM requires that the root partition is at the
1095                  * begining of the disk and cannot boot otherwise. 
1096                  * Warn Alpha users if they are about to shoot themselves in
1097                  * the foot in this way.
1098                  *
1099                  * Since partitions may not start precisely at offset 0 we
1100                  * check for a "close to 0" instead. :-(
1101                  */
1102                 if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
1103                     msgConfirm("Your root partition `a' does not seem to be the first\n"
1104                                "partition.  The Alpha's firmware can only boot from the\n"
1105                                "first partition.  So it is unlikely that your current\n"
1106                                "disk layout will be bootable boot after installation.\n"
1107                                "\n"
1108                                "Please allocate the root partition before allocating\n"
1109                                "any others.\n");
1110                 }
1111 #endif  /* alpha */
1112
1113                 tmp->private_data = p;
1114                 tmp->private_free = safe_free;
1115                 if (variable_cmp(DISK_LABELLED, "written"))
1116                     variable_set2(DISK_LABELLED, "yes", 0);
1117                 record_label_chunks(devs, dev);
1118                 clear_wins();
1119                 /* This is where we assign focus to new label so it shows. */
1120                 {
1121                     int i;
1122                     label_focus = -1;
1123                     for (i = 0; label_chunk_info[i].c; ++i) {
1124                         if (label_chunk_info[i].c == tmp) {
1125                             label_focus = i;
1126                             break;
1127                         }
1128                     }
1129                     if (label_focus == -1)
1130                         label_focus = i - 1;
1131                 }
1132             }
1133             break;
1134
1135         case KEY_DC:
1136         case 'R':       /* recover space (delete w/ recover) */
1137             /*
1138              * Delete the partition w/ space recovery.
1139              */
1140             rflags = DELCHUNK_RECOVER;
1141             /* fall through */
1142         case 'D':       /* delete */
1143             if (label_chunk_info[here].type == PART_SLICE) {
1144                 msg = MSG_NOT_APPLICABLE;
1145                 break;
1146             }
1147             else if (label_chunk_info[here].type == PART_FAT) {
1148                 msg = "Use the Disk Partition Editor to delete DOS partitions";
1149                 break;
1150             }
1151             Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1152             if (variable_cmp(DISK_LABELLED, "written"))
1153                 variable_set2(DISK_LABELLED, "yes", 0);
1154             record_label_chunks(devs, dev);
1155             break;
1156
1157         case 'M':       /* mount */
1158             switch(label_chunk_info[here].type) {
1159             case PART_SLICE:
1160                 msg = MSG_NOT_APPLICABLE;
1161                 break;
1162
1163             case PART_SWAP:
1164                 msg = "You don't need to specify a mountpoint for a swap partition.";
1165                 break;
1166
1167             case PART_FAT:
1168             case PART_EFI:
1169             case PART_FILESYSTEM:
1170                 oldp = label_chunk_info[here].c->private_data;
1171                 p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1172                 if (p) {
1173                     if (!oldp)
1174                         p->do_newfs = FALSE;
1175                     if ((label_chunk_info[here].type == PART_FAT ||
1176                             label_chunk_info[here].type == PART_EFI) &&
1177                         (!strcmp(p->mountpoint, "/") ||
1178                             !strcmp(p->mountpoint, "/usr") ||
1179                             !strcmp(p->mountpoint, "/var"))) {
1180                         msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1181                         strcpy(p->mountpoint, "/bogus");
1182                     }
1183                 }
1184                 if (variable_cmp(DISK_LABELLED, "written"))
1185                     variable_set2(DISK_LABELLED, "yes", 0);
1186                 record_label_chunks(devs, dev);
1187                 clear_wins();
1188                 break;
1189
1190             default:
1191                 msgFatal("Bogus partition under cursor???");
1192                 break;
1193             }
1194             break;
1195
1196         case 'N':       /* Set newfs options */
1197             if (label_chunk_info[here].c->private_data &&
1198                 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1199                 getNewfsOptionalArguments(
1200                     label_chunk_info[here].c->private_data);
1201             else
1202                 msg = MSG_NOT_APPLICABLE;
1203             clear_wins();
1204             break;
1205
1206         case 'S':       /* Toggle soft updates flag */
1207             if (label_chunk_info[here].type == PART_FILESYSTEM) {
1208                 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1209                 if (pi != NULL &&
1210                     pi->newfs_type == NEWFS_UFS)
1211                         pi->newfs_data.newfs_ufs.softupdates =
1212                             !pi->newfs_data.newfs_ufs.softupdates;
1213                 else
1214                     msg = MSG_NOT_APPLICABLE;
1215             }
1216             else
1217                 msg = MSG_NOT_APPLICABLE;
1218             break;
1219
1220         case 'T':       /* Toggle newfs state */
1221             if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1222                  label_chunk_info[here].type == PART_EFI) &&
1223                 (label_chunk_info[here].c->private_data)) {
1224                 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1225                 if (!pi->do_newfs)
1226                     label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1227                 else
1228                     label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1229
1230                 label_chunk_info[here].c->private_data =
1231                     new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1232                     : TRUE);
1233                 if (pi != NULL &&
1234                     pi->newfs_type == NEWFS_UFS) {
1235                     PartInfo *pi_new = label_chunk_info[here].c->private_data;
1236
1237                     pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1238                 }
1239                 safe_free(pi);
1240                 label_chunk_info[here].c->private_free = safe_free;
1241                 if (variable_cmp(DISK_LABELLED, "written"))
1242                     variable_set2(DISK_LABELLED, "yes", 0);
1243             }
1244             else
1245                 msg = MSG_NOT_APPLICABLE;
1246             break;
1247
1248         case 'U':
1249             clear();
1250             if (!variable_cmp(DISK_LABELLED, "written")) {
1251                 msgConfirm("You've already written out your changes -\n"
1252                            "it's too late to undo!");
1253             }
1254             else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1255                 variable_unset(DISK_PARTITIONED);
1256                 variable_unset(DISK_LABELLED);
1257                 for (i = 0; devs[i]; i++) {
1258                     Disk *d;
1259
1260                     if (!devs[i]->enabled)
1261                         continue;
1262                     else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1263                         Free_Disk(devs[i]->private);
1264                         devs[i]->private = d;
1265 #ifdef WITH_SLICES
1266                         diskPartition(devs[i]);
1267 #endif
1268                     }
1269                 }
1270                 record_label_chunks(devs, dev);
1271             }
1272             clear_wins();
1273             break;
1274
1275         case 'W':
1276             if (!variable_cmp(DISK_LABELLED, "written")) {
1277                 msgConfirm("You've already written out your changes - if you\n"
1278                            "wish to overwrite them, you'll have to restart\n"
1279                            "%s first.", ProgName);
1280             }
1281             else if (!msgNoYes("WARNING:  You are about to modify an EXISTING\n"
1282                           "installation.\n\n"
1283                           "Are you absolutely sure you want to continue?")) {
1284                 variable_set2(DISK_LABELLED, "yes", 0);
1285                 diskLabelCommit(NULL);
1286             }
1287             clear_wins();
1288             break;
1289
1290         case 'Z':       /* Set newfs command line */
1291             if (label_chunk_info[here].c->private_data &&
1292                 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1293                 getNewfsCmd(label_chunk_info[here].c->private_data);
1294             else
1295                 msg = MSG_NOT_APPLICABLE;
1296             clear_wins();
1297             break;
1298
1299 #ifndef __ia64__
1300         case '|':
1301             if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1302                           "This is an entirely undocumented feature which you are not\n"
1303                           "expected to understand!")) {
1304                 int i;
1305                 Device **devs;
1306
1307                 dialog_clear();
1308                 end_dialog();
1309                 DialogActive = FALSE;
1310                 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1311                 if (!devs) {
1312                     msgConfirm("Can't find any disk devices!");
1313                     break;
1314                 }
1315                 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1316                     if (devs[i]->enabled)
1317                         slice_wizard(((Disk *)devs[i]->private));
1318                 }
1319                 if (variable_cmp(DISK_LABELLED, "written"))
1320                     variable_set2(DISK_LABELLED, "yes", 0);
1321                 DialogActive = TRUE;
1322                 record_label_chunks(devs, dev);
1323                 clear_wins();
1324             }
1325             else
1326                 msg = "A most prudent choice!";
1327             break;
1328 #endif
1329
1330         case '\033':    /* ESC */
1331         case 'Q':
1332             labeling = FALSE;
1333             break;
1334
1335         default:
1336             beep();
1337             sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1338             msg = _msg;
1339             break;
1340         }
1341         if (label_chunk_info[here].type == PART_SLICE)
1342             pslice_focus = here;
1343         else
1344             label_focus = here;
1345     }
1346     restorescr(w);
1347     return DITEM_SUCCESS;
1348 }
1349
1350 static __inline daddr_t
1351 requested_part_size(char *varName, daddr_t nom, int def, int perc)
1352 {
1353     char *cp;
1354     daddr_t sz;
1355
1356     if ((cp = variable_get(varName)) != NULL)
1357         sz = strtoimax(cp, NULL, 0);
1358     else
1359         sz = nom + (def - nom) * perc / 100;
1360     return(sz * ONE_MEG);
1361 }
1362
1363 /*
1364  * Attempt to auto-label the disk.  'perc' (0-100) scales
1365  * the size of the various partitions within appropriate
1366  * bounds (NOMINAL through DEFAULT sizes).  The procedure
1367  * succeeds of NULL is returned.  A non-null return message
1368  * is either a failure-status message (*req == 0), or 
1369  * a confirmation requestor (*req == 1).  *req is 0 on
1370  * entry to this call.
1371  *
1372  * As a special exception to the usual sizing rules, /var is given
1373  * additional space equal to the amount of physical memory present
1374  * if perc == 100 in order to ensure that users with large hard drives
1375  * will have enough space to store a crashdump in /var/crash.
1376  *
1377  * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1378  * and /home.  /home receives any extra left over disk space. 
1379  */
1380 static char *
1381 try_auto_label(Device **devs, Device *dev, int perc, int *req)
1382 {
1383     daddr_t sz;
1384     Chunk *AutoHome, *AutoRoot, *AutoSwap;
1385     Chunk *AutoTmp, *AutoUsr, *AutoVar;
1386 #ifdef __ia64__
1387     Chunk *AutoEfi;
1388 #endif
1389     int mib[2];
1390     unsigned long physmem;
1391     size_t size;
1392     char *msg = NULL;
1393
1394     sz = space_free(label_chunk_info[here].c);
1395     if (sz <= FS_MIN_SIZE)
1396         return("Not enough free space to create a new partition in the slice");
1397
1398     (void)checkLabels(FALSE);
1399     AutoHome = AutoRoot = AutoSwap = NULL;
1400     AutoTmp = AutoUsr = AutoVar = NULL;
1401
1402 #ifdef __ia64__
1403     AutoEfi = NULL;
1404     if (EfiChunk == NULL) {
1405         sz = 100 * ONE_MEG;
1406         AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1407             label_chunk_info[here].c, sz, efi, 0, 0);
1408         if (AutoEfi == NULL) {
1409             *req = 1;
1410             msg = "Unable to create the EFI system partition. Too big?";
1411             goto done;
1412         }
1413         AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1414         AutoEfi->private_free = safe_free;
1415         AutoEfi->flags |= CHUNK_NEWFS;
1416         record_label_chunks(devs, dev);
1417     }
1418 #endif
1419
1420     if (RootChunk == NULL) {
1421         sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1422
1423         AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1424                             label_chunk_info[here].c, sz, part,
1425                             FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1426         if (!AutoRoot) {
1427             *req = 1;
1428             msg = "Unable to create the root partition. Too big?";
1429             goto done;
1430         }
1431         AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1432         AutoRoot->private_free = safe_free;
1433         AutoRoot->flags |= CHUNK_NEWFS;
1434         record_label_chunks(devs, dev);
1435     }
1436     if (SwapChunk == NULL) {
1437         sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1438         if (sz == 0) {
1439             daddr_t nom;
1440             daddr_t def;
1441
1442             mib[0] = CTL_HW;
1443             mib[1] = HW_PHYSMEM;
1444             size = sizeof physmem;
1445             sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1446             def = 2 * (int)(physmem / 512);
1447             if (def < SWAP_MIN_SIZE * ONE_MEG)
1448                 def = SWAP_MIN_SIZE * ONE_MEG;
1449             if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1450                 def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1451             nom = (int)(physmem / 512) / 8;
1452             sz = nom + (def - nom) * perc / 100;
1453         }
1454         AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1455                             label_chunk_info[here].c, sz, part,
1456                             FS_SWAP, CHUNK_AUTO_SIZE);
1457         if (!AutoSwap) {
1458             *req = 1;
1459             msg = "Unable to create the swap partition. Too big?";
1460             goto done;
1461         }
1462         AutoSwap->private_data = 0;
1463         AutoSwap->private_free = safe_free;
1464         record_label_chunks(devs, dev);
1465     }
1466     if (VarChunk == NULL) {
1467         /* Work out how much extra space we want for a crash dump */
1468         unsigned long crashdumpsz;
1469
1470         mib[0] = CTL_HW;
1471         mib[1] = HW_PHYSMEM;
1472         size = sizeof(physmem);
1473         sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1474
1475         if (perc == 100)
1476                 crashdumpsz = physmem / 1048576;
1477         else
1478                 crashdumpsz = 0;
1479
1480         sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE,        \
1481             VAR_DEFAULT_SIZE + crashdumpsz, perc);
1482
1483         AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1484                                 label_chunk_info[here].c, sz, part,
1485                                 FS_BSDFFS, CHUNK_AUTO_SIZE);
1486         if (!AutoVar) {
1487             *req = 1;
1488             msg = "Not enough free space for /var - you will need to\n"
1489                    "partition your disk manually with a custom install!";
1490             goto done;
1491         }
1492         AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1493         AutoVar->private_free = safe_free;
1494         AutoVar->flags |= CHUNK_NEWFS;
1495         record_label_chunks(devs, dev);
1496     }
1497     if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1498         sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1499
1500         AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1501                                 label_chunk_info[here].c, sz, part,
1502                                 FS_BSDFFS, CHUNK_AUTO_SIZE);
1503         if (!AutoTmp) {
1504             *req = 1;
1505             msg = "Not enough free space for /tmp - you will need to\n"
1506                    "partition your disk manually with a custom install!";
1507             goto done;
1508         }
1509         AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1510         AutoTmp->private_free = safe_free;
1511         AutoTmp->flags |= CHUNK_NEWFS;
1512         record_label_chunks(devs, dev);
1513     }
1514     if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1515         sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1516 #if AUTO_HOME == 0
1517         if (sz < space_free(label_chunk_info[here].c))
1518             sz = space_free(label_chunk_info[here].c);
1519 #endif
1520         if (sz) {
1521             if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1522                 *req = 1;
1523                 msg = "Not enough free space for /usr - you will need to\n"
1524                        "partition your disk manually with a custom install!";
1525             }
1526
1527             AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1528                                     label_chunk_info[here].c, sz, part,
1529                                     FS_BSDFFS, CHUNK_AUTO_SIZE);
1530             if (!AutoUsr) {
1531                 msg = "Unable to create the /usr partition.  Not enough space?\n"
1532                            "You will need to partition your disk manually with a custom install!";
1533                 goto done;
1534             }
1535             AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1536             AutoUsr->private_free = safe_free;
1537             AutoUsr->flags |= CHUNK_NEWFS;
1538             record_label_chunks(devs, dev);
1539         }
1540     }
1541 #if AUTO_HOME == 1
1542     if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1543         sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1544         if (sz < space_free(label_chunk_info[here].c))
1545             sz = space_free(label_chunk_info[here].c);
1546         if (sz) {
1547             if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1548                 *req = 1;
1549                 msg = "Not enough free space for /home - you will need to\n"
1550                        "partition your disk manually with a custom install!";
1551                 goto done;
1552             }
1553
1554             AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1555                                     label_chunk_info[here].c, sz, part,
1556                                     FS_BSDFFS, CHUNK_AUTO_SIZE);
1557             if (!AutoHome) {
1558                 msg = "Unable to create the /home partition.  Not enough space?\n"
1559                            "You will need to partition your disk manually with a custom install!";
1560                 goto done;
1561             }
1562             AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1563             AutoHome->private_free = safe_free;
1564             AutoHome->flags |= CHUNK_NEWFS;
1565             record_label_chunks(devs, dev);
1566         }
1567     }
1568 #endif
1569
1570     /* At this point, we're reasonably "labelled" */
1571     if (variable_cmp(DISK_LABELLED, "written"))
1572         variable_set2(DISK_LABELLED, "yes", 0);
1573
1574 done:
1575     if (msg) {
1576         if (AutoRoot != NULL)
1577             Delete_Chunk(AutoRoot->disk, AutoRoot);
1578         if (AutoSwap != NULL)
1579             Delete_Chunk(AutoSwap->disk, AutoSwap);
1580         if (AutoVar != NULL)
1581             Delete_Chunk(AutoVar->disk, AutoVar);
1582         if (AutoTmp != NULL)
1583             Delete_Chunk(AutoTmp->disk, AutoTmp);
1584         if (AutoUsr != NULL)
1585             Delete_Chunk(AutoUsr->disk, AutoUsr);
1586         if (AutoHome != NULL)
1587             Delete_Chunk(AutoHome->disk, AutoHome);
1588         record_label_chunks(devs, dev);
1589     }
1590     return(msg);
1591 }
1592
1593 static int
1594 diskLabelNonInteractive(Device *dev)
1595 {
1596     char *cp;
1597     PartType type;
1598     PartInfo *p;
1599     u_long flags;
1600     int i, status;
1601     Device **devs;
1602     Disk *d;
1603     
1604     status = DITEM_SUCCESS;
1605     cp = variable_get(VAR_DISK);
1606     if (!cp) {
1607         msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1608         return DITEM_FAILURE;
1609     }
1610     devs = deviceFind(cp, DEVICE_TYPE_DISK);
1611     if (!devs) {
1612         msgConfirm("diskLabel: No disk device %s found!", cp);
1613         return DITEM_FAILURE;
1614     }
1615     if (dev)
1616         d = dev->private;
1617     else
1618         d = devs[0]->private;
1619     record_label_chunks(devs, dev);
1620     for (i = 0; label_chunk_info[i].c; i++) {
1621         Chunk *c1 = label_chunk_info[i].c;
1622
1623         if (label_chunk_info[i].type == PART_SLICE) {
1624             char name[512];
1625             char typ[10], mpoint[50];
1626             int entries;
1627
1628             for (entries = 1;; entries++) {
1629                 intmax_t sz;
1630                 int soft = 0;
1631                 snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1632                 if ((cp = variable_get(name)) == NULL)
1633                     break;
1634                 if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1635                     msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1636                     status = DITEM_FAILURE;
1637                     break;
1638                 } else {
1639                     Chunk *tmp;
1640
1641                     flags = 0;
1642                     if (!strcmp(typ, "swap")) {
1643                         type = PART_SWAP;
1644                         strcpy(mpoint, "SWAP");
1645                     } else {
1646                         type = PART_FILESYSTEM;
1647                         if (!strcmp(mpoint, "/"))
1648                             flags |= CHUNK_IS_ROOT;
1649                     }
1650                     if (!sz)
1651                         sz = space_free(c1);
1652                     if (sz > space_free(c1)) {
1653                         msgConfirm("Not enough free space to create partition: %s", mpoint);
1654                         status = DITEM_FAILURE;
1655                         break;
1656                     }
1657                     if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1658                         (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1659                         msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1660                         status = DITEM_FAILURE;
1661                         break;
1662                     } else {
1663                         PartInfo *pi;
1664                         pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1665                         tmp->private_free = safe_free;
1666                         pi->newfs_data.newfs_ufs.softupdates = soft;
1667                     }
1668                 }
1669             }
1670         } else {
1671             /* Must be something we can set a mountpoint for */
1672             cp = variable_get(c1->name);
1673             if (cp) {
1674                 char mpoint[50], do_newfs[8];
1675                 Boolean newfs = FALSE;
1676
1677                 do_newfs[0] = '\0';
1678                 if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1679                     msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1680                     status = DITEM_FAILURE;
1681                     break;
1682                 }
1683                 newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1684                 if (c1->private_data) {
1685                     p = c1->private_data;
1686                     p->do_newfs = newfs;
1687                     strcpy(p->mountpoint, mpoint);
1688                 }
1689                 else {
1690                     c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1691                     c1->private_free = safe_free;
1692                 }
1693                 if (!strcmp(mpoint, "/"))
1694                     c1->flags |= CHUNK_IS_ROOT;
1695                 else
1696                     c1->flags &= ~CHUNK_IS_ROOT;
1697             }
1698         }
1699     }
1700     if (status == DITEM_SUCCESS)
1701         variable_set2(DISK_LABELLED, "yes", 0);
1702     return status;
1703 }