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