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