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