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