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