2 * The new sysinstall program.
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
10 * Jordan Hubbard. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
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
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.
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
37 #include "sysinstall.h"
41 #include <sys/disklabel.h>
43 /* Where we start displaying chunk information on the screen */
44 #define CHUNK_START_ROW 5
46 /* Where we keep track of MBR chunks */
47 static struct chunk *chunk_info[16];
48 static int current_chunk;
50 static void diskPartitionNonInteractive(Device *dev);
53 record_chunks(Disk *d)
55 struct chunk *c1 = NULL;
60 msgFatal("No chunk list found for %s!", d->name);
62 for (c1 = d->chunks->part; c1; c1 = c1->next) {
63 if (c1->type == unused && c1->size > last_free) {
70 if (current_chunk >= i)
71 current_chunk = i - 1;
82 for (i = Total = 0; chunk_info[i]; i++)
83 Total += chunk_info[i]->size;
84 if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
85 dialog_clear_norefresh();
86 msgConfirm("WARNING: A geometry of %d/%d/%d for %s is incorrect. Using\n"
87 "a more likely geometry. If this geometry is incorrect or you\n"
88 "are unsure as to whether or not it's correct, please consult\n"
89 "the Hardware Guide in the Documentation submenu or use the\n"
90 "(G)eometry command to change it now.\n\n"
91 "Remember: you need to enter whatever your BIOS thinks the\n"
92 "geometry is! For IDE, it's what you were told in the BIOS\n"
93 "setup. For SCSI, it's the translation mode your controller is\n"
94 "using. Do NOT use a ``physical geometry''.",
95 d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
96 Sanitize_Bios_Geom(d);
99 mvaddstr(0, 0, "Disk name:\t");
101 attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
102 attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
104 "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %lu sectors",
105 d->bios_cyl, d->bios_hd, d->bios_sect,
106 d->bios_cyl * d->bios_hd * d->bios_sect);
107 mvprintw(3, 0, "%10s %10s %10s %8s %6s %10s %8s %8s",
108 "Offset", "Size", "End", "Name", "PType", "Desc",
110 for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
111 if (i == current_chunk)
112 attrset(ATTR_SELECTED);
113 mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
114 chunk_info[i]->offset, chunk_info[i]->size,
115 chunk_info[i]->end, chunk_info[i]->name,
117 slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
118 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
119 if (i == current_chunk)
125 print_command_summary()
127 mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
128 mvprintw(16, 0, "A = Use Entire Disk B = Bad Block Scan C = Create Slice");
129 mvprintw(17, 0, "D = Delete Slice G = Set Drive Geometry S = Set Bootable");
130 mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
132 mvprintw(18, 48, "W = Write Changes");
133 mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
138 getBootMgr(char *dname)
140 #ifndef __alpha__ /* only meaningful on x86 */
141 extern u_char mbr[], boot0[];
146 cp = variable_get(VAR_BOOTMGR);
148 /* Figure out what kind of MBR the user wants */
149 sprintf(str, "Install Boot Manager for drive %s?", dname);
150 MenuMBRType.title = str;
151 i = dmenuOpenSimple(&MenuMBRType, FALSE);
154 if (!strncmp(cp, "boot", 4))
156 else if (!strcmp(cp, "standard"))
179 diskGetSelectCount(Device ***devs)
185 cp = variable_get(VAR_DISK);
186 dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
187 cnt = deviceCount(dp);
190 for (i = 0, enabled = 0; i < cnt; i++) {
198 diskPartition(Device *dev)
205 WINDOW *w = savescr();
206 Disk *d = (Disk *)dev->private;
209 keypad(stdscr, TRUE);
211 /* Flush both the dialog and curses library views of the screen
212 since we don't always know who called us */
213 dialog_clear_norefresh(), clear();
216 /* Set up the chunk array */
220 char *val, geometry[80];
222 /* Now print our overall state */
225 print_command_summary();
227 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
236 /* Get command character */
238 switch (toupper(key)) {
239 case '\014': /* ^L (redraw) */
244 case '\020': /* ^P */
247 if (current_chunk != 0)
251 case '\016': /* ^N */
256 if (chunk_info[current_chunk + 1])
265 while (chunk_info[current_chunk + 1])
271 systemDisplayHelp("slice");
278 #else /* The rest is only relevant on x86 */
279 cp = variable_get(VAR_DEDICATE_DISK);
280 if (cp && !strcasecmp(cp, "always"))
283 rv = msgYesNo("Do you want to do this with a true partition entry\n"
284 "so as to remain cooperative with any future possible\n"
285 "operating systems on the drive(s)?\n"
286 "(See also the section about ``dangerously dedicated''\n"
287 "disks in the FreeBSD FAQ.)");
293 variable_set2(DISK_PARTITIONED, "yes", 0);
299 if (chunk_info[current_chunk]->type != freebsd)
300 msg = "Can only scan for bad blocks in FreeBSD slice.";
301 else if (strncmp(d->name, "sd", 2) ||
302 strncmp(d->name, "da", 2) ||
303 !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
304 "Are you sure you want to do this on a SCSI disk?")) {
305 if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
306 chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
308 chunk_info[current_chunk]->flags |= CHUNK_BAD144;
314 if (chunk_info[current_chunk]->type != unused)
315 msg = "Slice in use, delete it first or move to an unused one.";
317 char *val, tmp[20], *cp;
319 chunk_e partitiontype;
321 snprintf(tmp, 20, "%lu", chunk_info[current_chunk]->size);
322 val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
323 "or append a trailing `M' for megabytes (e.g. 20M).");
324 if (val && (size = strtol(val, &cp, 0)) > 0) {
325 if (*cp && toupper(*cp) == 'M')
328 val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
329 "Pressing Enter will choose the default, a native FreeBSD\n"
330 "slice (type 165). You can choose other types, 6 for a\n"
331 "DOS partition or 131 for a Linux partition, for example.\n\n"
332 "Note: If you choose a non-FreeBSD partition type, it will not\n"
333 "be formatted or otherwise prepared, it will simply reserve space\n"
334 "for you to use another tool, such as DOS FORMAT, to later format\n"
335 "and use the partition.");
336 if (val && (subtype = strtol(val, NULL, 0)) > 0) {
338 partitiontype = freebsd;
339 else if (subtype == 6)
342 partitiontype = unknown;
344 if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
348 Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
349 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
350 variable_set2(DISK_PARTITIONED, "yes", 0);
360 if (chunk_info[current_chunk]->type == unused)
361 msg = "Slice is already unused!";
363 Delete_Chunk(d, chunk_info[current_chunk]);
364 variable_set2(DISK_PARTITIONED, "yes", 0);
370 if (chunk_info[current_chunk]->type == unused)
371 msg = "Slice is currently unused (use create instead)";
375 chunk_e partitiontype;
376 WINDOW *save = savescr();
379 val = msgGetInput(tmp, "New partition type:\n\n"
380 "Pressing Enter will choose the default, a native FreeBSD\n"
381 "slice (type 165). Other popular values are 6 for\n"
382 "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
383 "130 for a Linux swap partition.\n\n"
384 "Note: If you choose a non-FreeBSD partition type, it will not\n"
385 "be formatted or otherwise prepared, it will simply reserve space\n"
386 "for you to use another tool, such as DOS format, to later format\n"
387 "and actually use the partition.");
388 if (val && (subtype = strtol(val, NULL, 0)) > 0) {
390 partitiontype = freebsd;
391 else if (subtype == 6)
394 partitiontype = unknown;
395 chunk_info[current_chunk]->type = partitiontype;
396 chunk_info[current_chunk]->subtype = subtype;
403 snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
404 val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
405 "Don't forget to use the two slash (/) separator characters!\n"
406 "It's not possible to parse the field without them.");
409 nc = strtol(val, &val, 0);
410 nh = strtol(val + 1, &val, 0);
411 ns = strtol(val + 1, 0, 0);
412 Set_Bios_Geom(d, nc, nh, ns);
419 chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
423 if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
424 msgConfirm("You've already written this information out - you\n"
427 else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
430 sstrncpy(cp, d->name, sizeof cp);
431 Free_Disk(dev->private);
434 msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
436 variable_unset(DISK_PARTITIONED);
437 variable_unset(DISK_LABELLED);
445 if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n"
446 "installation. If you are installing FreeBSD for the first time\n"
447 "then you should simply type Q when you're finished here and your\n"
448 "changes will be committed in one batch automatically at the end of\n"
449 "these questions. If you're adding a disk, you should NOT write\n"
450 "from this screen, you should do it from the label editor.\n\n"
451 "Are you absolutely sure you want to do this now?")) {
452 variable_set2(DISK_PARTITIONED, "yes", 0);
454 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
455 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
456 * booteasy or a "standard" MBR -- both would be fatal in this case.
459 * Don't offer to update the MBR on this disk if the first "real" chunk looks like
460 * a FreeBSD "all disk" partition, or the disk is entirely FreeBSD.
462 if (((d->chunks->part->type != freebsd) || (d->chunks->part->offset > 1)))
463 mbrContents = getBootMgr(d->name);
466 Set_Boot_Mgr(d, mbrContents);
468 if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
469 msgConfirm("Disk partition write returned an error status!");
471 msgConfirm("Wrote FDISK partition information out successfully.");
477 if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
478 "No seat belts whatsoever are provided!")) {
482 variable_set2(DISK_PARTITIONED, "yes", 0);
486 msg = "Wise choice!";
490 case '\033': /* ESC */
493 /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
494 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
495 * booteasy or a "standard" MBR -- both would be fatal in this case.
498 if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
499 && (mbrContents = getBootMgr(d->name)) != NULL)
500 Set_Boot_Mgr(d, mbrContents);
503 * Don't offer to update the MBR on this disk if the first "real" chunk looks like
504 * a FreeBSD "all disk" partition, or the disk is entirely FreeBSD.
506 if (((d->chunks->part->type != freebsd) || (d->chunks->part->offset > 1)) &&
507 (mbrContents = getBootMgr(d->name)) != NULL)
508 Set_Boot_Mgr(d, mbrContents);
514 msg = "Type F1 or ? for help";
520 char buf[FILENAME_MAX];
522 dialog_clear_norefresh();
523 use_helpline("Press F1 to read more about disk slices.");
524 use_helpfile(systemHelpFile("partition", buf));
525 if (!variable_get(VAR_NO_WARN))
526 dialog_mesgbox("Disk slicing warning:", p, -1, -1);
533 bootalloc(char *name)
535 char buf[FILENAME_MAX];
538 snprintf(buf, sizeof buf, "/boot/%s", name);
539 if (stat(buf, &sb) != -1) {
542 fd = open(buf, O_RDONLY);
546 cp = malloc(sb.st_size);
547 if (read(fd, cp, sb.st_size) != sb.st_size) {
550 msgDebug("bootalloc: couldn't read %d bytes from %s\n", sb.st_size, buf);
556 msgDebug("bootalloc: couldn't open %s\n", buf);
559 msgDebug("bootalloc: can't stat %s\n", buf);
564 partitionHook(dialogMenuItem *selected)
566 Device **devs = NULL;
568 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
570 msgConfirm("Unable to find disk %s!", selected->prompt);
571 return DITEM_FAILURE;
573 /* Toggle enabled status? */
574 if (!devs[0]->enabled) {
575 devs[0]->enabled = TRUE;
576 diskPartition(devs[0]);
579 devs[0]->enabled = FALSE;
580 return DITEM_SUCCESS | DITEM_RESTORE;
584 partitionCheck(dialogMenuItem *selected)
586 Device **devs = NULL;
588 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
589 if (!devs || devs[0]->enabled == FALSE)
595 diskPartitionEditor(dialogMenuItem *self)
601 cnt = diskGetSelectCount(&devs);
602 devcnt = deviceCount(devs);
604 msgConfirm("No disks found! Please verify that your disk controller is being\n"
605 "properly probed at boot time. See the Hardware Guide on the\n"
606 "Documentation menu for clues on diagnosing this type of problem.");
607 return DITEM_FAILURE;
610 /* Some are already selected */
611 for (i = 0; i < devcnt; i++) {
612 if (devs[i]->enabled) {
613 if (variable_get(VAR_NONINTERACTIVE))
614 diskPartitionNonInteractive(devs[i]);
616 diskPartition(devs[i]);
621 /* No disks are selected, fall-back case now */
623 devs[0]->enabled = TRUE;
624 if (variable_get(VAR_NONINTERACTIVE))
625 diskPartitionNonInteractive(devs[0]);
627 diskPartition(devs[0]);
628 return DITEM_SUCCESS;
631 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
633 msgConfirm("No devices suitable for installation found!\n\n"
634 "Please verify that your disk controller (and attached drives)\n"
635 "were detected properly. This can be done by pressing the\n"
636 "[Scroll Lock] key and using the Arrow keys to move back to\n"
637 "the boot messages. Press [Scroll Lock] again to return.");
638 return DITEM_FAILURE;
641 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
644 return i | DITEM_RESTORE;
647 return DITEM_SUCCESS;
651 diskPartitionWrite(dialogMenuItem *self)
657 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
659 msgConfirm("Unable to find any disks to write to??");
660 return DITEM_FAILURE;
663 msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
664 cp = variable_get(DISK_PARTITIONED);
665 if (cp && !strcmp(cp, "written"))
666 return DITEM_SUCCESS;
668 for (i = 0; devs[i]; i++) {
670 Disk *d = (Disk *)devs[i]->private;
671 static u_char *boot1;
673 static u_char *boot2;
676 if (!devs[i]->enabled)
680 if (!boot1) boot1 = bootalloc("boot1");
681 Set_Boot_Blocks(d, boot1, NULL);
683 if (!boot1) boot1 = bootalloc("boot1");
684 if (!boot2) boot2 = bootalloc("boot2");
685 Set_Boot_Blocks(d, boot1, boot2);
688 msgNotify("Writing partition information to drive %s", d->name);
689 if (!Fake && Write_Disk(d)) {
690 msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
691 return DITEM_FAILURE;
694 /* If we've been through here before, we don't need to do the rest */
695 if (cp && !strcmp(cp, "written"))
696 return DITEM_SUCCESS;
698 /* Now scan for bad blocks, if necessary */
699 for (c1 = d->chunks->part; c1; c1 = c1->next) {
700 if (c1->flags & CHUNK_BAD144) {
703 msgNotify("Running bad block scan on slice %s", c1->name);
705 ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
707 msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
708 ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
710 msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
715 /* Now it's not "yes", but "written" */
716 variable_set2(DISK_PARTITIONED, "written", 0);
717 return DITEM_SUCCESS;
720 /* Partition a disk based wholly on which variables are set */
722 diskPartitionNonInteractive(Device *dev)
725 int i, sz, all_disk = 0;
727 Disk *d = (Disk *)dev->private;
730 cp = variable_get(VAR_GEOMETRY);
732 msgDebug("Setting geometry from script to: %s\n", cp);
733 d->bios_cyl = strtol(cp, &cp, 0);
734 d->bios_hd = strtol(cp + 1, &cp, 0);
735 d->bios_sect = strtol(cp + 1, 0, 0);
738 cp = variable_get(VAR_PARTITION);
740 if (!strcmp(cp, "free")) {
741 /* Do free disk space case */
742 for (i = 0; chunk_info[i]; i++) {
743 /* If a chunk is at least 10MB in size, use it. */
744 if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
745 Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3,
746 (chunk_info[i]->flags & CHUNK_ALIGN));
747 variable_set2(DISK_PARTITIONED, "yes", 0);
751 if (!chunk_info[i]) {
753 msgConfirm("Unable to find any free space on this disk!");
757 else if (!strcmp(cp, "all")) {
758 /* Do all disk space case */
759 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
761 All_FreeBSD(d, FALSE);
763 else if (!strcmp(cp, "exclusive")) {
764 /* Do really-all-the-disk-space case */
765 msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
767 All_FreeBSD(d, all_disk = TRUE);
769 else if ((sz = strtol(cp, &cp, 0))) {
770 /* Look for sz bytes free */
771 if (*cp && toupper(*cp) == 'M')
773 for (i = 0; chunk_info[i]; i++) {
774 /* If a chunk is at least sz MB, use it. */
775 if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
776 Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN));
777 variable_set2(DISK_PARTITIONED, "yes", 0);
781 if (!chunk_info[i]) {
783 msgConfirm("Unable to find %d free blocks on this disk!", sz);
787 else if (!strcmp(cp, "existing")) {
788 /* Do existing FreeBSD case */
789 for (i = 0; chunk_info[i]; i++) {
790 if (chunk_info[i]->type == freebsd)
793 if (!chunk_info[i]) {
795 msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
801 msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
805 mbrContents = getBootMgr(d->name);
806 Set_Boot_Mgr(d, mbrContents);
808 variable_set2(DISK_PARTITIONED, "yes", 0);