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