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