]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/sysinstall/label.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.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(__ia64__) || defined(__sparc64__) || defined(__amd64__)
65 #define ROOT_MIN_SIZE                   280
66 #else
67 #define ROOT_MIN_SIZE                   180
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               1024
86 #define USR_DEFAULT_SIZE                8192
87 #define VAR_DEFAULT_SIZE                4096
88 #define TMP_DEFAULT_SIZE                1024
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               512
97 #define USR_NOMINAL_SIZE                1536
98 #define VAR_NOMINAL_SIZE                512
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("%s", 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                 long double dsize;
1003                 struct chunk *tmp;
1004                 char osize[80];
1005                 u_long flags = 0;
1006
1007 #ifdef __powerpc__
1008                 /* Always use the maximum size for apple partitions */
1009                 if (label_chunk_info[here].c->type == apple)
1010                     size = sz;
1011                 else {
1012 #endif
1013                 sprintf(osize, "%jd", (intmax_t)sz);
1014                 val = msgGetInput(osize,
1015                                   "Please specify the partition size in blocks or append a trailing G for\n"
1016 #ifdef __ia64__
1017                                   "gigabytes, M for megabytes.\n"
1018 #else
1019                                   "gigabytes, M for megabytes, or C for cylinders.\n"
1020 #endif
1021                                   "%jd blocks (%jdMB) are free.",
1022                                   (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1023                 if (!val || (dsize = strtold(val, &cp)) <= 0) {
1024                     clear_wins();
1025                     break;
1026                 }
1027
1028                 if (*cp) {
1029                     if (toupper(*cp) == 'M')
1030                         size = (daddr_t) (dsize * ONE_MEG);
1031                     else if (toupper(*cp) == 'G')
1032                         size = (daddr_t) (dsize * ONE_GIG);
1033 #ifndef __ia64__
1034                     else if (toupper(*cp) == 'C')
1035                         size = (daddr_t) dsize * (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1036 #endif
1037                     else
1038                         size = (daddr_t) dsize;
1039                 } else {
1040                         size = (daddr_t) dsize;
1041                 }
1042
1043                 if (size < FS_MIN_SIZE) {
1044                     msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1045                     clear_wins();
1046                     break;
1047                 }
1048 #ifdef __powerpc__
1049                 }
1050 #endif
1051                 type = get_partition_type();
1052                 if (type == PART_NONE) {
1053                     clear_wins();
1054                     beep();
1055                     break;
1056                 }
1057
1058                 if (type == PART_FILESYSTEM || type == PART_EFI) {
1059                     if ((p = get_mountpoint(type, NULL)) == NULL) {
1060                         clear_wins();
1061                         beep();
1062                         break;
1063                     }
1064                     else if (!strcmp(p->mountpoint, "/")) {
1065                         if (type != PART_FILESYSTEM) {
1066                             clear_wins();
1067                             beep();
1068                             break;
1069                         }
1070                         else
1071                             flags |= CHUNK_IS_ROOT;
1072                     }
1073                     else
1074                         flags &= ~CHUNK_IS_ROOT;
1075                 }
1076                 else
1077                     p = NULL;
1078
1079                 if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1080                     msgConfirm("Warning: This is smaller than the recommended size for a\n"
1081                                "root partition.  For a variety of reasons, root\n"
1082                                "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1083                 }
1084                 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1085                     label_chunk_info[here].c, size,
1086 #ifdef __ia64__
1087                     (type == PART_EFI) ? efi : part,
1088                     (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1089 #else
1090                     part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1091 #endif
1092                     flags);
1093                 if (!tmp) {
1094                     msgConfirm("Unable to create the partition. Too big?");
1095                     clear_wins();
1096                     break;
1097                 }
1098
1099                 tmp->private_data = p;
1100                 tmp->private_free = safe_free;
1101                 if (variable_cmp(DISK_LABELLED, "written"))
1102                     variable_set2(DISK_LABELLED, "yes", 0);
1103                 record_label_chunks(devs, dev);
1104                 clear_wins();
1105                 /* This is where we assign focus to new label so it shows. */
1106                 {
1107                     int i;
1108                     label_focus = -1;
1109                     for (i = 0; label_chunk_info[i].c; ++i) {
1110                         if (label_chunk_info[i].c == tmp) {
1111                             label_focus = i;
1112                             break;
1113                         }
1114                     }
1115                     if (label_focus == -1)
1116                         label_focus = i - 1;
1117                 }
1118             }
1119             break;
1120
1121         case KEY_DC:
1122         case 'R':       /* recover space (delete w/ recover) */
1123             /*
1124              * Delete the partition w/ space recovery.
1125              */
1126             rflags = DELCHUNK_RECOVER;
1127             /* fall through */
1128         case 'D':       /* delete */
1129             if (label_chunk_info[here].type == PART_SLICE) {
1130                 msg = MSG_NOT_APPLICABLE;
1131                 break;
1132             }
1133             else if (label_chunk_info[here].type == PART_FAT) {
1134                 msg = "Use the Disk Partition Editor to delete DOS partitions";
1135                 break;
1136             }
1137             Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1138             if (variable_cmp(DISK_LABELLED, "written"))
1139                 variable_set2(DISK_LABELLED, "yes", 0);
1140             record_label_chunks(devs, dev);
1141             break;
1142
1143         case 'M':       /* mount */
1144             switch(label_chunk_info[here].type) {
1145             case PART_SLICE:
1146                 msg = MSG_NOT_APPLICABLE;
1147                 break;
1148
1149             case PART_SWAP:
1150                 msg = "You don't need to specify a mountpoint for a swap partition.";
1151                 break;
1152
1153             case PART_FAT:
1154             case PART_EFI:
1155             case PART_FILESYSTEM:
1156                 oldp = label_chunk_info[here].c->private_data;
1157                 p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1158                 if (p) {
1159                     if (!oldp)
1160                         p->do_newfs = FALSE;
1161                     if ((label_chunk_info[here].type == PART_FAT ||
1162                             label_chunk_info[here].type == PART_EFI) &&
1163                         (!strcmp(p->mountpoint, "/") ||
1164                             !strcmp(p->mountpoint, "/usr") ||
1165                             !strcmp(p->mountpoint, "/var"))) {
1166                         msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1167                         strcpy(p->mountpoint, "/bogus");
1168                     }
1169                 }
1170                 if (variable_cmp(DISK_LABELLED, "written"))
1171                     variable_set2(DISK_LABELLED, "yes", 0);
1172                 record_label_chunks(devs, dev);
1173                 clear_wins();
1174                 break;
1175
1176             default:
1177                 msgFatal("Bogus partition under cursor???");
1178                 break;
1179             }
1180             break;
1181
1182         case 'N':       /* Set newfs options */
1183             if (label_chunk_info[here].c->private_data &&
1184                 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1185                 getNewfsOptionalArguments(
1186                     label_chunk_info[here].c->private_data);
1187             else
1188                 msg = MSG_NOT_APPLICABLE;
1189             clear_wins();
1190             break;
1191
1192         case 'S':       /* Toggle soft updates flag */
1193             if (label_chunk_info[here].type == PART_FILESYSTEM) {
1194                 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1195                 if (pi != NULL &&
1196                     pi->newfs_type == NEWFS_UFS)
1197                         pi->newfs_data.newfs_ufs.softupdates =
1198                             !pi->newfs_data.newfs_ufs.softupdates;
1199                 else
1200                     msg = MSG_NOT_APPLICABLE;
1201             }
1202             else
1203                 msg = MSG_NOT_APPLICABLE;
1204             break;
1205
1206         case 'T':       /* Toggle newfs state */
1207             if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1208                  label_chunk_info[here].type == PART_EFI) &&
1209                 (label_chunk_info[here].c->private_data)) {
1210                 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1211                 if (!pi->do_newfs)
1212                     label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1213                 else
1214                     label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1215
1216                 label_chunk_info[here].c->private_data =
1217                     new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1218                     : TRUE);
1219                 if (pi != NULL &&
1220                     pi->newfs_type == NEWFS_UFS) {
1221                     PartInfo *pi_new = label_chunk_info[here].c->private_data;
1222
1223                     pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1224                 }
1225                 safe_free(pi);
1226                 label_chunk_info[here].c->private_free = safe_free;
1227                 if (variable_cmp(DISK_LABELLED, "written"))
1228                     variable_set2(DISK_LABELLED, "yes", 0);
1229             }
1230             else
1231                 msg = MSG_NOT_APPLICABLE;
1232             break;
1233
1234         case 'U':
1235             clear();
1236             if (!variable_cmp(DISK_LABELLED, "written")) {
1237                 msgConfirm("You've already written out your changes -\n"
1238                            "it's too late to undo!");
1239             }
1240             else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1241                 variable_unset(DISK_PARTITIONED);
1242                 variable_unset(DISK_LABELLED);
1243                 for (i = 0; devs[i]; i++) {
1244                     Disk *d;
1245
1246                     if (!devs[i]->enabled)
1247                         continue;
1248                     else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1249                         Free_Disk(devs[i]->private);
1250                         devs[i]->private = d;
1251 #ifdef WITH_SLICES
1252                         diskPartition(devs[i]);
1253 #endif
1254                     }
1255                 }
1256                 record_label_chunks(devs, dev);
1257             }
1258             clear_wins();
1259             break;
1260
1261         case 'W':
1262             if (!variable_cmp(DISK_LABELLED, "written")) {
1263                 msgConfirm("You've already written out your changes - if you\n"
1264                            "wish to overwrite them, you'll have to restart\n"
1265                            "%s first.", ProgName);
1266             }
1267             else if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
1268                           "installation.  If you are installing FreeBSD for the first time\n"
1269                           "then you should simply type Q when you're finished here and your\n"
1270                           "changes will be committed in one batch automatically at the end of\n"
1271                           "these questions.\n\n"
1272                           "Are you absolutely sure you want to do this now?")) {
1273                 variable_set2(DISK_LABELLED, "yes", 0);
1274                 diskLabelCommit(NULL);
1275             }
1276             clear_wins();
1277             break;
1278
1279         case 'Z':       /* Set newfs command line */
1280             if (label_chunk_info[here].c->private_data &&
1281                 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1282                 getNewfsCmd(label_chunk_info[here].c->private_data);
1283             else
1284                 msg = MSG_NOT_APPLICABLE;
1285             clear_wins();
1286             break;
1287
1288 #ifndef __ia64__
1289         case '|':
1290             if (!msgNoYes("Are you sure you want to go into Expert mode?\n\n"
1291                           "This is an entirely undocumented feature which you are not\n"
1292                           "expected to understand!")) {
1293                 int i;
1294                 Device **devs;
1295
1296                 dialog_clear();
1297                 end_dialog();
1298                 DialogActive = FALSE;
1299                 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1300                 if (!devs) {
1301                     msgConfirm("Can't find any disk devices!");
1302                     break;
1303                 }
1304                 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1305                     if (devs[i]->enabled)
1306                         slice_wizard(((Disk *)devs[i]->private));
1307                 }
1308                 if (variable_cmp(DISK_LABELLED, "written"))
1309                     variable_set2(DISK_LABELLED, "yes", 0);
1310                 DialogActive = TRUE;
1311                 record_label_chunks(devs, dev);
1312                 clear_wins();
1313             }
1314             else
1315                 msg = "A most prudent choice!";
1316             break;
1317 #endif
1318
1319         case '\033':    /* ESC */
1320         case 'Q':
1321             labeling = FALSE;
1322             break;
1323
1324         default:
1325             beep();
1326             sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1327             msg = _msg;
1328             break;
1329         }
1330         if (label_chunk_info[here].type == PART_SLICE)
1331             pslice_focus = here;
1332         else
1333             label_focus = here;
1334     }
1335     restorescr(w);
1336     return DITEM_SUCCESS;
1337 }
1338
1339 static __inline daddr_t
1340 requested_part_size(char *varName, daddr_t nom, int def, int perc)
1341 {
1342     char *cp;
1343     daddr_t sz;
1344
1345     if ((cp = variable_get(varName)) != NULL)
1346         sz = strtoimax(cp, NULL, 0);
1347     else
1348         sz = nom + (def - nom) * perc / 100;
1349     return(sz * ONE_MEG);
1350 }
1351
1352 /*
1353  * Attempt to auto-label the disk.  'perc' (0-100) scales
1354  * the size of the various partitions within appropriate
1355  * bounds (NOMINAL through DEFAULT sizes).  The procedure
1356  * succeeds of NULL is returned.  A non-null return message
1357  * is either a failure-status message (*req == 0), or 
1358  * a confirmation requestor (*req == 1).  *req is 0 on
1359  * entry to this call.
1360  *
1361  * As a special exception to the usual sizing rules, /var is given
1362  * additional space equal to the amount of physical memory present
1363  * if perc == 100 in order to ensure that users with large hard drives
1364  * will have enough space to store a crashdump in /var/crash.
1365  *
1366  * We autolabel the following partitions:  /, swap, /var, /tmp, /usr,
1367  * and /home.  /home receives any extra left over disk space. 
1368  */
1369 static char *
1370 try_auto_label(Device **devs, Device *dev, int perc, int *req)
1371 {
1372     daddr_t sz;
1373     Chunk *AutoHome, *AutoRoot, *AutoSwap;
1374     Chunk *AutoTmp, *AutoUsr, *AutoVar;
1375 #ifdef __ia64__
1376     Chunk *AutoEfi;
1377 #endif
1378     int mib[2];
1379     unsigned long physmem;
1380     size_t size;
1381     char *msg = NULL;
1382
1383     sz = space_free(label_chunk_info[here].c);
1384     if (sz <= FS_MIN_SIZE)
1385         return("Not enough free space to create a new partition in the slice");
1386
1387     (void)checkLabels(FALSE);
1388     AutoHome = AutoRoot = AutoSwap = NULL;
1389     AutoTmp = AutoUsr = AutoVar = NULL;
1390
1391 #ifdef __ia64__
1392     AutoEfi = NULL;
1393     if (EfiChunk == NULL) {
1394         sz = 400 * ONE_MEG;
1395         AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1396             label_chunk_info[here].c, sz, efi, 0, 0);
1397         if (AutoEfi == NULL) {
1398             *req = 1;
1399             msg = "Unable to create the EFI system partition. Too big?";
1400             goto done;
1401         }
1402         AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1403         AutoEfi->private_free = safe_free;
1404         AutoEfi->flags |= CHUNK_NEWFS;
1405         record_label_chunks(devs, dev);
1406     }
1407 #endif
1408
1409     if (RootChunk == NULL) {
1410         sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1411
1412         AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1413                             label_chunk_info[here].c, sz, part,
1414                             FS_BSDFFS,  CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1415         if (!AutoRoot) {
1416             *req = 1;
1417             msg = "Unable to create the root partition. Too big?";
1418             goto done;
1419         }
1420         AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1421         AutoRoot->private_free = safe_free;
1422         AutoRoot->flags |= CHUNK_NEWFS;
1423         record_label_chunks(devs, dev);
1424     }
1425     if (SwapChunk == NULL) {
1426         sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1427         if (sz == 0) {
1428             daddr_t nom;
1429             daddr_t def;
1430
1431             mib[0] = CTL_HW;
1432             mib[1] = HW_PHYSMEM;
1433             size = sizeof physmem;
1434             sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1435             def = 2 * (int)(physmem / 512);
1436             if (def < SWAP_MIN_SIZE * ONE_MEG)
1437                 def = SWAP_MIN_SIZE * ONE_MEG;
1438             if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1439                 def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1440             nom = (int)(physmem / 512) / 8;
1441             sz = nom + (def - nom) * perc / 100;
1442         }
1443         AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1444                             label_chunk_info[here].c, sz, part,
1445                             FS_SWAP, CHUNK_AUTO_SIZE);
1446         if (!AutoSwap) {
1447             *req = 1;
1448             msg = "Unable to create the swap partition. Too big?";
1449             goto done;
1450         }
1451         AutoSwap->private_data = 0;
1452         AutoSwap->private_free = safe_free;
1453         record_label_chunks(devs, dev);
1454     }
1455     if (VarChunk == NULL) {
1456         /* Work out how much extra space we want for a crash dump */
1457         unsigned long crashdumpsz;
1458
1459         mib[0] = CTL_HW;
1460         mib[1] = HW_PHYSMEM;
1461         size = sizeof(physmem);
1462         sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1463
1464         if (perc == 100)
1465                 crashdumpsz = physmem / 1048576;
1466         else
1467                 crashdumpsz = 0;
1468
1469         sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE,        \
1470             VAR_DEFAULT_SIZE + crashdumpsz, perc);
1471
1472         AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1473                                 label_chunk_info[here].c, sz, part,
1474                                 FS_BSDFFS, CHUNK_AUTO_SIZE);
1475         if (!AutoVar) {
1476             *req = 1;
1477             msg = "Not enough free space for /var - you will need to\n"
1478                    "partition your disk manually with a custom install!";
1479             goto done;
1480         }
1481         AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1482         AutoVar->private_free = safe_free;
1483         AutoVar->flags |= CHUNK_NEWFS;
1484         record_label_chunks(devs, dev);
1485     }
1486     if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1487         sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1488
1489         AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, 
1490                                 label_chunk_info[here].c, sz, part,
1491                                 FS_BSDFFS, CHUNK_AUTO_SIZE);
1492         if (!AutoTmp) {
1493             *req = 1;
1494             msg = "Not enough free space for /tmp - you will need to\n"
1495                    "partition your disk manually with a custom install!";
1496             goto done;
1497         }
1498         AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1499         AutoTmp->private_free = safe_free;
1500         AutoTmp->flags |= CHUNK_NEWFS;
1501         record_label_chunks(devs, dev);
1502     }
1503     if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1504         sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1505 #if AUTO_HOME == 0
1506         if (sz < space_free(label_chunk_info[here].c))
1507             sz = space_free(label_chunk_info[here].c);
1508 #endif
1509         if (sz) {
1510             if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1511                 *req = 1;
1512                 msg = "Not enough free space for /usr - you will need to\n"
1513                        "partition your disk manually with a custom install!";
1514             }
1515
1516             AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1517                                     label_chunk_info[here].c, sz, part,
1518                                     FS_BSDFFS, CHUNK_AUTO_SIZE);
1519             if (!AutoUsr) {
1520                 msg = "Unable to create the /usr partition.  Not enough space?\n"
1521                            "You will need to partition your disk manually with a custom install!";
1522                 goto done;
1523             }
1524             AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1525             AutoUsr->private_free = safe_free;
1526             AutoUsr->flags |= CHUNK_NEWFS;
1527             record_label_chunks(devs, dev);
1528         }
1529     }
1530 #if AUTO_HOME == 1
1531     if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1532         sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1533         if (sz < space_free(label_chunk_info[here].c))
1534             sz = space_free(label_chunk_info[here].c);
1535         if (sz) {
1536             if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1537                 *req = 1;
1538                 msg = "Not enough free space for /home - you will need to\n"
1539                        "partition your disk manually with a custom install!";
1540                 goto done;
1541             }
1542
1543             AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1544                                     label_chunk_info[here].c, sz, part,
1545                                     FS_BSDFFS, CHUNK_AUTO_SIZE);
1546             if (!AutoHome) {
1547                 msg = "Unable to create the /home partition.  Not enough space?\n"
1548                            "You will need to partition your disk manually with a custom install!";
1549                 goto done;
1550             }
1551             AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1552             AutoHome->private_free = safe_free;
1553             AutoHome->flags |= CHUNK_NEWFS;
1554             record_label_chunks(devs, dev);
1555         }
1556     }
1557 #endif
1558
1559     /* At this point, we're reasonably "labelled" */
1560     if (variable_cmp(DISK_LABELLED, "written"))
1561         variable_set2(DISK_LABELLED, "yes", 0);
1562
1563 done:
1564     if (msg) {
1565         if (AutoRoot != NULL)
1566             Delete_Chunk(AutoRoot->disk, AutoRoot);
1567         if (AutoSwap != NULL)
1568             Delete_Chunk(AutoSwap->disk, AutoSwap);
1569         if (AutoVar != NULL)
1570             Delete_Chunk(AutoVar->disk, AutoVar);
1571         if (AutoTmp != NULL)
1572             Delete_Chunk(AutoTmp->disk, AutoTmp);
1573         if (AutoUsr != NULL)
1574             Delete_Chunk(AutoUsr->disk, AutoUsr);
1575         if (AutoHome != NULL)
1576             Delete_Chunk(AutoHome->disk, AutoHome);
1577         record_label_chunks(devs, dev);
1578     }
1579     return(msg);
1580 }
1581
1582 static int
1583 diskLabelNonInteractive(Device *dev)
1584 {
1585     char *cp;
1586     PartType type;
1587     PartInfo *p;
1588     u_long flags;
1589     int i, status;
1590     Device **devs;
1591     Disk *d;
1592     
1593     status = DITEM_SUCCESS;
1594     cp = variable_get(VAR_DISK);
1595     if (!cp) {
1596         msgConfirm("diskLabel:  No disk selected - can't label automatically.");
1597         return DITEM_FAILURE;
1598     }
1599     devs = deviceFind(cp, DEVICE_TYPE_DISK);
1600     if (!devs) {
1601         msgConfirm("diskLabel: No disk device %s found!", cp);
1602         return DITEM_FAILURE;
1603     }
1604     if (dev)
1605         d = dev->private;
1606     else
1607         d = devs[0]->private;
1608     record_label_chunks(devs, dev);
1609     for (i = 0; label_chunk_info[i].c; i++) {
1610         Chunk *c1 = label_chunk_info[i].c;
1611
1612         if (label_chunk_info[i].type == PART_SLICE) {
1613             char name[512];
1614             char typ[10], mpoint[50];
1615             int entries;
1616
1617             for (entries = 1;; entries++) {
1618                 intmax_t sz;
1619                 int soft = 0;
1620                 snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1621                 if ((cp = variable_get(name)) == NULL)
1622                     break;
1623                 if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1624                     msgConfirm("For slice entry %s, got an invalid detail entry of: %s",  c1->name, cp);
1625                     status = DITEM_FAILURE;
1626                     break;
1627                 } else {
1628                     Chunk *tmp;
1629
1630                     flags = 0;
1631                     if (!strcmp(typ, "swap")) {
1632                         type = PART_SWAP;
1633                         strcpy(mpoint, "SWAP");
1634                     } else {
1635                         type = PART_FILESYSTEM;
1636                         if (!strcmp(mpoint, "/"))
1637                             flags |= CHUNK_IS_ROOT;
1638                     }
1639                     if (!sz)
1640                         sz = space_free(c1);
1641                     if (sz > space_free(c1)) {
1642                         msgConfirm("Not enough free space to create partition: %s", mpoint);
1643                         status = DITEM_FAILURE;
1644                         break;
1645                     }
1646                     if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1647                         (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1648                         msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1649                         status = DITEM_FAILURE;
1650                         break;
1651                     } else {
1652                         PartInfo *pi;
1653                         pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1654                         tmp->private_free = safe_free;
1655                         pi->newfs_data.newfs_ufs.softupdates = soft;
1656                         if (!strcmp(typ, "ufs1"))
1657                                 pi->newfs_data.newfs_ufs.ufs1 = TRUE;
1658                     }
1659                 }
1660             }
1661         } else {
1662             /* Must be something we can set a mountpoint for */
1663             cp = variable_get(c1->name);
1664             if (cp) {
1665                 char mpoint[50], do_newfs[8];
1666                 Boolean newfs = FALSE;
1667
1668                 do_newfs[0] = '\0';
1669                 if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1670                     msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1671                     status = DITEM_FAILURE;
1672                     break;
1673                 }
1674                 newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1675                 if (c1->private_data) {
1676                     p = c1->private_data;
1677                     p->do_newfs = newfs;
1678                     strcpy(p->mountpoint, mpoint);
1679                 }
1680                 else {
1681                     c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1682                     c1->private_free = safe_free;
1683                 }
1684                 if (!strcmp(mpoint, "/"))
1685                     c1->flags |= CHUNK_IS_ROOT;
1686                 else
1687                     c1->flags &= ~CHUNK_IS_ROOT;
1688             }
1689         }
1690     }
1691     if (status == DITEM_SUCCESS)
1692         variable_set2(DISK_LABELLED, "yes", 0);
1693     return status;
1694 }