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