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