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