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