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