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