Check error return value when creating the IPC service.
[dragonfly.git] / release / 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: src/release/sysinstall/disks.c,v 1.118.2.14 2002/10/24 13:03:44 nyan Exp $
8  * $DragonFly: src/release/sysinstall/Attic/disks.c,v 1.2 2003/06/17 04:27:21 dillon Exp $
9  *
10  * Copyright (c) 1995
11  *      Jordan Hubbard.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer,
18  *    verbatim and that no modifications are made prior to this
19  *    point in the file.
20  * 2. Redistributions in binary form must reproduce the above copyright
21  *    notice, this list of conditions and the following disclaimer in the
22  *    documentation and/or other materials provided with the distribution.
23  *
24  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  */
37
38 #include "sysinstall.h"
39 #include <ctype.h>
40 #include <fcntl.h>
41 #include <sys/stat.h>
42 #include <sys/disklabel.h>
43
44 enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_SIZE };
45
46 #ifdef PC98
47 #define SUBTYPE_FREEBSD         50324
48 #define SUBTYPE_FAT             37218
49 #else
50 #define SUBTYPE_FREEBSD         165
51 #define SUBTYPE_FAT             6
52 #endif
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 MBR the user wants */
187         sprintf(str, "Install Boot Manager for drive %s?", dname);
188         MenuMBRType.title = str;
189         i = dmenuOpenSimple(&MenuMBRType, FALSE);
190     } else {
191         if (!strncmp(cp, "boot", 4))
192             BootMgr = 0;
193         else
194             BootMgr = 2;
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 2:
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 #ifndef __alpha__       /* 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 #ifdef __alpha__
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], *cp;
403                 int size;
404 #ifdef PC98
405                 char name[16];
406
407                 snprintf(name, 16, "%s", "FreeBSD");
408                 val = msgGetInput(name,
409                         "Please specify the name for new FreeBSD slice.");
410                 if (val)
411                         strncpy(name, val, 16);
412 #else
413                 int subtype;
414                 chunk_e partitiontype;
415 #endif
416                 snprintf(tmp, 20, "%lu", chunk_info[current_chunk]->size);
417                 val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
418                                   "or append a trailing `M' for megabytes (e.g. 20M).");
419                 if (val && (size = strtol(val, &cp, 0)) > 0) {
420                     if (*cp && toupper(*cp) == 'M')
421                         size *= ONE_MEG;
422                     else if (*cp && toupper(*cp) == 'G')
423                         size *= ONE_GIG;
424 #ifdef PC98
425                     Create_Chunk(d, chunk_info[current_chunk]->offset, size,
426                         freebsd, 3,
427                         (chunk_info[current_chunk]->flags & CHUNK_ALIGN),
428                         name);
429                     variable_set2(DISK_PARTITIONED, "yes", 0);
430                     record_chunks(d);
431 #else
432                     sprintf(tmp, "%d", SUBTYPE_FREEBSD);
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                     if (val && (subtype = strtol(val, NULL, 0)) > 0) {
442                         if (subtype == SUBTYPE_FREEBSD)
443                             partitiontype = freebsd;
444                         else if (subtype == SUBTYPE_FAT)
445                             partitiontype = fat;
446                         else
447                             partitiontype = unknown;
448 #ifdef __alpha__
449                         if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
450                             All_FreeBSD(d, 1);
451                         else
452 #endif
453                         Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
454                                      (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
455                         variable_set2(DISK_PARTITIONED, "yes", 0);
456                         record_chunks(d);
457                     }
458 #endif /* PC98 */
459                 }
460                 clear();
461             }
462             break;
463             
464         case KEY_DC:
465         case 'D':
466             if (chunk_info[current_chunk]->type == unused)
467                 msg = "Slice is already unused!";
468             else {
469                 Delete_Chunk(d, chunk_info[current_chunk]);
470                 variable_set2(DISK_PARTITIONED, "yes", 0);
471                 record_chunks(d);
472             }
473             break;
474             
475         case 'T':
476             if (chunk_info[current_chunk]->type == unused)
477                 msg = "Slice is currently unused (use create instead)";
478             else {
479                 char *val, tmp[20];
480                 int subtype;
481                 chunk_e partitiontype;
482
483                 sprintf(tmp, "%d", SUBTYPE_FREEBSD);
484 #ifdef PC98
485                 val = msgGetInput(tmp, "New partition type:\n\n"
486                                   "Pressing Enter will choose the default, a native FreeBSD\n"
487                                   "slice (type 50324).  Other popular values are 37218 for\n"
488                                   "DOS FAT partition.\n\n"
489                                   "Note:  If you choose a non-FreeBSD partition type, it will not\n"
490                                   "be formatted or otherwise prepared, it will simply reserve space\n"
491                                   "for you to use another tool, such as DOS format, to later format\n"
492                                   "and actually use the partition.");
493 #else
494                 val = msgGetInput(tmp, "New partition type:\n\n"
495                                   "Pressing Enter will choose the default, a native FreeBSD\n"
496                                   "slice (type 165).  Other popular values are 6 for\n"
497                                   "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
498                                   "130 for a Linux swap partition.\n\n"
499                                   "Note:  If you choose a non-FreeBSD partition type, it will not\n"
500                                   "be formatted or otherwise prepared, it will simply reserve space\n"
501                                   "for you to use another tool, such as DOS format, to later format\n"
502                                   "and actually use the partition.");
503 #endif /* PC98 */
504                 if (val && (subtype = strtol(val, NULL, 0)) > 0) {
505                     if (subtype == SUBTYPE_FREEBSD)
506                         partitiontype = freebsd;
507                     else if (subtype == SUBTYPE_FAT)
508                         partitiontype = fat;
509                     else
510                         partitiontype = unknown;
511                     chunk_info[current_chunk]->type = partitiontype;
512                     chunk_info[current_chunk]->subtype = subtype;
513                 }
514             }
515             break;
516             
517         case 'G':
518             snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
519             val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
520                               "Don't forget to use the two slash (/) separator characters!\n"
521                               "It's not possible to parse the field without them.");
522             if (val) {
523                 long nc, nh, ns;
524                 nc = strtol(val, &val, 0);
525                 nh = strtol(val + 1, &val, 0);
526                 ns = strtol(val + 1, 0, 0);
527                 Set_Bios_Geom(d, nc, nh, ns);
528             }
529             clear();
530             break;
531         
532         case 'S':
533             /* Set Bootable */
534             chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
535             break;
536         
537         case 'U':
538             if (!variable_cmp(DISK_LABELLED, "written")) {
539                 msgConfirm("You've already written this information out - you\n"
540                            "can't undo it.");
541             }
542             else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
543                 char cp[BUFSIZ];
544
545                 sstrncpy(cp, d->name, sizeof cp);
546                 Free_Disk(dev->private);
547                 d = Open_Disk(cp);
548                 if (!d)
549                     msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
550                 dev->private = d;
551                 variable_unset(DISK_PARTITIONED);
552                 variable_unset(DISK_LABELLED);
553                 if (d)
554                     record_chunks(d);
555             }
556             clear();
557             break;
558
559         case 'W':
560             if (!msgNoYes("WARNING:  This should only be used when modifying an EXISTING\n"
561                                "installation.  If you are installing FreeBSD for the first time\n"
562                                "then you should simply type Q when you're finished here and your\n"
563                                "changes will be committed in one batch automatically at the end of\n"
564                                "these questions.  If you're adding a disk, you should NOT write\n"
565                                "from this screen, you should do it from the label editor.\n\n"
566                                "Are you absolutely sure you want to do this now?")) {
567                 variable_set2(DISK_PARTITIONED, "yes", 0);
568
569                 /*
570                  * Don't trash the MBR if the first (and therefore only) chunk
571                  * is marked for a truly dedicated disk (i.e., the disklabel
572                  * starts at sector 0), even in cases where the user has
573                  * requested booteasy or a "standard" MBR -- both would be
574                  * fatal in this case.
575                  */
576                 /*
577                  * Don't offer to update the MBR on this disk if the first
578                  * "real" chunk looks like a FreeBSD "all disk" partition,
579                  * or the disk is entirely FreeBSD.
580                  */
581 #ifdef PC98
582                 if ((d->chunks->part->type != freebsd) ||
583                     (d->chunks->part->offset > 1))
584                     getBootMgr(d->name, &bootipl, &bootipl_size,
585                                &bootmenu, &bootmenu_size);
586                 else {
587                     bootipl = NULL;
588                     bootipl_size = 0;
589                     bootmenu = NULL;
590                     bootmenu_size = 0;
591                 }
592                 Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
593 #else
594                 if ((d->chunks->part->type != freebsd) ||
595                     (d->chunks->part->offset > 1))
596                     getBootMgr(d->name, &mbrContents, &mbrSize);
597                 else {
598                     mbrContents = NULL;
599                     mbrSize = 0;
600                 }
601                 Set_Boot_Mgr(d, mbrContents, mbrSize);
602 #endif
603
604                 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
605                     msgConfirm("Disk partition write returned an error status!");
606                 else
607                     msgConfirm("Wrote FDISK partition information out successfully.");
608             }
609             clear();
610             break;
611             
612         case '|':
613             if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
614                           "No seat belts whatsoever are provided!")) {
615                 clear();
616                 refresh();
617                 slice_wizard(d);
618                 variable_set2(DISK_PARTITIONED, "yes", 0);
619                 record_chunks(d);
620             }
621             else
622                 msg = "Wise choice!";
623             clear();
624             break;
625
626         case '\033':    /* ESC */
627         case 'Q':
628             chunking = FALSE;
629             /*
630              * Don't trash the MBR if the first (and therefore only) chunk
631              * is marked for a truly dedicated disk (i.e., the disklabel
632              * starts at sector 0), even in cases where the user has requested
633              * booteasy or a "standard" MBR -- both would be fatal in this case.
634              */
635             /*
636              * Don't offer to update the MBR on this disk if the first "real"
637              * chunk looks like a FreeBSD "all disk" partition, or the disk is
638              * entirely FreeBSD. 
639              */
640             if ((d->chunks->part->type != freebsd) ||
641                 (d->chunks->part->offset > 1)) {
642                 if (variable_cmp(DISK_PARTITIONED, "written")) {
643 #ifdef PC98
644                     getBootMgr(d->name, &bootipl, &bootipl_size,
645                         &bootmenu, &bootmenu_size);
646                     if (bootipl != NULL && bootmenu != NULL)
647                         Set_Boot_Mgr(d, bootipl, bootipl_size,
648                             bootmenu, bootmenu_size);
649 #else
650                     getBootMgr(d->name, &mbrContents, &mbrSize);
651                     if (mbrContents != NULL)
652                         Set_Boot_Mgr(d, mbrContents, mbrSize);
653 #endif
654                 }
655             }
656             break;
657
658         case 'Z':
659             size_unit = (size_unit + 1) % UNIT_SIZE;
660             break;
661             
662         default:
663             beep();
664             msg = "Type F1 or ? for help";
665             break;
666         }
667     }
668     p = CheckRules(d);
669     if (p) {
670         char buf[FILENAME_MAX];
671         
672         use_helpline("Press F1 to read more about disk slices.");
673         use_helpfile(systemHelpFile("partition", buf));
674         if (!variable_get(VAR_NO_WARN))
675             dialog_mesgbox("Disk slicing warning:", p, -1, -1);
676         free(p);
677     }
678     restorescr(w);
679 }
680
681 static u_char *
682 bootalloc(char *name, size_t *size)
683 {
684     char buf[FILENAME_MAX];
685     struct stat sb;
686
687     snprintf(buf, sizeof buf, "/boot/%s", name);
688     if (stat(buf, &sb) != -1) {
689         int fd;
690
691         fd = open(buf, O_RDONLY);
692         if (fd != -1) {
693             u_char *cp;
694
695             cp = malloc(sb.st_size);
696             if (read(fd, cp, sb.st_size) != sb.st_size) {
697                 free(cp);
698                 close(fd);
699                 msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
700                 return NULL;
701             }
702             close(fd);
703             if (size != NULL)
704                 *size = sb.st_size;
705             return cp;
706         }
707         msgDebug("bootalloc: couldn't open %s\n", buf);
708     }
709     else
710         msgDebug("bootalloc: can't stat %s\n", buf);
711     return NULL;
712 }
713         
714 static int
715 partitionHook(dialogMenuItem *selected)
716 {
717     Device **devs = NULL;
718
719     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
720     if (!devs) {
721         msgConfirm("Unable to find disk %s!", selected->prompt);
722         return DITEM_FAILURE;
723     }
724     /* Toggle enabled status? */
725     if (!devs[0]->enabled) {
726         devs[0]->enabled = TRUE;
727         diskPartition(devs[0]);
728     }
729     else
730         devs[0]->enabled = FALSE;
731     return DITEM_SUCCESS;
732 }
733
734 static int
735 partitionCheck(dialogMenuItem *selected)
736 {
737     Device **devs = NULL;
738
739     devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
740     if (!devs || devs[0]->enabled == FALSE)
741         return FALSE;
742     return TRUE;
743 }
744
745 int
746 diskPartitionEditor(dialogMenuItem *self)
747 {
748     DMenu *menu;
749     Device **devs;
750     int i, cnt, devcnt;
751
752     cnt = diskGetSelectCount(&devs);
753     devcnt = deviceCount(devs);
754     if (cnt == -1) {
755         msgConfirm("No disks found!  Please verify that your disk controller is being\n"
756                    "properly probed at boot time.  See the Hardware Guide on the\n"
757                    "Documentation menu for clues on diagnosing this type of problem.");
758         return DITEM_FAILURE;
759     }
760     else if (cnt) {
761         /* Some are already selected */
762         for (i = 0; i < devcnt; i++) {
763             if (devs[i]->enabled) {
764                 if (variable_get(VAR_NONINTERACTIVE) &&
765                   !variable_get(VAR_DISKINTERACTIVE))
766                     diskPartitionNonInteractive(devs[i]);
767                 else
768                     diskPartition(devs[i]);
769             }
770         }
771     }
772     else {
773         /* No disks are selected, fall-back case now */
774         if (devcnt == 1) {
775             devs[0]->enabled = TRUE;
776             if (variable_get(VAR_NONINTERACTIVE) &&
777               !variable_get(VAR_DISKINTERACTIVE))
778                 diskPartitionNonInteractive(devs[0]);
779             else
780                 diskPartition(devs[0]);
781             return DITEM_SUCCESS;
782         }
783         else {
784             menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
785             if (!menu) {
786                 msgConfirm("No devices suitable for installation found!\n\n"
787                            "Please verify that your disk controller (and attached drives)\n"
788                            "were detected properly.  This can be done by pressing the\n"
789                            "[Scroll Lock] key and using the Arrow keys to move back to\n"
790                            "the boot messages.  Press [Scroll Lock] again to return.");
791                 return DITEM_FAILURE;
792             }
793             else {
794                 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
795                 free(menu);
796             }
797             return i;
798         }
799     }
800     return DITEM_SUCCESS;
801 }
802
803 int
804 diskPartitionWrite(dialogMenuItem *self)
805 {
806     Device **devs;
807     int i;
808
809     if (!variable_cmp(DISK_PARTITIONED, "written"))
810         return DITEM_SUCCESS;
811
812     devs = deviceFind(NULL, DEVICE_TYPE_DISK);
813     if (!devs) {
814         msgConfirm("Unable to find any disks to write to??");
815         return DITEM_FAILURE;
816     }
817     if (isDebug())
818         msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
819     for (i = 0; devs[i]; i++) {
820         Disk *d = (Disk *)devs[i]->private;
821         static u_char *boot1;
822 #ifndef __alpha__
823         static u_char *boot2;
824 #endif
825
826         if (!devs[i]->enabled)
827             continue;
828
829 #ifdef __alpha__
830         if (!boot1) boot1 = bootalloc("boot1", NULL);
831         Set_Boot_Blocks(d, boot1, NULL);
832 #else
833         if (!boot1) boot1 = bootalloc("boot1", NULL);
834         if (!boot2) boot2 = bootalloc("boot2", NULL);
835         Set_Boot_Blocks(d, boot1, boot2);
836 #endif
837
838         msgNotify("Writing partition information to drive %s", d->name);
839         if (!Fake && Write_Disk(d)) {
840             msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
841             return DITEM_FAILURE;
842         }
843     }
844     /* Now it's not "yes", but "written" */
845     variable_set2(DISK_PARTITIONED, "written", 0);
846     return DITEM_SUCCESS | DITEM_RESTORE;
847 }
848
849 /* Partition a disk based wholly on which variables are set */
850 static void
851 diskPartitionNonInteractive(Device *dev)
852 {
853     char *cp;
854     int i, sz, all_disk = 0;
855 #ifdef PC98
856     u_char *bootipl;
857     size_t bootipl_size;
858     u_char *bootmenu;
859     size_t bootmenu_size;
860 #else
861     u_char *mbrContents;
862     size_t mbrSize;
863 #endif
864     Disk *d = (Disk *)dev->private;
865
866     record_chunks(d);
867     cp = variable_get(VAR_GEOMETRY);
868     if (cp) {
869         msgDebug("Setting geometry from script to: %s\n", cp);
870         d->bios_cyl = strtol(cp, &cp, 0);
871         d->bios_hd = strtol(cp + 1, &cp, 0);
872         d->bios_sect = strtol(cp + 1, 0, 0);
873     }
874
875     cp = variable_get(VAR_PARTITION);
876     if (cp) {
877         if (!strcmp(cp, "free")) {
878             /* Do free disk space case */
879             for (i = 0; chunk_info[i]; i++) {
880                 /* If a chunk is at least 10MB in size, use it. */
881                 if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
882 #ifdef PC98
883                     Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
884                                  freebsd, 3,
885                                  (chunk_info[i]->flags & CHUNK_ALIGN),
886                                  "FreeBSD");
887 #else
888                     Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
889                                  freebsd, 3,
890                                  (chunk_info[i]->flags & CHUNK_ALIGN));
891 #endif
892                     variable_set2(DISK_PARTITIONED, "yes", 0);
893                     break;
894                 }
895             }
896             if (!chunk_info[i]) {
897                 msgConfirm("Unable to find any free space on this disk!");
898                 return;
899             }
900         }
901         else if (!strcmp(cp, "all")) {
902             /* Do all disk space case */
903             msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
904
905             All_FreeBSD(d, FALSE);
906         }
907         else if (!strcmp(cp, "exclusive")) {
908             /* Do really-all-the-disk-space case */
909             msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
910
911             All_FreeBSD(d, all_disk = TRUE);
912         }
913         else if ((sz = strtol(cp, &cp, 0))) {
914             /* Look for sz bytes free */
915             if (*cp && toupper(*cp) == 'M')
916                 sz *= ONE_MEG;
917             else if (*cp && toupper(*cp) == 'G')
918                 sz *= ONE_GIG;
919             for (i = 0; chunk_info[i]; i++) {
920                 /* If a chunk is at least sz MB, use it. */
921                 if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
922 #ifdef PC98
923                     Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
924                                  (chunk_info[i]->flags & CHUNK_ALIGN),
925                                  "FreeBSD");
926 #else
927                     Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
928                                  (chunk_info[i]->flags & CHUNK_ALIGN));
929 #endif
930                     variable_set2(DISK_PARTITIONED, "yes", 0);
931                     break;
932                 }
933             }
934             if (!chunk_info[i]) {
935                 msgConfirm("Unable to find %d free blocks on this disk!", sz);
936                 return;
937             }
938         }
939         else if (!strcmp(cp, "existing")) {
940             /* Do existing FreeBSD case */
941             for (i = 0; chunk_info[i]; i++) {
942                 if (chunk_info[i]->type == freebsd)
943                     break;
944             }
945             if (!chunk_info[i]) {
946                 msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
947                 return;
948             }
949         }
950         else {
951             msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
952             return;
953         }
954         if (!all_disk) {
955 #ifdef PC98
956             getBootMgr(d->name, &bootipl, &bootipl_size,
957                        &bootmenu, &bootmenu_size);
958             Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
959 #else
960             getBootMgr(d->name, &mbrContents, &mbrSize);
961             Set_Boot_Mgr(d, mbrContents, mbrSize);
962 #endif
963         }
964         variable_set2(DISK_PARTITIONED, "yes", 0);
965     }
966 }
967