]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/sysinstall/label.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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(__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         pi->newfs_data.newfs_ufs.ufs1 = FALSE;
388     }
389
390     return pi;
391 }
392
393 /* Get the mountpoint for a partition and save it away */
394 static PartInfo *
395 get_mountpoint(PartType type, struct chunk *old)
396 {
397     char *val;
398     PartInfo *tmp;
399     Boolean newfs;
400
401     if (old && old->private_data)
402         tmp = old->private_data;
403     else
404         tmp = NULL;
405     val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
406     val = msgGetInput(val, "Please specify a mount point for the partition");
407     if (!val || !*val) {
408         if (!old)
409             return NULL;
410         else {
411             free(old->private_data);
412             old->private_data = NULL;
413         }
414         return NULL;
415     }
416
417     /* Is it just the same value? */
418     if (tmp && !strcmp(tmp->mountpoint, val))
419         return NULL;
420
421     /* Did we use it already? */
422     if (check_conflict(val)) {
423         msgConfirm("You already have a mount point for %s assigned!", val);
424         return NULL;
425     }
426
427     /* Is it bogus? */
428     if (*val != '/') {
429         msgConfirm("Mount point must start with a / character");
430         return NULL;
431     }
432
433     /* Is it going to be mounted on root? */
434     if (!strcmp(val, "/")) {
435         if (old)
436             old->flags |= CHUNK_IS_ROOT;
437     }
438     else if (old)
439         old->flags &= ~CHUNK_IS_ROOT;
440
441     newfs = TRUE;
442     if (tmp) {
443         newfs = tmp->do_newfs;
444         safe_free(tmp);
445     }
446     val = string_skipwhite(string_prune(val));
447     tmp = new_part(type, val, newfs);
448     if (old) {
449         old->private_data = tmp;
450         old->private_free = safe_free;
451     }
452     return tmp;
453 }
454
455 /* Get the type of the new partiton */
456 static PartType
457 get_partition_type(void)
458 {
459     char selection[20];
460     int i;
461     static unsigned char *fs_types[] = {
462 #ifdef __ia64__
463         "EFI",  "An EFI system partition",
464 #endif
465         "FS",   "A file system",
466         "Swap", "A swap partition.",
467     };
468     WINDOW *w = savescr();
469
470     i = dialog_menu("Please choose a partition type",
471         "If you want to use this partition for swap space, select Swap.\n"
472         "If you want to put a filesystem on it, choose FS.",
473         -1, -1,
474 #ifdef __ia64__
475         3, 3,
476 #else
477         2, 2,
478 #endif
479         fs_types, selection, NULL, NULL);
480     restorescr(w);
481     if (!i) {
482 #ifdef __ia64__
483         if (!strcmp(selection, "EFI"))
484             return PART_EFI;
485 #endif
486         if (!strcmp(selection, "FS"))
487             return PART_FILESYSTEM;
488         else if (!strcmp(selection, "Swap"))
489             return PART_SWAP;
490     }
491     return PART_NONE;
492 }
493
494 /* If the user wants a special newfs command for this, set it */
495 static void
496 getNewfsCmd(PartInfo *p)
497 {
498     char buffer[NEWFS_CMD_ARGS_MAX];
499     char *val;
500
501     switch (p->newfs_type) {
502     case NEWFS_UFS:
503         snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
504             NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ?  "-U" : "",
505             p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
506             p->newfs_data.newfs_ufs.user_options);
507         break;
508     case NEWFS_MSDOS:
509         snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
510         break;
511     case NEWFS_CUSTOM:
512         strcpy(buffer, p->newfs_data.newfs_custom.command);
513         break;
514     }
515
516     val = msgGetInput(buffer,
517         "Please enter the newfs command and options you'd like to use in\n"
518         "creating this file system.");
519     if (val != NULL) {
520         p->newfs_type = NEWFS_CUSTOM;
521         strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
522     }
523 }
524
525 static void
526 getNewfsOptionalArguments(PartInfo *p)
527 {
528         char buffer[NEWFS_CMD_ARGS_MAX];
529         char *val;
530
531         /* Must be UFS, per argument checking in I/O routines. */
532
533         strlcpy(buffer,  p->newfs_data.newfs_ufs.user_options,
534             NEWFS_CMD_ARGS_MAX);
535         val = msgGetInput(buffer,
536             "Please enter any additional UFS newfs options you'd like to\n"
537             "use in creating this file system.");
538         if (val != NULL)
539                 strlcpy(p->newfs_data.newfs_ufs.user_options, val,
540                     NEWFS_CMD_ARGS_MAX);
541 }
542
543 #define MAX_MOUNT_NAME  9
544
545 #define PART_PART_COL   0
546 #define PART_MOUNT_COL  10
547 #define PART_SIZE_COL   (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
548 #define PART_NEWFS_COL  (PART_SIZE_COL + 8)
549 #define PART_OFF        38
550
551 #define TOTAL_AVAIL_LINES       (10)
552 #define PSLICE_SHOWABLE          (4)
553
554
555 /* stick this all up on the screen */
556 static void
557 print_label_chunks(void)
558 {
559     int  i, j, srow, prow, pcol;
560     daddr_t sz;
561     char clrmsg[80];
562     int ChunkPartStartRow;
563     WINDOW *ChunkWin;
564
565     /********************************************************/
566     /*** These values are for controling screen resources ***/
567     /*** Each label line holds up to 2 labels, so beware! ***/
568     /*** strategy will be to try to always make sure the  ***/
569     /*** highlighted label is in the active display area. ***/
570     /********************************************************/
571     int  pslice_max, label_max;
572     int  pslice_count, label_count, label_focus_found, pslice_focus_found;
573
574     attrset(A_REVERSE);
575     mvaddstr(0, 25, "FreeBSD Disklabel Editor");
576     attrset(A_NORMAL);
577
578     /*** Count the number of parition slices ***/
579     pslice_count = 0;
580     for (i = 0; label_chunk_info[i].c ; i++) {
581         if (label_chunk_info[i].type == PART_SLICE)
582             ++pslice_count;
583     }
584     pslice_max = pslice_count;
585
586     /*** 4 line max for partition slices ***/
587     if (pslice_max > PSLICE_SHOWABLE) {
588         pslice_max = PSLICE_SHOWABLE;
589     }
590     ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
591     
592     /*** View partition slices modulo pslice_max ***/
593     label_max = TOTAL_AVAIL_LINES - pslice_max;
594
595     for (i = 0; i < 2; i++) {
596         mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
597         mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
598
599         mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
600         mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
601
602         mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
603         mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
604
605         mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
606         mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
607     }
608     srow = CHUNK_SLICE_START_ROW;
609     prow = 0;
610     pcol = 0;
611
612     /*** these variables indicate that the focused item is shown currently ***/
613     label_focus_found = 0;
614     pslice_focus_found = 0;
615    
616     label_count = 0;
617     pslice_count = 0;
618     mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "          ");
619     mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "          ");
620
621     ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
622
623     wclear(ChunkWin);
624     /*** wrefresh(ChunkWin); ***/
625
626     for (i = 0; label_chunk_info[i].c; i++) {
627         /* Is it a slice entry displayed at the top? */
628         if (label_chunk_info[i].type == PART_SLICE) {
629             /*** This causes the new pslice to replace the previous display ***/
630             /*** focus must remain on the most recently active pslice       ***/
631             if (pslice_count == pslice_max) {
632                 if (pslice_focus_found) {
633                     /*** This is where we can mark the more following ***/
634                     attrset(A_BOLD);
635                     mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
636                     attrset(A_NORMAL);
637                     continue;
638                 }
639                 else {
640                     /*** this is where we set the more previous ***/
641                     attrset(A_BOLD);
642                     mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
643                     attrset(A_NORMAL);
644                     pslice_count = 0;
645                     srow = CHUNK_SLICE_START_ROW;
646                 }
647             }
648
649             sz = space_free(label_chunk_info[i].c);
650             if (i == here)
651                 attrset(ATTR_SELECTED);
652             if (i == pslice_focus)
653                 pslice_focus_found = -1;
654
655             if (label_chunk_info[i].c->type == whole) {
656                 if (sz >= 100 * ONE_GIG)
657                     mvprintw(srow++, 0,
658                         "Disk: %s\t\tFree: %jd blocks (%jdGB)",
659                         label_chunk_info[i].c->disk->name, (intmax_t)sz,
660                         (intmax_t)(sz / ONE_GIG));
661                 else
662                     mvprintw(srow++, 0,
663                         "Disk: %s\t\tFree: %jd blocks (%jdMB)",
664                         label_chunk_info[i].c->disk->name, (intmax_t)sz,
665                         (intmax_t)(sz / ONE_MEG));
666             } else {
667                 if (sz >= 100 * ONE_GIG)
668                     mvprintw(srow++, 0, 
669                         "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
670                         label_chunk_info[i].c->disk->name,
671                         label_chunk_info[i].c->name, 
672                         (intmax_t)sz, (intmax_t)(sz / ONE_GIG));
673                 else
674                     mvprintw(srow++, 0, 
675                         "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
676                         label_chunk_info[i].c->disk->name,
677                         label_chunk_info[i].c->name, 
678                         (intmax_t)sz, (intmax_t)(sz / ONE_MEG));
679             }
680             attrset(A_NORMAL);
681             clrtoeol();
682             move(0, 0);
683             /*** refresh(); ***/
684             ++pslice_count;
685         }
686         /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
687         else {
688             char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
689
690             /*
691              * We copy this into a blank-padded string so that it looks like
692              * a solid bar in reverse-video
693              */
694             memset(onestr, ' ', PART_OFF - 1);
695             onestr[PART_OFF - 1] = '\0';
696
697             /*** Track how many labels have been displayed ***/
698             if (label_count == ((label_max - 1 ) * 2)) {
699                 if (label_focus_found) {
700                     continue;
701                 }
702                 else {
703                     label_count = 0;
704                     prow = 0;
705                     pcol = 0;
706                 }
707             }
708
709             /* Go for two columns if we've written one full columns worth */
710             /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
711             if (label_count == label_max - 1) {
712                 pcol = PART_OFF;
713                 prow = 0;
714             }
715             memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
716             /* If it's a filesystem, display the mountpoint */
717             if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
718                 || label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
719                 mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
720             else if (label_chunk_info[i].type == PART_SWAP)
721                 mountpoint = "swap";
722             else
723                 mountpoint = "<none>";
724
725             /* Now display the newfs field */
726             if (label_chunk_info[i].type == PART_FAT)
727                 strcpy(newfs, "DOS");
728 #if defined(__ia64__)
729             else if (label_chunk_info[i].type == PART_EFI) {
730                 strcpy(newfs, "EFI");
731                 if (label_chunk_info[i].c->private_data) {
732                     strcat(newfs, "  ");
733                     PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
734                     strcat(newfs, pi->do_newfs ? " Y" : " N");
735                 }
736             }
737 #endif
738             else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
739                 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
740
741                 switch (pi->newfs_type) {
742                 case NEWFS_UFS:
743                         strcpy(newfs, NEWFS_UFS_STRING);
744                         if (pi->newfs_data.newfs_ufs.ufs1)
745                                 strcat(newfs, "1");
746                         else
747                                 strcat(newfs, "2");
748                         if (pi->newfs_data.newfs_ufs.softupdates)
749                                 strcat(newfs, "+S");
750                         else
751                                 strcat(newfs, "  ");
752
753                         break;
754                 case NEWFS_MSDOS:
755                         strcpy(newfs, "FAT");
756                         break;
757                 case NEWFS_CUSTOM:
758                         strcpy(newfs, "CUST");
759                         break;
760                 }
761                 strcat(newfs, pi->do_newfs ? " Y" : " N ");
762             }
763             else if (label_chunk_info[i].type == PART_SWAP)
764                 strcpy(newfs, "SWAP");
765             else
766                 strcpy(newfs, "*");
767             for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
768                 onestr[PART_MOUNT_COL + j] = mountpoint[j];
769             if (label_chunk_info[i].c->size == 0)
770                 snprintf(num, 10, "%5dMB", 0);
771             else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
772                 snprintf(num, 10, "%5jdMB",
773                     (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
774             else
775                 snprintf(num, 10, "%5jdGB",
776                     (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
777             memcpy(onestr + PART_SIZE_COL, num, strlen(num));
778             memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
779             onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
780             if (i == label_focus) {
781                 label_focus_found = -1;
782                 wattrset(ChunkWin, A_BOLD);
783             }
784             if (i == here)
785                 wattrset(ChunkWin, ATTR_SELECTED);
786
787             /*** lazy man's way of expensively padding this string ***/
788             while (strlen(onestr) < 37)
789                 strcat(onestr, " ");
790
791             mvwaddstr(ChunkWin, prow, pcol, onestr);
792             wattrset(ChunkWin, A_NORMAL);
793             move(0, 0);
794             ++prow;
795             ++label_count;
796         }
797     }
798     
799     /*** this will erase all the extra stuff ***/
800     memset(clrmsg, ' ', 37);
801     clrmsg[37] = '\0';
802    
803     while (pslice_count < pslice_max) {
804         mvprintw(srow++, 0, clrmsg);
805         clrtoeol();
806         ++pslice_count;
807     }
808     while (label_count < (2 * (label_max - 1))) {
809         mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
810         ++label_count;
811         if (prow == (label_max - 1)) {
812             prow = 0;
813             pcol = PART_OFF;
814         }
815     }
816     refresh();
817     wrefresh(ChunkWin);
818 }
819
820 static void
821 print_command_summary(void)
822 {
823     mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
824     mvprintw(18, 0, "C = Create        D = Delete   M = Mount pt.");
825     if (!RunningAsInit)
826         mvprintw(18, 56, "W = Write");
827     mvprintw(19, 0, "N = Newfs Opts    Q = Finish   S = Toggle SoftUpdates   Z = Custom Newfs");
828     mvprintw(20, 0, "T = Toggle Newfs  U = Undo     A = Auto Defaults        R = Delete+Merge");
829     mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
830     move(0, 0);
831 }
832
833 static void
834 clear_wins(void)
835 {
836     clear();
837     print_label_chunks();
838 }
839
840 static int
841 diskLabel(Device *dev)
842 {
843     daddr_t sz;
844     int  key = 0;
845     Boolean labeling;
846     char *msg = NULL;
847     PartInfo *p, *oldp;
848     PartType type;
849     Device **devs;
850     WINDOW *w = savescr();
851
852     label_focus = 0;
853     pslice_focus = 0;
854     here = 0;
855
856     devs = deviceFind(NULL, DEVICE_TYPE_DISK);
857     if (!devs) {
858         msgConfirm("No disks found!");
859         restorescr(w);
860         return DITEM_FAILURE;
861     }
862     labeling = TRUE;
863     keypad(stdscr, TRUE);
864     record_label_chunks(devs, dev);
865
866     clear();
867     while (labeling) {
868         char *cp;
869         int rflags = DELCHUNK_NORMAL;
870
871         print_label_chunks();
872         print_command_summary();
873         if (msg) {
874             attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
875             clrtoeol();
876             beep();
877             msg = NULL;
878         }
879         else {
880             move(23, 0);
881             clrtoeol();
882         }
883
884         refresh();
885         key = getch();
886         switch (toupper(key)) {
887             int i;
888             static char _msg[40];
889
890         case '\014':    /* ^L */
891             clear_wins();
892             break;
893
894         case '\020':    /* ^P */
895         case KEY_UP:
896         case '-':
897             if (here != 0)
898                 --here;
899             else
900                 while (label_chunk_info[here + 1].c)
901                     ++here;
902             break;
903
904         case '\016':    /* ^N */
905         case KEY_DOWN:
906         case '+':
907         case '\r':
908         case '\n':
909             if (label_chunk_info[here + 1].c)
910                 ++here;
911             else
912                 here = 0;
913             break;
914
915         case KEY_HOME:
916             here = 0;
917             break;
918
919         case KEY_END:
920             while (label_chunk_info[here + 1].c)
921                 ++here;
922             break;
923
924         case KEY_F(1):
925         case '?':
926             systemDisplayHelp("partition");
927             clear_wins();
928             break;
929
930         case '1':
931             if (label_chunk_info[here].type == PART_FILESYSTEM) {
932                 PartInfo *pi =
933                     ((PartInfo *)label_chunk_info[here].c->private_data);
934
935                 if ((pi != NULL) &&
936                     (pi->newfs_type == NEWFS_UFS)) {
937                         pi->newfs_data.newfs_ufs.ufs1 = true;
938                 } else
939                     msg = MSG_NOT_APPLICABLE;
940             } else
941                 msg = MSG_NOT_APPLICABLE;
942             break;
943                 break;
944
945         case '2':
946             if (label_chunk_info[here].type == PART_FILESYSTEM) {
947                 PartInfo *pi =
948                     ((PartInfo *)label_chunk_info[here].c->private_data);
949
950                 if ((pi != NULL) &&
951                     (pi->newfs_type == NEWFS_UFS)) {
952                         pi->newfs_data.newfs_ufs.ufs1 = false;
953                 } else
954                     msg = MSG_NOT_APPLICABLE;
955             } else
956                 msg = MSG_NOT_APPLICABLE;
957             break;
958                 break;
959
960         case 'A':
961             if (label_chunk_info[here].type != PART_SLICE) {
962                 msg = "You can only do this in a disk slice (at top of screen)";
963                 break;
964             }
965             /*
966              * Generate standard partitions automatically.  If we do not
967              * have sufficient space we attempt to scale-down the size
968              * of the partitions within certain bounds.
969              */
970             {
971                 int perc;
972                 int req = 0;
973
974                 for (perc = 100; perc > 0; perc -= 5) {
975                     req = 0;    /* reset for each loop */
976                     if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
977                         break;
978                 }
979                 if (msg) {
980                     if (req) {
981                         msgConfirm(msg);
982                         clear_wins();
983                         msg = NULL;
984                     }
985                 }
986             }
987             break;
988             
989         case 'C':
990             if (label_chunk_info[here].type != PART_SLICE) {
991                 msg = "You can only do this in a master partition (see top of screen)";
992                 break;
993             }
994             sz = space_free(label_chunk_info[here].c);
995             if (sz <= FS_MIN_SIZE) {
996                 msg = "Not enough space to create an additional FreeBSD partition";
997                 break;
998             }
999             else {
1000                 char *val;
1001                 daddr_t size;
1002                 struct chunk *tmp;
1003                 char osize[80];
1004                 u_long flags = 0;
1005
1006 #ifdef __powerpc__
1007                 /* Always use the maximum size for apple partitions */
1008                 if (label_chunk_info[here].c->type == apple)
1009                     size = sz;
1010                 else {
1011 #endif
1012                 sprintf(osize, "%jd", (intmax_t)sz);
1013                 val = msgGetInput(osize,
1014                                   "Please specify the partition size in blocks or append a trailing G for\n"
1015 #ifdef __ia64__
1016                                   "gigabytes, M for megabytes.\n"
1017 #else
1018                                   "gigabytes, M for megabytes, or C for cylinders.\n"
1019 #endif
1020                                   "%jd blocks (%jdMB) are free.",
1021                                   (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1022                 if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
1023                     clear_wins();
1024                     break;
1025                 }
1026
1027                 if (*cp) {
1028                     if (toupper(*cp) == 'M')
1029                         size *= ONE_MEG;
1030                     else if (toupper(*cp) == 'G')
1031                         size *= ONE_GIG;
1032 #ifndef __ia64__
1033                     else if (toupper(*cp) == 'C')
1034                         size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1035 #endif
1036                 }
1037                 if (size <= FS_MIN_SIZE) {
1038                     msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1039                     clear_wins();
1040                     break;
1041                 }
1042 #ifdef __powerpc__
1043                 }
1044 #endif
1045                 type = get_partition_type();
1046                 if (type == PART_NONE) {
1047                     clear_wins();
1048                     beep();
1049                     break;
1050                 }
1051
1052                 if (type == PART_FILESYSTEM || type == PART_EFI) {
1053                     if ((p = get_mountpoint(type, NULL)) == NULL) {
1054                         clear_wins();
1055                         beep();
1056                         break;
1057                     }
1058                     else if (!strcmp(p->mountpoint, "/")) {
1059                         if (type != PART_FILESYSTEM) {
1060                             clear_wins();
1061                             beep();
1062                             break;
1063                         }
1064                         else
1065                             flags |= CHUNK_IS_ROOT;
1066                     }
1067                     else
1068                         flags &= ~CHUNK_IS_ROOT;
1069                 }
1070                 else
1071                     p = NULL;
1072
1073                 if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1074                     msgConfirm("Warning: This is smaller than the recommended size for a\n"
1075                                "root partition.  For a variety of reasons, root\n"
1076                                "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1077                 }
1078                 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1079                     label_chunk_info[here].c, size,
1080 #ifdef __ia64__
1081                     (type == PART_EFI) ? efi : part,
1082                     (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1083 #else
1084                     part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1085 #endif
1086                     flags);
1087                 if (!tmp) {
1088                     msgConfirm("Unable to create the partition. Too big?");
1089                     clear_wins();
1090                     break;
1091                 }
1092
1093                 tmp->private_data = p;
1094                 tmp->private_free = safe_free;
1095                 if (variable_cmp(DISK_LABELLED, "written"))
1096                     variable_set2(DISK_LABELLED, "yes", 0);
1097                 record_label_chunks(devs, dev);
1098                 clear_wins();
1099                 /* This is where we assign focus to new label so it shows. */
1100                 {
1101                     int i;
1102                     label_focus = -1;
1103                     for (i = 0; label_chunk_info[i].c; ++i) {
1104                         if (label_chunk_info[i].c == tmp) {
1105                             label_focus = i;
1106                             break;
1107                         }
1108                     }
1109                     if (label_focus == -1)
1110                         label_focus = i - 1;
1111                 }
1112             }
1113             break;
1114
1115         case KEY_DC:
1116         case 'R':       /* recover space (delete w/ recover) */
1117             /*
1118              * Delete the partition w/ space recovery.
1119              */
1120             rflags = DELCHUNK_RECOVER;
1121             /* fall through */
1122         case 'D':       /* delete */
1123             if (label_chunk_info[here].type == PART_SLICE) {
1124                 msg = MSG_NOT_APPLICABLE;
1125                 break;
1126             }
1127             else if (label_chunk_info[here].type == PART_FAT) {
1128                 msg = "Use the Disk Partition Editor to delete DOS partitions";
1129                 break;
1130             }
1131             Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1132             if (variable_cmp(DISK_LABELLED, "written"))
1133                 variable_set2(DISK_LABELLED, "yes", 0);
1134             record_label_chunks(devs, dev);
1135             break;
1136
1137         case 'M':       /* mount */
1138             switch(label_chunk_info[here].type) {
1139             case PART_SLICE:
1140                 msg = MSG_NOT_APPLICABLE;
1141                 break;
1142
1143             case PART_SWAP:
1144                 msg = "You don't need to specify a mountpoint for a swap partition.";
1145                 break;
1146
1147             case PART_FAT:
1148             case PART_EFI:
1149             case PART_FILESYSTEM:
1150                 oldp = label_chunk_info[here].c->private_data;
1151                 p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1152                 if (p) {
1153                     if (!oldp)
1154                         p->do_newfs = FALSE;
1155                     if ((label_chunk_info[here].type == PART_FAT ||
1156                             label_chunk_info[here].type == PART_EFI) &&
1157                         (!strcmp(p->mountpoint, "/") ||
1158                             !strcmp(p->mountpoint, "/usr") ||
1159                             !strcmp(p->mountpoint, "/var"))) {
1160                         msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1161                         strcpy(p->mountpoint, "/bogus");
1162                     }
1163                 }
1164                 if (variable_cmp(DISK_LABELLED, "written"))
1165                     variable_set2(DISK_LABELLED, "yes", 0);
1166                 record_label_chunks(devs, dev);
1167                 clear_wins();
1168                 break;
1169
1170             default:
1171                 msgFatal("Bogus partition under cursor???");
1172                 break;
1173             }
1174             break;
1175
1176         case 'N':       /* Set newfs options */
1177             if (label_chunk_info[here].c->private_data &&
1178                 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1179                 getNewfsOptionalArguments(
1180                     label_chunk_info[here].c->private_data);
1181             else
1182                 msg = MSG_NOT_APPLICABLE;
1183             clear_wins();
1184             break;
1185
1186         case 'S':       /* Toggle soft updates flag */
1187             if (label_chunk_info[here].type == PART_FILESYSTEM) {
1188                 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1189                 if (pi != NULL &&
1190                     pi->newfs_type == NEWFS_UFS)
1191                         pi->newfs_data.newfs_ufs.softupdates =
1192                             !pi->newfs_data.newfs_ufs.softupdates;
1193                 else
1194                     msg = MSG_NOT_APPLICABLE;
1195             }
1196             else
1197                 msg = MSG_NOT_APPLICABLE;
1198             break;
1199
1200         case 'T':       /* Toggle newfs state */
1201             if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1202                  label_chunk_info[here].type == PART_EFI) &&
1203                 (label_chunk_info[here].c->private_data)) {
1204                 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1205                 if (!pi->do_newfs)
1206                     label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1207                 else
1208                     label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1209
1210                 label_chunk_info[here].c->private_data =
1211                     new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1212                     : TRUE);
1213                 if (pi != NULL &&
1214                     pi->newfs_type == NEWFS_UFS) {
1215                     PartInfo *pi_new = label_chunk_info[here].c->private_data;
1216
1217                     pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1218                 }
1219                 safe_free(pi);
1220                 label_chunk_info[here].c->private_free = safe_free;
1221                 if (variable_cmp(DISK_LABELLED, "written"))
1222                     variable_set2(DISK_LABELLED, "yes", 0);
1223             }
1224             else
1225                 msg = MSG_NOT_APPLICABLE;
1226             break;
1227
1228         case 'U':
1229             clear();
1230             if (!variable_cmp(DISK_LABELLED, "written")) {
1231                 msgConfirm("You've already written out your changes -\n"
1232                            "it's too late to undo!");
1233             }
1234             else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1235                 variable_unset(DISK_PARTITIONED);
1236                 variable_unset(DISK_LABELLED);
1237                 for (i = 0; devs[i]; i++) {
1238                     Disk *d;
1239
1240                     if (!devs[i]->enabled)
1241                         continue;
1242                     else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1243                         Free_Disk(devs[i]->private);
1244                         devs[i]->private = d;
1245 #ifdef WITH_SLICES
1246                         diskPartition(devs[i]);
1247 #endif
1248                     }
1249                 }
1250                 record_label_chunks(devs, dev);
1251             }
1252             clear_wins();
1253             break;
1254
1255         case 'W':
1256             if (!variable_cmp(DISK_LABELLED, "written")) {
1257                 msgConfirm("You've already written out your changes - if you\n"
1258                            "wish to overwrite them, you'll have to restart\n"
1259                            "%s first.", ProgName);
1260             }
1261             else if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
1262                           "installation.  If you are installing FreeBSD for the first time\n"
1263                           "then you should simply type Q when you're finished here and your\n"
1264                           "changes will be committed in one batch automatically at the end of\n"
1265                           "these questions.\n\n"
1266                           "Are you absolutely sure you want to do this now?")) {
1267                 variable_set2(DISK_LABELLED, "yes", 0);
1268                 diskLabelCommit(NULL);
1269             }
1270             clear_wins();
1271             break;
1272
1273         case 'Z':       /* Set newfs command line */
1274             if (label_chunk_info[here].c->private_data &&
1275                 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1276                 getNewfsCmd(label_chunk_info[here].c->private_data);
1277             else
1278                 msg = MSG_NOT_APPLICABLE;
1279             clear_wins();
1280             break;
1281
1282 #ifndef __ia64__
1283         case '|':
1284             if (!msgNoYes("Are you sure you want to go into Expert mode?\n\n"
1285                           "This is an entirely undocumented feature which you are not\n"
1286                           "expected to understand!")) {
1287                 int i;
1288                 Device **devs;
1289
1290                 dialog_clear();
1291                 end_dialog();
1292                 DialogActive = FALSE;
1293                 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1294                 if (!devs) {
1295                     msgConfirm("Can't find any disk devices!");
1296                     break;
1297                 }
1298                 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1299                     if (devs[i]->enabled)
1300                         slice_wizard(((Disk *)devs[i]->private));
1301                 }
1302                 if (variable_cmp(DISK_LABELLED, "written"))
1303                     variable_set2(DISK_LABELLED, "yes", 0);
1304                 DialogActive = TRUE;
1305                 record_label_chunks(devs, dev);
1306                 clear_wins();
1307             }
1308             else
1309                 msg = "A most prudent choice!";
1310             break;
1311 #endif
1312
1313         case '\033':    /* ESC */
1314         case 'Q':
1315             labeling = FALSE;
1316             break;
1317
1318         default:
1319             beep();
1320             sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1321             msg = _msg;
1322             break;
1323         }
1324         if (label_chunk_info[here].type == PART_SLICE)
1325             pslice_focus = here;
1326         else
1327             label_focus = here;
1328     }
1329     restorescr(w);
1330     return DITEM_SUCCESS;
1331 }
1332
1333 static __inline daddr_t
1334 requested_part_size(char *varName, daddr_t nom, int def, int perc)
1335 {
1336     char *cp;
1337     daddr_t sz;
1338
1339     if ((cp = variable_get(varName)) != NULL)
1340         sz = strtoimax(cp, NULL, 0);
1341     else
1342         sz = nom + (def - nom) * perc / 100;
1343     return(sz * ONE_MEG);
1344 }
1345
1346 /*
1347  * Attempt to auto-label the disk.  'perc' (0-100) scales
1348  * the size of the various partitions within appropriate
1349  * bounds (NOMINAL through DEFAULT sizes).  The procedure
1350  * succeeds of NULL is returned.  A non-null return message
1351  * is either a failure-status message (*req == 0), or 
1352  * a confirmation requestor (*req == 1).  *req is 0 on
1353  * entry to this call.
1354  *
1355  * As a special exception to the usual sizing rules, /var is given
1356  * additional space equal to the amount of physical memory present
1357  * if perc == 100 in order to ensure that users with large hard drives
1358  * will have enough space to store a crashdump in /var/crash.
1359  *
1360  * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1361  * and /home.  /home receives any extra left over disk space. 
1362  */
1363 static char *
1364 try_auto_label(Device **devs, Device *dev, int perc, int *req)
1365 {
1366     daddr_t sz;
1367     Chunk *AutoHome, *AutoRoot, *AutoSwap;
1368     Chunk *AutoTmp, *AutoUsr, *AutoVar;
1369 #ifdef __ia64__
1370     Chunk *AutoEfi;
1371 #endif
1372     int mib[2];
1373     unsigned long physmem;
1374     size_t size;
1375     char *msg = NULL;
1376
1377     sz = space_free(label_chunk_info[here].c);
1378     if (sz <= FS_MIN_SIZE)
1379         return("Not enough free space to create a new partition in the slice");
1380
1381     (void)checkLabels(FALSE);
1382     AutoHome = AutoRoot = AutoSwap = NULL;
1383     AutoTmp = AutoUsr = AutoVar = NULL;
1384
1385 #ifdef __ia64__
1386     AutoEfi = NULL;
1387     if (EfiChunk == NULL) {
1388         sz = 400 * ONE_MEG;
1389         AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1390             label_chunk_info[here].c, sz, efi, 0, 0);
1391         if (AutoEfi == NULL) {
1392             *req = 1;
1393             msg = "Unable to create the EFI system partition. Too big?";
1394             goto done;
1395         }
1396         AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1397         AutoEfi->private_free = safe_free;
1398         AutoEfi->flags |= CHUNK_NEWFS;
1399         record_label_chunks(devs, dev);
1400     }
1401 #endif
1402
1403     if (RootChunk == NULL) {
1404         sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1405
1406         AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1407                             label_chunk_info[here].c, sz, part,
1408                             FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1409         if (!AutoRoot) {
1410             *req = 1;
1411             msg = "Unable to create the root partition. Too big?";
1412             goto done;
1413         }
1414         AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1415         AutoRoot->private_free = safe_free;
1416         AutoRoot->flags |= CHUNK_NEWFS;
1417         record_label_chunks(devs, dev);
1418     }
1419     if (SwapChunk == NULL) {
1420         sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1421         if (sz == 0) {
1422             daddr_t nom;
1423             daddr_t def;
1424
1425             mib[0] = CTL_HW;
1426             mib[1] = HW_PHYSMEM;
1427             size = sizeof physmem;
1428             sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1429             def = 2 * (int)(physmem / 512);
1430             if (def < SWAP_MIN_SIZE * ONE_MEG)
1431                 def = SWAP_MIN_SIZE * ONE_MEG;
1432             if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1433                 def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1434             nom = (int)(physmem / 512) / 8;
1435             sz = nom + (def - nom) * perc / 100;
1436         }
1437         AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1438                             label_chunk_info[here].c, sz, part,
1439                             FS_SWAP, CHUNK_AUTO_SIZE);
1440         if (!AutoSwap) {
1441             *req = 1;
1442             msg = "Unable to create the swap partition. Too big?";
1443             goto done;
1444         }
1445         AutoSwap->private_data = 0;
1446         AutoSwap->private_free = safe_free;
1447         record_label_chunks(devs, dev);
1448     }
1449     if (VarChunk == NULL) {
1450         /* Work out how much extra space we want for a crash dump */
1451         unsigned long crashdumpsz;
1452
1453         mib[0] = CTL_HW;
1454         mib[1] = HW_PHYSMEM;
1455         size = sizeof(physmem);
1456         sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1457
1458         if (perc == 100)
1459                 crashdumpsz = physmem / 1048576;
1460         else
1461                 crashdumpsz = 0;
1462
1463         sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE,        \
1464             VAR_DEFAULT_SIZE + crashdumpsz, perc);
1465
1466         AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1467                                 label_chunk_info[here].c, sz, part,
1468                                 FS_BSDFFS, CHUNK_AUTO_SIZE);
1469         if (!AutoVar) {
1470             *req = 1;
1471             msg = "Not enough free space for /var - you will need to\n"
1472                    "partition your disk manually with a custom install!";
1473             goto done;
1474         }
1475         AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1476         AutoVar->private_free = safe_free;
1477         AutoVar->flags |= CHUNK_NEWFS;
1478         record_label_chunks(devs, dev);
1479     }
1480     if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1481         sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1482
1483         AutoTmp = 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 (!AutoTmp) {
1487             *req = 1;
1488             msg = "Not enough free space for /tmp - you will need to\n"
1489                    "partition your disk manually with a custom install!";
1490             goto done;
1491         }
1492         AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1493         AutoTmp->private_free = safe_free;
1494         AutoTmp->flags |= CHUNK_NEWFS;
1495         record_label_chunks(devs, dev);
1496     }
1497     if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1498         sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1499 #if AUTO_HOME == 0
1500         if (sz < space_free(label_chunk_info[here].c))
1501             sz = space_free(label_chunk_info[here].c);
1502 #endif
1503         if (sz) {
1504             if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1505                 *req = 1;
1506                 msg = "Not enough free space for /usr - you will need to\n"
1507                        "partition your disk manually with a custom install!";
1508             }
1509
1510             AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1511                                     label_chunk_info[here].c, sz, part,
1512                                     FS_BSDFFS, CHUNK_AUTO_SIZE);
1513             if (!AutoUsr) {
1514                 msg = "Unable to create the /usr partition.  Not enough space?\n"
1515                            "You will need to partition your disk manually with a custom install!";
1516                 goto done;
1517             }
1518             AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1519             AutoUsr->private_free = safe_free;
1520             AutoUsr->flags |= CHUNK_NEWFS;
1521             record_label_chunks(devs, dev);
1522         }
1523     }
1524 #if AUTO_HOME == 1
1525     if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1526         sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1527         if (sz < space_free(label_chunk_info[here].c))
1528             sz = space_free(label_chunk_info[here].c);
1529         if (sz) {
1530             if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1531                 *req = 1;
1532                 msg = "Not enough free space for /home - you will need to\n"
1533                        "partition your disk manually with a custom install!";
1534                 goto done;
1535             }
1536
1537             AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1538                                     label_chunk_info[here].c, sz, part,
1539                                     FS_BSDFFS, CHUNK_AUTO_SIZE);
1540             if (!AutoHome) {
1541                 msg = "Unable to create the /home partition.  Not enough space?\n"
1542                            "You will need to partition your disk manually with a custom install!";
1543                 goto done;
1544             }
1545             AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1546             AutoHome->private_free = safe_free;
1547             AutoHome->flags |= CHUNK_NEWFS;
1548             record_label_chunks(devs, dev);
1549         }
1550     }
1551 #endif
1552
1553     /* At this point, we're reasonably "labelled" */
1554     if (variable_cmp(DISK_LABELLED, "written"))
1555         variable_set2(DISK_LABELLED, "yes", 0);
1556
1557 done:
1558     if (msg) {
1559         if (AutoRoot != NULL)
1560             Delete_Chunk(AutoRoot->disk, AutoRoot);
1561         if (AutoSwap != NULL)
1562             Delete_Chunk(AutoSwap->disk, AutoSwap);
1563         if (AutoVar != NULL)
1564             Delete_Chunk(AutoVar->disk, AutoVar);
1565         if (AutoTmp != NULL)
1566             Delete_Chunk(AutoTmp->disk, AutoTmp);
1567         if (AutoUsr != NULL)
1568             Delete_Chunk(AutoUsr->disk, AutoUsr);
1569         if (AutoHome != NULL)
1570             Delete_Chunk(AutoHome->disk, AutoHome);
1571         record_label_chunks(devs, dev);
1572     }
1573     return(msg);
1574 }
1575
1576 static int
1577 diskLabelNonInteractive(Device *dev)
1578 {
1579     char *cp;
1580     PartType type;
1581     PartInfo *p;
1582     u_long flags;
1583     int i, status;
1584     Device **devs;
1585     Disk *d;
1586     
1587     status = DITEM_SUCCESS;
1588     cp = variable_get(VAR_DISK);
1589     if (!cp) {
1590         msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1591         return DITEM_FAILURE;
1592     }
1593     devs = deviceFind(cp, DEVICE_TYPE_DISK);
1594     if (!devs) {
1595         msgConfirm("diskLabel: No disk device %s found!", cp);
1596         return DITEM_FAILURE;
1597     }
1598     if (dev)
1599         d = dev->private;
1600     else
1601         d = devs[0]->private;
1602     record_label_chunks(devs, dev);
1603     for (i = 0; label_chunk_info[i].c; i++) {
1604         Chunk *c1 = label_chunk_info[i].c;
1605
1606         if (label_chunk_info[i].type == PART_SLICE) {
1607             char name[512];
1608             char typ[10], mpoint[50];
1609             int entries;
1610
1611             for (entries = 1;; entries++) {
1612                 intmax_t sz;
1613                 int soft = 0;
1614                 snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1615                 if ((cp = variable_get(name)) == NULL)
1616                     break;
1617                 if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1618                     msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1619                     status = DITEM_FAILURE;
1620                     break;
1621                 } else {
1622                     Chunk *tmp;
1623
1624                     flags = 0;
1625                     if (!strcmp(typ, "swap")) {
1626                         type = PART_SWAP;
1627                         strcpy(mpoint, "SWAP");
1628                     } else {
1629                         type = PART_FILESYSTEM;
1630                         if (!strcmp(mpoint, "/"))
1631                             flags |= CHUNK_IS_ROOT;
1632                     }
1633                     if (!sz)
1634                         sz = space_free(c1);
1635                     if (sz > space_free(c1)) {
1636                         msgConfirm("Not enough free space to create partition: %s", mpoint);
1637                         status = DITEM_FAILURE;
1638                         break;
1639                     }
1640                     if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1641                         (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1642                         msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1643                         status = DITEM_FAILURE;
1644                         break;
1645                     } else {
1646                         PartInfo *pi;
1647                         pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1648                         tmp->private_free = safe_free;
1649                         pi->newfs_data.newfs_ufs.softupdates = soft;
1650                     }
1651                 }
1652             }
1653         } else {
1654             /* Must be something we can set a mountpoint for */
1655             cp = variable_get(c1->name);
1656             if (cp) {
1657                 char mpoint[50], do_newfs[8];
1658                 Boolean newfs = FALSE;
1659
1660                 do_newfs[0] = '\0';
1661                 if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1662                     msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1663                     status = DITEM_FAILURE;
1664                     break;
1665                 }
1666                 newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1667                 if (c1->private_data) {
1668                     p = c1->private_data;
1669                     p->do_newfs = newfs;
1670                     strcpy(p->mountpoint, mpoint);
1671                 }
1672                 else {
1673                     c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1674                     c1->private_free = safe_free;
1675                 }
1676                 if (!strcmp(mpoint, "/"))
1677                     c1->flags |= CHUNK_IS_ROOT;
1678                 else
1679                     c1->flags &= ~CHUNK_IS_ROOT;
1680             }
1681         }
1682     }
1683     if (status == DITEM_SUCCESS)
1684         variable_set2(DISK_LABELLED, "yes", 0);
1685     return status;
1686 }