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