]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/sysinstall/disks.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / sysinstall / disks.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 <fcntl.h>
40 #include <inttypes.h>
41 #include <libdisk.h>
42 #include <sys/stat.h>
43 #include <sys/disklabel.h>
44
45 #ifdef WITH_SLICES
46 enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_GIG, UNIT_SIZE };
47
48 #ifdef PC98
49 #define SUBTYPE_FREEBSD         50324
50 #define SUBTYPE_FAT             37218
51 #else
52 #define SUBTYPE_FREEBSD         165
53 #define SUBTYPE_FAT             6
54 #endif
55 #define SUBTYPE_EFI             239
56
57 #ifdef PC98
58 #define OTHER_SLICE_VALUES                                              \
59         "Other popular values are 37218 for a\n"                        \
60         "DOS FAT partition.\n\n"
61 #else
62 #define OTHER_SLICE_VALUES                                              \
63         "Other popular values are 6 for a\n"                            \
64         "DOS FAT partition, 131 for a Linux ext2fs partition, or\n"     \
65         "130 for a Linux swap partition.\n\n"
66 #endif
67 #define NON_FREEBSD_NOTE                                                \
68         "Note:  If you choose a non-FreeBSD partition type, it will not\n" \
69         "be formatted or otherwise prepared, it will simply reserve space\n" \
70         "for you to use another tool, such as DOS format, to later format\n" \
71         "and actually use the partition."
72
73 /* Where we start displaying chunk information on the screen */
74 #define CHUNK_START_ROW         5
75
76 /* Where we keep track of MBR chunks */
77 #define CHUNK_INFO_ENTRIES      16
78 static struct chunk *chunk_info[CHUNK_INFO_ENTRIES];
79 static int current_chunk;
80
81 static void     diskPartitionNonInteractive(Device *dev);
82 static u_char * bootalloc(char *name, size_t *size);
83
84 static void
85 record_chunks(Disk *d)
86 {
87     struct chunk *c1 = NULL;
88     int i = 0;
89     daddr_t last_free = 0;
90
91     if (!d->chunks)
92         msgFatal("No chunk list found for %s!", d->name);
93
94     for (c1 = d->chunks->part; c1; c1 = c1->next) {
95         if (c1->type == unused && c1->size > last_free) {
96             last_free = c1->size;
97             current_chunk = i;
98         }
99         chunk_info[i++] = c1;
100     }
101     chunk_info[i] = NULL;
102     if (current_chunk >= i)
103         current_chunk = i - 1;
104 }
105
106 static daddr_t Total;
107
108 static void
109 check_geometry(Disk *d)
110 {
111     int sg;
112
113 #ifdef PC98
114     if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256)
115 #else
116     if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64)
117 #endif
118     {
119         dialog_clear_norefresh();
120         sg = msgYesNo("WARNING:  It is safe to use a geometry of %lu/%lu/%lu for %s on\n"
121                       "computers with modern BIOS versions.  If this disk is to be used\n"
122                       "on an old machine it is recommended that it does not have more\n"
123                       "than 65535 cylinders, more than 255 heads, or more than\n"
124 #ifdef PC98
125                       "255"
126 #else
127                       "63"
128 #endif
129                       " sectors per track.\n"
130                       "\n"
131                       "Would you like to keep using the current geometry?\n",
132                       d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
133         if (sg == 1) {
134             Sanitize_Bios_Geom(d);
135             msgConfirm("A geometry of %lu/%lu/%lu was calculated for %s.\n"
136                        "\n"
137                        "If you are not sure about this, please consult the Hardware Guide\n"
138                        "in the Documentation submenu or use the (G)eometry command to\n"
139                        "change it.  Remember: you need to enter whatever your BIOS thinks\n"
140                        "the geometry is!  For IDE, it's what you were told in the BIOS\n"
141                        "setup.  For SCSI, it's the translation mode your controller is\n"
142                        "using.  Do NOT use a ``physical geometry''.\n",
143                        d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
144         }
145     }
146 }
147
148 static void
149 print_chunks(Disk *d, int u)
150 {
151     int row;
152     int i;
153     daddr_t sz;
154     char *szstr;
155
156     szstr = (u == UNIT_GIG ? "GB" : (u == UNIT_MEG ? "MB" :
157         (u == UNIT_KILO ? "KB" : "ST")));
158
159     Total = 0;
160     for (i = 0; chunk_info[i]; i++)
161         Total += chunk_info[i]->size;
162     attrset(A_NORMAL);
163     mvaddstr(0, 0, "Disk name:\t");
164     clrtobot();
165     attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
166     attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
167     mvprintw(1, 0,
168              "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %jd sectors (%jdMB)",
169              d->bios_cyl, d->bios_hd, d->bios_sect,
170              (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect,
171              (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024);
172     mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s",
173              "Offset", "Size", szstr, "End", "Name", "PType", "Desc",
174              "Subtype", "Flags");
175     for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
176         switch(u) {
177         default:        /* fall thru */
178         case UNIT_BLOCKS:
179             sz = chunk_info[i]->size;
180             break;
181         case UNIT_KILO:
182             sz = chunk_info[i]->size / (1024/512);
183             break;
184         case UNIT_MEG:
185             sz = chunk_info[i]->size / (1024/512) / 1024;
186             break;
187         case UNIT_GIG:
188             sz = chunk_info[i]->size / (1024/512) / 1024 / 1024;
189             break;
190         }
191         if (i == current_chunk)
192             attrset(ATTR_SELECTED);
193         mvprintw(row, 0, "%10jd %10jd %10jd %8s %6d %10s %8d\t%-6s",
194                  (intmax_t)chunk_info[i]->offset, (intmax_t)sz,
195                  (intmax_t)chunk_info[i]->end, chunk_info[i]->name,
196                  chunk_info[i]->type, 
197                  slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
198                  chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
199         if (i == current_chunk)
200             attrset(A_NORMAL);
201     }
202 }
203
204 static void
205 print_command_summary(void)
206 {
207     mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
208     mvprintw(16, 0, "A = Use Entire Disk   G = set Drive Geometry   C = Create Slice");
209     mvprintw(17, 0, "D = Delete Slice      Z = Toggle Size Units    S = Set Bootable   | = Expert m.");
210     mvprintw(18, 0, "T = Change Type       U = Undo All Changes");
211
212     if (!RunningAsInit)
213         mvprintw(18, 47, "W = Write Changes  Q = Finish");
214     else
215         mvprintw(18, 47, "Q = Finish");
216     mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
217     move(0, 0);
218 }
219
220 #ifdef PC98
221 static void
222 getBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size,
223            u_char **bootmenu, size_t *bootmenu_size)
224 {
225     static u_char *boot0;
226     static size_t boot0_size;
227     static u_char *boot05;
228     static size_t boot05_size;
229
230     char str[80];
231     char *cp;
232     int i = 0;
233
234     cp = variable_get(VAR_BOOTMGR);
235     if (!cp) {
236         /* Figure out what kind of IPL the user wants */
237         sprintf(str, "Install Boot Manager for drive %s?", dname);
238         MenuIPLType.title = str;
239         i = dmenuOpenSimple(&MenuIPLType, FALSE);
240     } else {
241         if (!strncmp(cp, "boot", 4))
242             BootMgr = 0;
243         else
244             BootMgr = 1;
245     }
246     if (cp || i) {
247         switch (BootMgr) {
248         case 0:
249             if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
250             *bootipl = boot0;
251             *bootipl_size = boot0_size;
252             if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size);
253             *bootmenu = boot05;
254             *bootmenu_size = boot05_size;
255             return;
256         case 1:
257         default:
258             break;
259         }
260     }
261     *bootipl = NULL;
262     *bootipl_size = 0;
263     *bootmenu = NULL;
264     *bootmenu_size = 0;
265 }
266 #else
267 static void
268 getBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize)
269 {
270 #if defined(__i386__) || defined(__amd64__)     /* only meaningful on x86 */
271     static u_char *mbr, *boot0;
272     static size_t mbr_size, boot0_size;
273     char str[80];
274     char *cp;
275     int i = 0;
276
277     cp = variable_get(VAR_BOOTMGR);
278     if (!cp) {
279         /* Figure out what kind of MBR the user wants */
280         sprintf(str, "Install Boot Manager for drive %s?", dname);
281         MenuMBRType.title = str;
282         i = dmenuOpenSimple(&MenuMBRType, FALSE);
283     }
284     else {
285         if (!strncmp(cp, "boot", 4))
286             BootMgr = 0;
287         else if (!strcmp(cp, "standard"))
288             BootMgr = 1;
289         else
290             BootMgr = 2;
291     }
292     if (cp || i) {
293         switch (BootMgr) {
294         case 0:
295             if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
296             *bootCode = boot0;
297             *bootCodeSize = boot0_size;
298             return;
299         case 1:
300             if (!mbr) mbr = bootalloc("mbr", &mbr_size);
301             *bootCode = mbr;
302             *bootCodeSize = mbr_size;
303             return;
304         case 2:
305         default:
306             break;
307         }
308     }
309 #endif
310     *bootCode = NULL;
311     *bootCodeSize = 0;
312 }
313 #endif
314 #endif /* WITH_SLICES */
315
316 int
317 diskGetSelectCount(Device ***devs)
318 {
319     int i, cnt, enabled;
320     char *cp;
321     Device **dp;
322
323     cp = variable_get(VAR_DISK);
324     dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
325     cnt = deviceCount(dp);
326     if (!cnt)
327         return -1;
328     for (i = 0, enabled = 0; i < cnt; i++) {
329         if (dp[i]->enabled)
330             ++enabled;
331     }
332     return enabled;
333 }
334
335 #ifdef WITH_SLICES
336 void
337 diskPartition(Device *dev)
338 {
339     char *cp, *p;
340     int rv, key = 0;
341     int i;
342     Boolean chunking;
343     char *msg = NULL;
344 #ifdef PC98
345     u_char *bootipl;
346     size_t bootipl_size;
347     u_char *bootmenu;
348     size_t bootmenu_size;
349 #else
350     u_char *mbrContents;
351     size_t mbrSize;
352 #endif
353     WINDOW *w = savescr();
354     Disk *d = (Disk *)dev->private;
355     int size_unit;
356
357     size_unit = UNIT_BLOCKS;
358     chunking = TRUE;
359     keypad(stdscr, TRUE);
360
361     /* Flush both the dialog and curses library views of the screen
362        since we don't always know who called us */
363     dialog_clear_norefresh(), clear();
364     current_chunk = 0;
365
366     /* Set up the chunk array */
367     record_chunks(d);
368
369     /* Give the user a chance to sanitize the disk geometry, if necessary */
370     check_geometry(d);
371
372     while (chunking) {
373         char *val, geometry[80];
374             
375         /* Now print our overall state */
376         if (d)
377             print_chunks(d, size_unit);
378         print_command_summary();
379         if (msg) {
380             attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
381             beep();
382             msg = NULL;
383         }
384         else {
385             move(23, 0);
386             clrtoeol();
387         }
388
389         /* Get command character */
390         key = getch();
391         switch (toupper(key)) {
392         case '\014':    /* ^L (redraw) */
393             clear();
394             msg = NULL;
395             break;
396             
397         case '\020':    /* ^P */
398         case KEY_UP:
399         case '-':
400             if (current_chunk != 0)
401                 --current_chunk;
402             break;
403             
404         case '\016':    /* ^N */
405         case KEY_DOWN:
406         case '+':
407         case '\r':
408         case '\n':
409             if (chunk_info[current_chunk + 1])
410                 ++current_chunk;
411             break;
412
413         case KEY_HOME:
414             current_chunk = 0;
415             break;
416
417         case KEY_END:
418             while (chunk_info[current_chunk + 1])
419                 ++current_chunk;
420             break;
421
422         case KEY_F(1):
423         case '?':
424             systemDisplayHelp("slice");
425             clear();
426             break;
427
428         case 'A':
429 #if !defined(__i386__) && !defined(__amd64__)
430             rv = 1;
431 #else       /* The rest is only relevant on x86 */
432             rv = 0;
433 #endif
434             All_FreeBSD(d, rv);
435             variable_set2(DISK_PARTITIONED, "yes", 0);
436             record_chunks(d);
437             clear();
438             break;
439             
440         case 'C':
441             if (chunk_info[current_chunk]->type != unused)
442                 msg = "Slice in use, delete it first or move to an unused one.";
443             else {
444                 char *val, tmp[20], name[16], *cp;
445                 daddr_t size;
446                 int subtype;
447                 chunk_e partitiontype;
448 #ifdef PC98
449                 snprintf(name, sizeof (name), "%s", "FreeBSD");
450                 val = msgGetInput(name,
451                         "Please specify the name for new FreeBSD slice.");
452                 if (val)
453                         strncpy(name, val, sizeof (name));
454 #else
455                 name[0] = '\0';
456 #endif
457                 snprintf(tmp, 20, "%jd", (intmax_t)chunk_info[current_chunk]->size);
458                 val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
459                                   "or append a trailing `M' for megabytes (e.g. 20M).");
460                 if (val && (size = strtoimax(val, &cp, 0)) > 0) {
461                     if (*cp && toupper(*cp) == 'M')
462                         size *= ONE_MEG;
463                     else if (*cp && toupper(*cp) == 'G')
464                         size *= ONE_GIG;
465                     sprintf(tmp, "%d", SUBTYPE_FREEBSD);
466                     val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
467                         "Pressing Enter will choose the default, a native FreeBSD\n"
468                         "slice (type %u).  "
469                         OTHER_SLICE_VALUES
470                         NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
471                     if (val && (subtype = strtol(val, NULL, 0)) > 0) {
472                         if (subtype == SUBTYPE_FREEBSD)
473                             partitiontype = freebsd;
474                         else if (subtype == SUBTYPE_FAT)
475                             partitiontype = fat;
476                         else if (subtype == SUBTYPE_EFI)
477                             partitiontype = efi;
478                         else
479 #ifdef PC98
480                             partitiontype = pc98;
481 #else
482                             partitiontype = mbr;
483 #endif
484                         Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
485                                      (chunk_info[current_chunk]->flags & CHUNK_ALIGN), name);
486                         variable_set2(DISK_PARTITIONED, "yes", 0);
487                         record_chunks(d);
488                     }
489                 }
490                 clear();
491             }
492             break;
493             
494         case KEY_DC:
495         case 'D':
496             if (chunk_info[current_chunk]->type == unused)
497                 msg = "Slice is already unused!";
498             else {
499                 Delete_Chunk(d, chunk_info[current_chunk]);
500                 variable_set2(DISK_PARTITIONED, "yes", 0);
501                 record_chunks(d);
502             }
503             break;
504             
505         case 'T':
506             if (chunk_info[current_chunk]->type == unused)
507                 msg = "Slice is currently unused (use create instead)";
508             else {
509                 char *val, tmp[20];
510                 int subtype;
511                 chunk_e partitiontype;
512
513                 sprintf(tmp, "%d", chunk_info[current_chunk]->subtype);
514                 val = msgGetInput(tmp, "New partition type:\n\n"
515                     "Pressing Enter will use the current type. To choose a native\n"
516                     "FreeBSD slice enter %u.  "
517                     OTHER_SLICE_VALUES
518                     NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
519                 if (val && (subtype = strtol(val, NULL, 0)) > 0) {
520                     if (subtype == SUBTYPE_FREEBSD)
521                         partitiontype = freebsd;
522                     else if (subtype == SUBTYPE_FAT)
523                         partitiontype = fat;
524                     else if (subtype == SUBTYPE_EFI)
525                         partitiontype = efi;
526                     else
527 #ifdef PC98
528                         partitiontype = pc98;
529 #else
530                         partitiontype = mbr;
531 #endif
532                     chunk_info[current_chunk]->type = partitiontype;
533                     chunk_info[current_chunk]->subtype = subtype;
534                 }
535             }
536             break;
537             
538         case 'G':
539             snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
540             val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
541                               "Don't forget to use the two slash (/) separator characters!\n"
542                               "It's not possible to parse the field without them.");
543             if (val) {
544                 long nc, nh, ns;
545                 nc = strtol(val, &val, 0);
546                 nh = strtol(val + 1, &val, 0);
547                 ns = strtol(val + 1, 0, 0);
548                 Set_Bios_Geom(d, nc, nh, ns);
549             }
550             clear();
551             break;
552         
553         case 'S':
554             /* Clear active states so we won't have two */
555             for (i = 0; (chunk_info[i] != NULL) && (i < CHUNK_INFO_ENTRIES); i++)
556                 chunk_info[i]->flags &= !CHUNK_ACTIVE;
557
558             /* Set Bootable */
559             chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
560             break;
561         
562         case 'U':
563             if (!variable_cmp(DISK_LABELLED, "written")) {
564                 msgConfirm("You've already written this information out - you\n"
565                            "can't undo it.");
566             }
567             else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
568                 char cp[BUFSIZ];
569
570                 sstrncpy(cp, d->name, sizeof cp);
571                 Free_Disk(dev->private);
572                 d = Open_Disk(cp);
573                 if (!d)
574                     msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
575                 dev->private = d;
576                 variable_unset(DISK_PARTITIONED);
577                 variable_unset(DISK_LABELLED);
578                 if (d)
579                     record_chunks(d);
580             }
581             clear();
582             break;
583
584         case 'W':
585             if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
586                                "installation.  If you are installing FreeBSD for the first time\n"
587                                "then you should simply type Q when you're finished here and your\n"
588                                "changes will be committed in one batch automatically at the end of\n"
589                                "these questions.  If you're adding a disk, you should NOT write\n"
590                                "from this screen, you should do it from the label editor.\n\n"
591                                "Are you absolutely sure you want to do this now?")) {
592                 variable_set2(DISK_PARTITIONED, "yes", 0);
593
594 #ifdef PC98
595                 /*
596                  * Don't trash the IPL if the first (and therefore only) chunk
597                  * is marked for a truly dedicated disk (i.e., the disklabel
598                  * starts at sector 0), even in cases where the user has
599                  * requested a FreeBSD Boot Manager -- both would be fatal in
600                  * this case.
601                  */
602                 /*
603                  * Don't offer to update the IPL on this disk if the first
604                  * "real" chunk looks like a FreeBSD "all disk" partition,
605                  * or the disk is entirely FreeBSD.
606                  */
607                 if ((d->chunks->part->type != freebsd) ||
608                     (d->chunks->part->offset > 1))
609                     getBootMgr(d->name, &bootipl, &bootipl_size,
610                                &bootmenu, &bootmenu_size);
611                 else {
612                     bootipl = NULL;
613                     bootipl_size = 0;
614                     bootmenu = NULL;
615                     bootmenu_size = 0;
616                 }
617                 Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
618 #else
619                 /*
620                  * Don't trash the MBR if the first (and therefore only) chunk
621                  * is marked for a truly dedicated disk (i.e., the disklabel
622                  * starts at sector 0), even in cases where the user has
623                  * requested booteasy or a "standard" MBR -- both would be
624                  * fatal in this case.
625                  */
626                 /*
627                  * Don't offer to update the MBR on this disk if the first
628                  * "real" chunk looks like a FreeBSD "all disk" partition,
629                  * or the disk is entirely FreeBSD.
630                  */
631                 if ((d->chunks->part->type != freebsd) ||
632                     (d->chunks->part->offset > 1))
633                     getBootMgr(d->name, &mbrContents, &mbrSize);
634                 else {
635                     mbrContents = NULL;
636                     mbrSize = 0;
637                 }
638                 Set_Boot_Mgr(d, mbrContents, mbrSize);
639 #endif
640
641                 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
642                     msgConfirm("Disk partition write returned an error status!");
643                 else
644                     msgConfirm("Wrote FDISK partition information out successfully.");
645             }
646             clear();
647             break;
648
649         case '|':
650             if (!msgNoYes("Are you SURE you want to go into Expert mode?\n"
651                           "No seat belts whatsoever are provided!")) {
652                 clear();
653                 refresh();
654                 slice_wizard(d);
655                 variable_set2(DISK_PARTITIONED, "yes", 0);
656                 record_chunks(d);
657             }
658             else
659                 msg = "Wise choice!";
660             clear();
661             break;
662
663         case '\033':    /* ESC */
664         case 'Q':
665             chunking = FALSE;
666 #ifdef PC98
667             /*
668              * Don't trash the IPL if the first (and therefore only) chunk
669              * is marked for a truly dedicated disk (i.e., the disklabel
670              * starts at sector 0), even in cases where the user has requested
671              * a FreeBSD Boot Manager -- both would be fatal in this case.
672              */
673             /*
674              * Don't offer to update the IPL on this disk if the first "real"
675              * chunk looks like a FreeBSD "all disk" partition, or the disk is
676              * entirely FreeBSD. 
677              */
678             if ((d->chunks->part->type != freebsd) ||
679                 (d->chunks->part->offset > 1)) {
680                 if (variable_cmp(DISK_PARTITIONED, "written")) {
681                     getBootMgr(d->name, &bootipl, &bootipl_size,
682                         &bootmenu, &bootmenu_size);
683                     if (bootipl != NULL && bootmenu != NULL)
684                         Set_Boot_Mgr(d, bootipl, bootipl_size,
685                             bootmenu, bootmenu_size);
686                 }
687             }
688 #else
689             /*
690              * Don't trash the MBR if the first (and therefore only) chunk
691              * is marked for a truly dedicated disk (i.e., the disklabel
692              * starts at sector 0), even in cases where the user has requested
693              * booteasy or a "standard" MBR -- both would be fatal in this case.
694              */
695             /*
696              * Don't offer to update the MBR on this disk if the first "real"
697              * chunk looks like a FreeBSD "all disk" partition, or the disk is
698              * entirely FreeBSD. 
699              */
700             if ((d->chunks->part->type != freebsd) ||
701                 (d->chunks->part->offset > 1)) {
702                 if (variable_cmp(DISK_PARTITIONED, "written")) {
703                     getBootMgr(d->name, &mbrContents, &mbrSize);
704                     if (mbrContents != NULL)
705                         Set_Boot_Mgr(d, mbrContents, mbrSize);
706                 }
707             }
708 #endif
709             break;
710
711         case 'Z':
712             size_unit = (size_unit + 1) % UNIT_SIZE;
713             break;
714             
715         default:
716             beep();
717             msg = "Type F1 or ? for help";
718             break;
719         }
720     }
721     p = CheckRules(d);
722     if (p) {
723         char buf[FILENAME_MAX];
724         
725         use_helpline("Press F1 to read more about disk slices.");
726         use_helpfile(systemHelpFile("partition", buf));
727         if (!variable_get(VAR_NO_WARN))
728             dialog_mesgbox("Disk slicing warning:", p, -1, -1);
729         free(p);
730     }
731     restorescr(w);
732 }
733 #endif /* WITH_SLICES */
734
735 static u_char *
736 bootalloc(char *name, size_t *size)
737 {
738     char buf[FILENAME_MAX];
739     struct stat sb;
740
741     snprintf(buf, sizeof buf, "/boot/%s", name);
742     if (stat(buf, &sb) != -1) {
743         int fd;
744
745         fd = open(buf, O_RDONLY);
746         if (fd != -1) {
747             u_char *cp;
748
749             cp = malloc(sb.st_size);
750             if (read(fd, cp, sb.st_size) != sb.st_size) {
751                 free(cp);
752                 close(fd);
753                 msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
754                 return NULL;
755             }
756             close(fd);
757             if (size != NULL)
758                 *size = sb.st_size;
759             return cp;
760         }
761         msgDebug("bootalloc: couldn't open %s\n", buf);
762     }
763     else
764         msgDebug("bootalloc: can't stat %s\n", buf);
765     return NULL;
766 }
767
768 #ifdef WITH_SLICES 
769 static int
770 partitionHook(dialogMenuItem *selected)
771 {
772     Device **devs = NULL;
773
774     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
775     if (!devs) {
776         msgConfirm("Unable to find disk %s!", selected->prompt);
777         return DITEM_FAILURE;
778     }
779     /* Toggle enabled status? */
780     if (!devs[0]->enabled) {
781         devs[0]->enabled = TRUE;
782         diskPartition(devs[0]);
783     }
784     else
785         devs[0]->enabled = FALSE;
786     return DITEM_SUCCESS;
787 }
788
789 static int
790 partitionCheck(dialogMenuItem *selected)
791 {
792     Device **devs = NULL;
793
794     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
795     if (!devs || devs[0]->enabled == FALSE)
796         return FALSE;
797     return TRUE;
798 }
799
800 int
801 diskPartitionEditor(dialogMenuItem *self)
802 {
803     DMenu *menu;
804     Device **devs;
805     int i, cnt, devcnt;
806
807     cnt = diskGetSelectCount(&devs);
808     devcnt = deviceCount(devs);
809     if (cnt == -1) {
810         msgConfirm("No disks found!  Please verify that your disk controller is being\n"
811                    "properly probed at boot time.  See the Hardware Guide on the\n"
812                    "Documentation menu for clues on diagnosing this type of problem.");
813         return DITEM_FAILURE;
814     }
815     else if (cnt) {
816         /* Some are already selected */
817         for (i = 0; i < devcnt; i++) {
818             if (devs[i]->enabled) {
819                 if (variable_get(VAR_NONINTERACTIVE) &&
820                   !variable_get(VAR_DISKINTERACTIVE))
821                     diskPartitionNonInteractive(devs[i]);
822                 else
823                     diskPartition(devs[i]);
824             }
825         }
826     }
827     else {
828         /* No disks are selected, fall-back case now */
829         if (devcnt == 1) {
830             devs[0]->enabled = TRUE;
831             if (variable_get(VAR_NONINTERACTIVE) &&
832               !variable_get(VAR_DISKINTERACTIVE))
833                 diskPartitionNonInteractive(devs[0]);
834             else
835                 diskPartition(devs[0]);
836             return DITEM_SUCCESS;
837         }
838         else {
839             menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
840             if (!menu) {
841                 msgConfirm("No devices suitable for installation found!\n\n"
842                            "Please verify that your disk controller (and attached drives)\n"
843                            "were detected properly.  This can be done by pressing the\n"
844                            "[Scroll Lock] key and using the Arrow keys to move back to\n"
845                            "the boot messages.  Press [Scroll Lock] again to return.");
846                 return DITEM_FAILURE;
847             }
848             else {
849                 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
850                 free(menu);
851             }
852             return i;
853         }
854     }
855     return DITEM_SUCCESS;
856 }
857 #endif /* WITH_SLICES */
858
859 int
860 diskPartitionWrite(dialogMenuItem *self)
861 {
862     Device **devs;
863     int i;
864
865     if (!variable_cmp(DISK_PARTITIONED, "written"))
866         return DITEM_SUCCESS;
867
868     devs = deviceFind(NULL, DEVICE_TYPE_DISK);
869     if (!devs) {
870         msgConfirm("Unable to find any disks to write to??");
871         return DITEM_FAILURE;
872     }
873     if (isDebug())
874         msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
875     for (i = 0; devs[i]; i++) {
876         Disk *d = (Disk *)devs[i]->private;
877         static u_char *boot1;
878 #if defined(__i386__) || defined(__amd64__)
879         static u_char *boot2;
880 #endif
881
882         if (!devs[i]->enabled)
883             continue;
884
885 #if defined(__i386__) || defined(__amd64__)
886         if (!boot1) boot1 = bootalloc("boot1", NULL);
887         if (!boot2) boot2 = bootalloc("boot2", NULL);
888         Set_Boot_Blocks(d, boot1, boot2);
889 #elif !defined(__ia64__)
890         if (!boot1) boot1 = bootalloc("boot1", NULL);
891         Set_Boot_Blocks(d, boot1, NULL);
892 #endif
893
894         msgNotify("Writing partition information to drive %s", d->name);
895         if (!Fake && Write_Disk(d)) {
896             if (RunningAsInit) {
897                 msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
898             } else {
899                 msgConfirm("ERROR: Unable to write data to disk %s!\n\n"
900                     "To edit the labels on a running system set\n"
901                     "sysctl kern.geom.debugflags=16 and try again.", d->name);
902             }
903             return DITEM_FAILURE;
904         }
905     }
906     /* Now it's not "yes", but "written" */
907     variable_set2(DISK_PARTITIONED, "written", 0);
908     return DITEM_SUCCESS | DITEM_RESTORE;
909 }
910
911 #ifdef WITH_SLICES
912 /* Partition a disk based wholly on which variables are set */
913 static void
914 diskPartitionNonInteractive(Device *dev)
915 {
916     char *cp;
917     int i, all_disk = 0;
918     daddr_t sz;
919 #ifdef PC98
920     u_char *bootipl;
921     size_t bootipl_size;
922     u_char *bootmenu;
923     size_t bootmenu_size;
924 #else
925     u_char *mbrContents;
926     size_t mbrSize;
927 #endif
928     Disk *d = (Disk *)dev->private;
929
930     record_chunks(d);
931     cp = variable_get(VAR_GEOMETRY);
932     if (cp) {
933         if (!strcasecmp(cp, "sane")) {
934 #ifdef PC98
935             if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256)
936 #else
937             if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64)
938 #endif
939             {
940                 msgDebug("Warning:  A geometry of %lu/%lu/%lu for %s is incorrect.\n",
941                     d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
942                 Sanitize_Bios_Geom(d);
943                 msgDebug("Sanitized geometry for %s is %lu/%lu/%lu.\n",
944                     d->name, d->bios_cyl, d->bios_hd, d->bios_sect);
945             }
946         } else {
947             msgDebug("Setting geometry from script to: %s\n", cp);
948             d->bios_cyl = strtol(cp, &cp, 0);
949             d->bios_hd = strtol(cp + 1, &cp, 0);
950             d->bios_sect = strtol(cp + 1, 0, 0);
951         }
952     }
953
954     cp = variable_get(VAR_PARTITION);
955     if (cp) {
956         if (!strcmp(cp, "free")) {
957             /* Do free disk space case */
958             for (i = 0; chunk_info[i]; i++) {
959                 /* If a chunk is at least 10MB in size, use it. */
960                 if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
961                     Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
962                                  freebsd, 3,
963                                  (chunk_info[i]->flags & CHUNK_ALIGN),
964                                  "FreeBSD");
965                     variable_set2(DISK_PARTITIONED, "yes", 0);
966                     break;
967                 }
968             }
969             if (!chunk_info[i]) {
970                 msgConfirm("Unable to find any free space on this disk!");
971                 return;
972             }
973         }
974         else if (!strcmp(cp, "all")) {
975             /* Do all disk space case */
976             msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
977
978             All_FreeBSD(d, FALSE);
979         }
980         else if (!strcmp(cp, "exclusive")) {
981             /* Do really-all-the-disk-space case */
982             msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
983
984             All_FreeBSD(d, all_disk = TRUE);
985         }
986         else if ((sz = strtoimax(cp, &cp, 0))) {
987             /* Look for sz bytes free */
988             if (*cp && toupper(*cp) == 'M')
989                 sz *= ONE_MEG;
990             else if (*cp && toupper(*cp) == 'G')
991                 sz *= ONE_GIG;
992             for (i = 0; chunk_info[i]; i++) {
993                 /* If a chunk is at least sz MB, use it. */
994                 if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
995                     Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
996                                  (chunk_info[i]->flags & CHUNK_ALIGN),
997                                  "FreeBSD");
998                     variable_set2(DISK_PARTITIONED, "yes", 0);
999                     break;
1000                 }
1001             }
1002             if (!chunk_info[i]) {
1003                     msgConfirm("Unable to find %jd free blocks on this disk!",
1004                         (intmax_t)sz);
1005                 return;
1006             }
1007         }
1008         else if (!strcmp(cp, "existing")) {
1009             /* Do existing FreeBSD case */
1010             for (i = 0; chunk_info[i]; i++) {
1011                 if (chunk_info[i]->type == freebsd)
1012                     break;
1013             }
1014             if (!chunk_info[i]) {
1015                 msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
1016                 return;
1017             }
1018         }
1019         else {
1020             msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
1021             return;
1022         }
1023         if (!all_disk) {
1024 #ifdef PC98
1025             getBootMgr(d->name, &bootipl, &bootipl_size,
1026                        &bootmenu, &bootmenu_size);
1027             Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
1028 #else
1029             getBootMgr(d->name, &mbrContents, &mbrSize);
1030             Set_Boot_Mgr(d, mbrContents, mbrSize);
1031 #endif
1032         }
1033         variable_set2(DISK_PARTITIONED, "yes", 0);
1034     }
1035 }
1036 #endif /* WITH_SLICES */