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