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