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>
42 #include <sys/param.h>
43 #include <sys/sysctl.h>
45 #define AUTO_HOME 0 /* do not create /home automatically */
48 * Everything to do with editing the contents of disk labels.
51 /* A nice message we use a lot in the disklabel editor */
52 #define MSG_NOT_APPLICABLE "That option is not applicable here"
54 /* Where to start printing the freebsd slices */
55 #define CHUNK_SLICE_START_ROW 2
56 #define CHUNK_PART_START_ROW 11
58 /* The smallest filesystem we're willing to create */
59 #define FS_MIN_SIZE ONE_MEG
62 * Minimum partition sizes
64 #if defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
65 #define ROOT_MIN_SIZE 280
67 #define ROOT_MIN_SIZE 180
69 #define SWAP_MIN_SIZE 32
70 #define USR_MIN_SIZE 160
71 #define VAR_MIN_SIZE 20
72 #define TMP_MIN_SIZE 20
73 #define HOME_MIN_SIZE 20
76 * Swap size limit for auto-partitioning (4G).
78 #define SWAP_AUTO_LIMIT_SIZE 4096
81 * Default partition sizes. If we do not have sufficient disk space
82 * for this configuration we scale things relative to the NOM vs DEFAULT
83 * sizes. If the disk is larger then /home will get any remaining space.
85 #define ROOT_DEFAULT_SIZE 1024
86 #define USR_DEFAULT_SIZE 8192
87 #define VAR_DEFAULT_SIZE 4096
88 #define TMP_DEFAULT_SIZE 1024
89 #define HOME_DEFAULT_SIZE USR_DEFAULT_SIZE
92 * Nominal partition sizes. These are used to scale the default sizes down
93 * when we have insufficient disk space. If this isn't sufficient we scale
94 * down using the MIN sizes instead.
96 #define ROOT_NOMINAL_SIZE 512
97 #define USR_NOMINAL_SIZE 1536
98 #define VAR_NOMINAL_SIZE 512
99 #define TMP_NOMINAL_SIZE 128
100 #define HOME_NOMINAL_SIZE USR_NOMINAL_SIZE
102 /* The bottom-most row we're allowed to scribble on */
103 #define CHUNK_ROW_MAX 16
106 /* All the chunks currently displayed on the screen */
110 } label_chunk_info[MAX_CHUNKS + 1];
113 /*** with this value we try to track the most recently added label ***/
114 static int label_focus = 0, pslice_focus = 0;
116 static int diskLabel(Device *dev);
117 static int diskLabelNonInteractive(Device *dev);
118 static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
121 labelHook(dialogMenuItem *selected)
123 Device **devs = NULL;
125 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
127 msgConfirm("Unable to find disk %s!", selected->prompt);
128 return DITEM_FAILURE;
130 /* Toggle enabled status? */
131 if (!devs[0]->enabled) {
132 devs[0]->enabled = TRUE;
136 devs[0]->enabled = FALSE;
137 return DITEM_SUCCESS;
141 labelCheck(dialogMenuItem *selected)
143 Device **devs = NULL;
145 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
146 if (!devs || devs[0]->enabled == FALSE)
152 diskLabelEditor(dialogMenuItem *self)
159 cnt = diskGetSelectCount(&devs);
161 msgConfirm("No disks found! Please verify that your disk controller is being\n"
162 "properly probed at boot time. See the Hardware Guide on the\n"
163 "Documentation menu for clues on diagnosing this type of problem.");
164 return DITEM_FAILURE;
167 /* Some are already selected */
168 if (variable_get(VAR_NONINTERACTIVE) &&
169 !variable_get(VAR_DISKINTERACTIVE))
170 i = diskLabelNonInteractive(NULL);
175 /* No disks are selected, fall-back case now */
176 cnt = deviceCount(devs);
178 devs[0]->enabled = TRUE;
179 if (variable_get(VAR_NONINTERACTIVE) &&
180 !variable_get(VAR_DISKINTERACTIVE))
181 i = diskLabelNonInteractive(devs[0]);
183 i = diskLabel(devs[0]);
186 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
188 msgConfirm("No devices suitable for installation found!\n\n"
189 "Please verify that your disk controller (and attached drives)\n"
190 "were detected properly. This can be done by pressing the\n"
191 "[Scroll Lock] key and using the Arrow keys to move back to\n"
192 "the boot messages. Press [Scroll Lock] again to return.");
196 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
201 if (DITEM_STATUS(i) != DITEM_FAILURE) {
202 if (variable_cmp(DISK_LABELLED, "written"))
203 variable_set2(DISK_LABELLED, "yes", 0);
209 diskLabelCommit(dialogMenuItem *self)
215 if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
218 msgConfirm("You must assign disk labels before this option can be used.");
221 /* The routine will guard against redundant writes, just as this one does */
222 else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
224 else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
227 msgInfo("All filesystem information written successfully.");
228 variable_set2(DISK_LABELLED, "written", 0);
234 /* See if we're already using a desired partition name */
236 check_conflict(char *name)
240 for (i = 0; label_chunk_info[i].c; i++)
241 if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT
242 || label_chunk_info[i].type == PART_EFI) && label_chunk_info[i].c->private_data
243 && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
248 /* How much space is in this FreeBSD slice? */
250 space_free(struct chunk *c)
253 daddr_t sz = c->size;
255 for (c1 = c->part; c1; c1 = c1->next) {
256 if (c1->type != unused)
260 msgFatal("Partitions are larger than actual chunk??");
264 /* Snapshot the current situation into the displayed chunks structure */
266 record_label_chunks(Device **devs, Device *dev)
269 struct chunk *c1, *c2;
273 /* First buzz through and pick up the FreeBSD slices */
274 for (i = 0; devs[i]; i++) {
275 if ((dev && devs[i] != dev) || !devs[i]->enabled)
277 d = (Disk *)devs[i]->private;
279 msgFatal("No chunk list found for %s!", d->name);
282 label_chunk_info[j].type = PART_SLICE;
283 label_chunk_info[j].c = d->chunks;
287 /* Put the slice entries first */
288 for (c1 = d->chunks->part; c1; c1 = c1->next) {
289 if (c1->type == freebsd) {
290 label_chunk_info[j].type = PART_SLICE;
291 label_chunk_info[j].c = c1;
295 if (c1->type == apple) {
296 label_chunk_info[j].type = PART_SLICE;
297 label_chunk_info[j].c = c1;
304 /* Now run through again and get the FreeBSD partition entries */
305 for (i = 0; devs[i]; i++) {
306 if (!devs[i]->enabled)
308 d = (Disk *)devs[i]->private;
309 /* Then buzz through and pick up the partitions */
310 for (c1 = d->chunks->part; c1; c1 = c1->next) {
311 if (c1->type == freebsd) {
312 for (c2 = c1->part; c2; c2 = c2->next) {
313 if (c2->type == part) {
314 if (c2->subtype == FS_SWAP)
315 label_chunk_info[j].type = PART_SWAP;
317 label_chunk_info[j].type = PART_FILESYSTEM;
318 label_chunk_info[j].c = c2;
323 else if (c1->type == fat) {
324 label_chunk_info[j].type = PART_FAT;
325 label_chunk_info[j].c = c1;
329 else if (c1->type == efi) {
330 label_chunk_info[j].type = PART_EFI;
331 label_chunk_info[j].c = c1;
334 else if (c1->type == part) {
335 if (c1->subtype == FS_SWAP)
336 label_chunk_info[j].type = PART_SWAP;
338 label_chunk_info[j].type = PART_FILESYSTEM;
339 label_chunk_info[j].c = c1;
344 else if (c1->type == apple) {
345 for (c2 = c1->part; c2; c2 = c2->next) {
346 if (c2->type == part) {
347 if (c2->subtype == FS_SWAP)
348 label_chunk_info[j].type = PART_SWAP;
350 label_chunk_info[j].type = PART_FILESYSTEM;
351 label_chunk_info[j].c = c2;
359 label_chunk_info[j].c = NULL;
361 here = j ? j - 1 : 0;
365 /* A new partition entry */
367 new_part(PartType type, char *mpoint, Boolean newfs)
372 mpoint = (type == PART_EFI) ? "/efi" : "/change_me";
374 pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
375 sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
377 pi->do_newfs = newfs;
379 if (type == PART_EFI) {
380 pi->newfs_type = NEWFS_MSDOS;
382 pi->newfs_type = NEWFS_UFS;
383 strcpy(pi->newfs_data.newfs_ufs.user_options, "");
384 pi->newfs_data.newfs_ufs.acls = FALSE;
385 pi->newfs_data.newfs_ufs.multilabel = FALSE;
386 pi->newfs_data.newfs_ufs.softupdates = strcmp(mpoint, "/");
387 pi->newfs_data.newfs_ufs.ufs1 = FALSE;
393 /* Get the mountpoint for a partition and save it away */
395 get_mountpoint(PartType type, struct chunk *old)
401 if (old && old->private_data)
402 tmp = old->private_data;
405 val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
406 val = msgGetInput(val, "Please specify a mount point for the partition");
411 free(old->private_data);
412 old->private_data = NULL;
417 /* Is it just the same value? */
418 if (tmp && !strcmp(tmp->mountpoint, val))
421 /* Did we use it already? */
422 if (check_conflict(val)) {
423 msgConfirm("You already have a mount point for %s assigned!", val);
429 msgConfirm("Mount point must start with a / character");
433 /* Is it going to be mounted on root? */
434 if (!strcmp(val, "/")) {
436 old->flags |= CHUNK_IS_ROOT;
439 old->flags &= ~CHUNK_IS_ROOT;
443 newfs = tmp->do_newfs;
446 val = string_skipwhite(string_prune(val));
447 tmp = new_part(type, val, newfs);
449 old->private_data = tmp;
450 old->private_free = safe_free;
455 /* Get the type of the new partiton */
457 get_partition_type(void)
461 static unsigned char *fs_types[] = {
463 "EFI", "An EFI system partition",
465 "FS", "A file system",
466 "Swap", "A swap partition.",
468 WINDOW *w = savescr();
470 i = dialog_menu("Please choose a partition type",
471 "If you want to use this partition for swap space, select Swap.\n"
472 "If you want to put a filesystem on it, choose FS.",
479 fs_types, selection, NULL, NULL);
483 if (!strcmp(selection, "EFI"))
486 if (!strcmp(selection, "FS"))
487 return PART_FILESYSTEM;
488 else if (!strcmp(selection, "Swap"))
494 /* If the user wants a special newfs command for this, set it */
496 getNewfsCmd(PartInfo *p)
498 char buffer[NEWFS_CMD_ARGS_MAX];
501 switch (p->newfs_type) {
503 snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
504 NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ? "-U" : "",
505 p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
506 p->newfs_data.newfs_ufs.user_options);
509 snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
512 strcpy(buffer, p->newfs_data.newfs_custom.command);
516 val = msgGetInput(buffer,
517 "Please enter the newfs command and options you'd like to use in\n"
518 "creating this file system.");
520 p->newfs_type = NEWFS_CUSTOM;
521 strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
526 getNewfsOptionalArguments(PartInfo *p)
528 char buffer[NEWFS_CMD_ARGS_MAX];
531 /* Must be UFS, per argument checking in I/O routines. */
533 strlcpy(buffer, p->newfs_data.newfs_ufs.user_options,
535 val = msgGetInput(buffer,
536 "Please enter any additional UFS newfs options you'd like to\n"
537 "use in creating this file system.");
539 strlcpy(p->newfs_data.newfs_ufs.user_options, val,
543 #define MAX_MOUNT_NAME 9
545 #define PART_PART_COL 0
546 #define PART_MOUNT_COL 10
547 #define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
548 #define PART_NEWFS_COL (PART_SIZE_COL + 8)
551 #define TOTAL_AVAIL_LINES (10)
552 #define PSLICE_SHOWABLE (4)
555 /* stick this all up on the screen */
557 print_label_chunks(void)
559 int i, j, srow, prow, pcol;
562 int ChunkPartStartRow;
565 /********************************************************/
566 /*** These values are for controling screen resources ***/
567 /*** Each label line holds up to 2 labels, so beware! ***/
568 /*** strategy will be to try to always make sure the ***/
569 /*** highlighted label is in the active display area. ***/
570 /********************************************************/
571 int pslice_max, label_max;
572 int pslice_count, label_count, label_focus_found, pslice_focus_found;
575 mvaddstr(0, 25, "FreeBSD Disklabel Editor");
578 /*** Count the number of parition slices ***/
580 for (i = 0; label_chunk_info[i].c ; i++) {
581 if (label_chunk_info[i].type == PART_SLICE)
584 pslice_max = pslice_count;
586 /*** 4 line max for partition slices ***/
587 if (pslice_max > PSLICE_SHOWABLE) {
588 pslice_max = PSLICE_SHOWABLE;
590 ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
592 /*** View partition slices modulo pslice_max ***/
593 label_max = TOTAL_AVAIL_LINES - pslice_max;
595 for (i = 0; i < 2; i++) {
596 mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
597 mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
599 mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
600 mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
602 mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
603 mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
605 mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
606 mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
608 srow = CHUNK_SLICE_START_ROW;
612 /*** these variables indicate that the focused item is shown currently ***/
613 label_focus_found = 0;
614 pslice_focus_found = 0;
618 mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
619 mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
621 ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
624 /*** wrefresh(ChunkWin); ***/
626 for (i = 0; label_chunk_info[i].c; i++) {
627 /* Is it a slice entry displayed at the top? */
628 if (label_chunk_info[i].type == PART_SLICE) {
629 /*** This causes the new pslice to replace the previous display ***/
630 /*** focus must remain on the most recently active pslice ***/
631 if (pslice_count == pslice_max) {
632 if (pslice_focus_found) {
633 /*** This is where we can mark the more following ***/
635 mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
640 /*** this is where we set the more previous ***/
642 mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
645 srow = CHUNK_SLICE_START_ROW;
649 sz = space_free(label_chunk_info[i].c);
651 attrset(ATTR_SELECTED);
652 if (i == pslice_focus)
653 pslice_focus_found = -1;
655 if (label_chunk_info[i].c->type == whole) {
656 if (sz >= 100 * ONE_GIG)
658 "Disk: %s\t\tFree: %jd blocks (%jdGB)",
659 label_chunk_info[i].c->disk->name, (intmax_t)sz,
660 (intmax_t)(sz / ONE_GIG));
663 "Disk: %s\t\tFree: %jd blocks (%jdMB)",
664 label_chunk_info[i].c->disk->name, (intmax_t)sz,
665 (intmax_t)(sz / ONE_MEG));
667 if (sz >= 100 * ONE_GIG)
669 "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
670 label_chunk_info[i].c->disk->name,
671 label_chunk_info[i].c->name,
672 (intmax_t)sz, (intmax_t)(sz / ONE_GIG));
675 "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
676 label_chunk_info[i].c->disk->name,
677 label_chunk_info[i].c->name,
678 (intmax_t)sz, (intmax_t)(sz / ONE_MEG));
686 /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
688 char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
691 * We copy this into a blank-padded string so that it looks like
692 * a solid bar in reverse-video
694 memset(onestr, ' ', PART_OFF - 1);
695 onestr[PART_OFF - 1] = '\0';
697 /*** Track how many labels have been displayed ***/
698 if (label_count == ((label_max - 1 ) * 2)) {
699 if (label_focus_found) {
709 /* Go for two columns if we've written one full columns worth */
710 /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
711 if (label_count == label_max - 1) {
715 memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
716 /* If it's a filesystem, display the mountpoint */
717 if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
718 || label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
719 mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
720 else if (label_chunk_info[i].type == PART_SWAP)
723 mountpoint = "<none>";
725 /* Now display the newfs field */
726 if (label_chunk_info[i].type == PART_FAT)
727 strcpy(newfs, "DOS");
728 #if defined(__ia64__)
729 else if (label_chunk_info[i].type == PART_EFI) {
730 strcpy(newfs, "EFI");
731 if (label_chunk_info[i].c->private_data) {
733 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
734 strcat(newfs, pi->do_newfs ? " Y" : " N");
738 else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
739 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
741 switch (pi->newfs_type) {
743 strcpy(newfs, NEWFS_UFS_STRING);
744 if (pi->newfs_data.newfs_ufs.ufs1)
748 if (pi->newfs_data.newfs_ufs.softupdates)
755 strcpy(newfs, "FAT");
758 strcpy(newfs, "CUST");
761 strcat(newfs, pi->do_newfs ? " Y" : " N ");
763 else if (label_chunk_info[i].type == PART_SWAP)
764 strcpy(newfs, "SWAP");
767 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
768 onestr[PART_MOUNT_COL + j] = mountpoint[j];
769 if (label_chunk_info[i].c->size == 0)
770 snprintf(num, 10, "%5dMB", 0);
771 else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
772 snprintf(num, 10, "%5jdMB",
773 (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
775 snprintf(num, 10, "%5jdGB",
776 (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
777 memcpy(onestr + PART_SIZE_COL, num, strlen(num));
778 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
779 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
780 if (i == label_focus) {
781 label_focus_found = -1;
782 wattrset(ChunkWin, A_BOLD);
785 wattrset(ChunkWin, ATTR_SELECTED);
787 /*** lazy man's way of expensively padding this string ***/
788 while (strlen(onestr) < 37)
791 mvwaddstr(ChunkWin, prow, pcol, onestr);
792 wattrset(ChunkWin, A_NORMAL);
799 /*** this will erase all the extra stuff ***/
800 memset(clrmsg, ' ', 37);
803 while (pslice_count < pslice_max) {
804 mvprintw(srow++, 0, clrmsg);
808 while (label_count < (2 * (label_max - 1))) {
809 mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
811 if (prow == (label_max - 1)) {
821 print_command_summary(void)
823 mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
824 mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
826 mvprintw(18, 56, "W = Write");
827 mvprintw(19, 0, "N = Newfs Opts Q = Finish S = Toggle SoftUpdates Z = Custom Newfs");
828 mvprintw(20, 0, "T = Toggle Newfs U = Undo A = Auto Defaults R = Delete+Merge");
829 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
837 print_label_chunks();
841 diskLabel(Device *dev)
850 WINDOW *w = savescr();
856 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
858 msgConfirm("No disks found!");
860 return DITEM_FAILURE;
863 keypad(stdscr, TRUE);
864 record_label_chunks(devs, dev);
869 int rflags = DELCHUNK_NORMAL;
871 print_label_chunks();
872 print_command_summary();
874 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
886 switch (toupper(key)) {
888 static char _msg[40];
890 case '\014': /* ^L */
894 case '\020': /* ^P */
900 while (label_chunk_info[here + 1].c)
904 case '\016': /* ^N */
909 if (label_chunk_info[here + 1].c)
920 while (label_chunk_info[here + 1].c)
926 systemDisplayHelp("partition");
931 if (label_chunk_info[here].type == PART_FILESYSTEM) {
933 ((PartInfo *)label_chunk_info[here].c->private_data);
936 (pi->newfs_type == NEWFS_UFS)) {
937 pi->newfs_data.newfs_ufs.ufs1 = true;
939 msg = MSG_NOT_APPLICABLE;
941 msg = MSG_NOT_APPLICABLE;
946 if (label_chunk_info[here].type == PART_FILESYSTEM) {
948 ((PartInfo *)label_chunk_info[here].c->private_data);
951 (pi->newfs_type == NEWFS_UFS)) {
952 pi->newfs_data.newfs_ufs.ufs1 = false;
954 msg = MSG_NOT_APPLICABLE;
956 msg = MSG_NOT_APPLICABLE;
961 if (label_chunk_info[here].type != PART_SLICE) {
962 msg = "You can only do this in a disk slice (at top of screen)";
966 * Generate standard partitions automatically. If we do not
967 * have sufficient space we attempt to scale-down the size
968 * of the partitions within certain bounds.
974 for (perc = 100; perc > 0; perc -= 5) {
975 req = 0; /* reset for each loop */
976 if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
981 msgConfirm("%s", msg);
990 if (label_chunk_info[here].type != PART_SLICE) {
991 msg = "You can only do this in a master partition (see top of screen)";
994 sz = space_free(label_chunk_info[here].c);
995 if (sz <= FS_MIN_SIZE) {
996 msg = "Not enough space to create an additional FreeBSD partition";
1008 /* Always use the maximum size for apple partitions */
1009 if (label_chunk_info[here].c->type == apple)
1013 sprintf(osize, "%jd", (intmax_t)sz);
1014 val = msgGetInput(osize,
1015 "Please specify the partition size in blocks or append a trailing G for\n"
1017 "gigabytes, M for megabytes.\n"
1019 "gigabytes, M for megabytes, or C for cylinders.\n"
1021 "%jd blocks (%jdMB) are free.",
1022 (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1023 if (!val || (dsize = strtold(val, &cp)) <= 0) {
1029 if (toupper(*cp) == 'M')
1030 size = (daddr_t) (dsize * ONE_MEG);
1031 else if (toupper(*cp) == 'G')
1032 size = (daddr_t) (dsize * ONE_GIG);
1034 else if (toupper(*cp) == 'C')
1035 size = (daddr_t) dsize * (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1038 size = (daddr_t) dsize;
1040 size = (daddr_t) dsize;
1043 if (size < FS_MIN_SIZE) {
1044 msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1051 type = get_partition_type();
1052 if (type == PART_NONE) {
1058 if (type == PART_FILESYSTEM || type == PART_EFI) {
1059 if ((p = get_mountpoint(type, NULL)) == NULL) {
1064 else if (!strcmp(p->mountpoint, "/")) {
1065 if (type != PART_FILESYSTEM) {
1071 flags |= CHUNK_IS_ROOT;
1074 flags &= ~CHUNK_IS_ROOT;
1079 if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1080 msgConfirm("Warning: This is smaller than the recommended size for a\n"
1081 "root partition. For a variety of reasons, root\n"
1082 "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1084 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1085 label_chunk_info[here].c, size,
1087 (type == PART_EFI) ? efi : part,
1088 (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1090 part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1094 msgConfirm("Unable to create the partition. Too big?");
1099 tmp->private_data = p;
1100 tmp->private_free = safe_free;
1101 if (variable_cmp(DISK_LABELLED, "written"))
1102 variable_set2(DISK_LABELLED, "yes", 0);
1103 record_label_chunks(devs, dev);
1105 /* This is where we assign focus to new label so it shows. */
1109 for (i = 0; label_chunk_info[i].c; ++i) {
1110 if (label_chunk_info[i].c == tmp) {
1115 if (label_focus == -1)
1116 label_focus = i - 1;
1122 case 'R': /* recover space (delete w/ recover) */
1124 * Delete the partition w/ space recovery.
1126 rflags = DELCHUNK_RECOVER;
1128 case 'D': /* delete */
1129 if (label_chunk_info[here].type == PART_SLICE) {
1130 msg = MSG_NOT_APPLICABLE;
1133 else if (label_chunk_info[here].type == PART_FAT) {
1134 msg = "Use the Disk Partition Editor to delete DOS partitions";
1137 Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1138 if (variable_cmp(DISK_LABELLED, "written"))
1139 variable_set2(DISK_LABELLED, "yes", 0);
1140 record_label_chunks(devs, dev);
1143 case 'M': /* mount */
1144 switch(label_chunk_info[here].type) {
1146 msg = MSG_NOT_APPLICABLE;
1150 msg = "You don't need to specify a mountpoint for a swap partition.";
1155 case PART_FILESYSTEM:
1156 oldp = label_chunk_info[here].c->private_data;
1157 p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1160 p->do_newfs = FALSE;
1161 if ((label_chunk_info[here].type == PART_FAT ||
1162 label_chunk_info[here].type == PART_EFI) &&
1163 (!strcmp(p->mountpoint, "/") ||
1164 !strcmp(p->mountpoint, "/usr") ||
1165 !strcmp(p->mountpoint, "/var"))) {
1166 msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1167 strcpy(p->mountpoint, "/bogus");
1170 if (variable_cmp(DISK_LABELLED, "written"))
1171 variable_set2(DISK_LABELLED, "yes", 0);
1172 record_label_chunks(devs, dev);
1177 msgFatal("Bogus partition under cursor???");
1182 case 'N': /* Set newfs options */
1183 if (label_chunk_info[here].c->private_data &&
1184 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1185 getNewfsOptionalArguments(
1186 label_chunk_info[here].c->private_data);
1188 msg = MSG_NOT_APPLICABLE;
1192 case 'S': /* Toggle soft updates flag */
1193 if (label_chunk_info[here].type == PART_FILESYSTEM) {
1194 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1196 pi->newfs_type == NEWFS_UFS)
1197 pi->newfs_data.newfs_ufs.softupdates =
1198 !pi->newfs_data.newfs_ufs.softupdates;
1200 msg = MSG_NOT_APPLICABLE;
1203 msg = MSG_NOT_APPLICABLE;
1206 case 'T': /* Toggle newfs state */
1207 if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1208 label_chunk_info[here].type == PART_EFI) &&
1209 (label_chunk_info[here].c->private_data)) {
1210 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1212 label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1214 label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1216 label_chunk_info[here].c->private_data =
1217 new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1220 pi->newfs_type == NEWFS_UFS) {
1221 PartInfo *pi_new = label_chunk_info[here].c->private_data;
1223 pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1226 label_chunk_info[here].c->private_free = safe_free;
1227 if (variable_cmp(DISK_LABELLED, "written"))
1228 variable_set2(DISK_LABELLED, "yes", 0);
1231 msg = MSG_NOT_APPLICABLE;
1236 if (!variable_cmp(DISK_LABELLED, "written")) {
1237 msgConfirm("You've already written out your changes -\n"
1238 "it's too late to undo!");
1240 else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1241 variable_unset(DISK_PARTITIONED);
1242 variable_unset(DISK_LABELLED);
1243 for (i = 0; devs[i]; i++) {
1246 if (!devs[i]->enabled)
1248 else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1249 Free_Disk(devs[i]->private);
1250 devs[i]->private = d;
1252 diskPartition(devs[i]);
1256 record_label_chunks(devs, dev);
1262 if (!variable_cmp(DISK_LABELLED, "written")) {
1263 msgConfirm("You've already written out your changes - if you\n"
1264 "wish to overwrite them, you'll have to restart\n"
1265 "%s first.", ProgName);
1267 else if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
1268 "installation. If you are installing FreeBSD for the first time\n"
1269 "then you should simply type Q when you're finished here and your\n"
1270 "changes will be committed in one batch automatically at the end of\n"
1271 "these questions.\n\n"
1272 "Are you absolutely sure you want to do this now?")) {
1273 variable_set2(DISK_LABELLED, "yes", 0);
1274 diskLabelCommit(NULL);
1279 case 'Z': /* Set newfs command line */
1280 if (label_chunk_info[here].c->private_data &&
1281 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1282 getNewfsCmd(label_chunk_info[here].c->private_data);
1284 msg = MSG_NOT_APPLICABLE;
1290 if (!msgNoYes("Are you sure you want to go into Expert mode?\n\n"
1291 "This is an entirely undocumented feature which you are not\n"
1292 "expected to understand!")) {
1298 DialogActive = FALSE;
1299 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1301 msgConfirm("Can't find any disk devices!");
1304 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1305 if (devs[i]->enabled)
1306 slice_wizard(((Disk *)devs[i]->private));
1308 if (variable_cmp(DISK_LABELLED, "written"))
1309 variable_set2(DISK_LABELLED, "yes", 0);
1310 DialogActive = TRUE;
1311 record_label_chunks(devs, dev);
1315 msg = "A most prudent choice!";
1319 case '\033': /* ESC */
1326 sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1330 if (label_chunk_info[here].type == PART_SLICE)
1331 pslice_focus = here;
1336 return DITEM_SUCCESS;
1339 static __inline daddr_t
1340 requested_part_size(char *varName, daddr_t nom, int def, int perc)
1345 if ((cp = variable_get(varName)) != NULL)
1346 sz = strtoimax(cp, NULL, 0);
1348 sz = nom + (def - nom) * perc / 100;
1349 return(sz * ONE_MEG);
1353 * Attempt to auto-label the disk. 'perc' (0-100) scales
1354 * the size of the various partitions within appropriate
1355 * bounds (NOMINAL through DEFAULT sizes). The procedure
1356 * succeeds of NULL is returned. A non-null return message
1357 * is either a failure-status message (*req == 0), or
1358 * a confirmation requestor (*req == 1). *req is 0 on
1359 * entry to this call.
1361 * As a special exception to the usual sizing rules, /var is given
1362 * additional space equal to the amount of physical memory present
1363 * if perc == 100 in order to ensure that users with large hard drives
1364 * will have enough space to store a crashdump in /var/crash.
1366 * We autolabel the following partitions: /, swap, /var, /tmp, /usr,
1367 * and /home. /home receives any extra left over disk space.
1370 try_auto_label(Device **devs, Device *dev, int perc, int *req)
1373 Chunk *AutoHome, *AutoRoot, *AutoSwap;
1374 Chunk *AutoTmp, *AutoUsr, *AutoVar;
1379 unsigned long physmem;
1383 sz = space_free(label_chunk_info[here].c);
1384 if (sz <= FS_MIN_SIZE)
1385 return("Not enough free space to create a new partition in the slice");
1387 (void)checkLabels(FALSE);
1388 AutoHome = AutoRoot = AutoSwap = NULL;
1389 AutoTmp = AutoUsr = AutoVar = NULL;
1393 if (EfiChunk == NULL) {
1395 AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1396 label_chunk_info[here].c, sz, efi, 0, 0);
1397 if (AutoEfi == NULL) {
1399 msg = "Unable to create the EFI system partition. Too big?";
1402 AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1403 AutoEfi->private_free = safe_free;
1404 AutoEfi->flags |= CHUNK_NEWFS;
1405 record_label_chunks(devs, dev);
1409 if (RootChunk == NULL) {
1410 sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1412 AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1413 label_chunk_info[here].c, sz, part,
1414 FS_BSDFFS, CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1417 msg = "Unable to create the root partition. Too big?";
1420 AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1421 AutoRoot->private_free = safe_free;
1422 AutoRoot->flags |= CHUNK_NEWFS;
1423 record_label_chunks(devs, dev);
1425 if (SwapChunk == NULL) {
1426 sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1432 mib[1] = HW_PHYSMEM;
1433 size = sizeof physmem;
1434 sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1435 def = 2 * (int)(physmem / 512);
1436 if (def < SWAP_MIN_SIZE * ONE_MEG)
1437 def = SWAP_MIN_SIZE * ONE_MEG;
1438 if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1439 def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1440 nom = (int)(physmem / 512) / 8;
1441 sz = nom + (def - nom) * perc / 100;
1443 AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1444 label_chunk_info[here].c, sz, part,
1445 FS_SWAP, CHUNK_AUTO_SIZE);
1448 msg = "Unable to create the swap partition. Too big?";
1451 AutoSwap->private_data = 0;
1452 AutoSwap->private_free = safe_free;
1453 record_label_chunks(devs, dev);
1455 if (VarChunk == NULL) {
1456 /* Work out how much extra space we want for a crash dump */
1457 unsigned long crashdumpsz;
1460 mib[1] = HW_PHYSMEM;
1461 size = sizeof(physmem);
1462 sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1465 crashdumpsz = physmem / 1048576;
1469 sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, \
1470 VAR_DEFAULT_SIZE + crashdumpsz, perc);
1472 AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1473 label_chunk_info[here].c, sz, part,
1474 FS_BSDFFS, CHUNK_AUTO_SIZE);
1477 msg = "Not enough free space for /var - you will need to\n"
1478 "partition your disk manually with a custom install!";
1481 AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1482 AutoVar->private_free = safe_free;
1483 AutoVar->flags |= CHUNK_NEWFS;
1484 record_label_chunks(devs, dev);
1486 if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1487 sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1489 AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1490 label_chunk_info[here].c, sz, part,
1491 FS_BSDFFS, CHUNK_AUTO_SIZE);
1494 msg = "Not enough free space for /tmp - you will need to\n"
1495 "partition your disk manually with a custom install!";
1498 AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1499 AutoTmp->private_free = safe_free;
1500 AutoTmp->flags |= CHUNK_NEWFS;
1501 record_label_chunks(devs, dev);
1503 if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1504 sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1506 if (sz < space_free(label_chunk_info[here].c))
1507 sz = space_free(label_chunk_info[here].c);
1510 if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1512 msg = "Not enough free space for /usr - you will need to\n"
1513 "partition your disk manually with a custom install!";
1516 AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1517 label_chunk_info[here].c, sz, part,
1518 FS_BSDFFS, CHUNK_AUTO_SIZE);
1520 msg = "Unable to create the /usr partition. Not enough space?\n"
1521 "You will need to partition your disk manually with a custom install!";
1524 AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1525 AutoUsr->private_free = safe_free;
1526 AutoUsr->flags |= CHUNK_NEWFS;
1527 record_label_chunks(devs, dev);
1531 if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1532 sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1533 if (sz < space_free(label_chunk_info[here].c))
1534 sz = space_free(label_chunk_info[here].c);
1536 if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1538 msg = "Not enough free space for /home - you will need to\n"
1539 "partition your disk manually with a custom install!";
1543 AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1544 label_chunk_info[here].c, sz, part,
1545 FS_BSDFFS, CHUNK_AUTO_SIZE);
1547 msg = "Unable to create the /home partition. Not enough space?\n"
1548 "You will need to partition your disk manually with a custom install!";
1551 AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1552 AutoHome->private_free = safe_free;
1553 AutoHome->flags |= CHUNK_NEWFS;
1554 record_label_chunks(devs, dev);
1559 /* At this point, we're reasonably "labelled" */
1560 if (variable_cmp(DISK_LABELLED, "written"))
1561 variable_set2(DISK_LABELLED, "yes", 0);
1565 if (AutoRoot != NULL)
1566 Delete_Chunk(AutoRoot->disk, AutoRoot);
1567 if (AutoSwap != NULL)
1568 Delete_Chunk(AutoSwap->disk, AutoSwap);
1569 if (AutoVar != NULL)
1570 Delete_Chunk(AutoVar->disk, AutoVar);
1571 if (AutoTmp != NULL)
1572 Delete_Chunk(AutoTmp->disk, AutoTmp);
1573 if (AutoUsr != NULL)
1574 Delete_Chunk(AutoUsr->disk, AutoUsr);
1575 if (AutoHome != NULL)
1576 Delete_Chunk(AutoHome->disk, AutoHome);
1577 record_label_chunks(devs, dev);
1583 diskLabelNonInteractive(Device *dev)
1593 status = DITEM_SUCCESS;
1594 cp = variable_get(VAR_DISK);
1596 msgConfirm("diskLabel: No disk selected - can't label automatically.");
1597 return DITEM_FAILURE;
1599 devs = deviceFind(cp, DEVICE_TYPE_DISK);
1601 msgConfirm("diskLabel: No disk device %s found!", cp);
1602 return DITEM_FAILURE;
1607 d = devs[0]->private;
1608 record_label_chunks(devs, dev);
1609 for (i = 0; label_chunk_info[i].c; i++) {
1610 Chunk *c1 = label_chunk_info[i].c;
1612 if (label_chunk_info[i].type == PART_SLICE) {
1614 char typ[10], mpoint[50];
1617 for (entries = 1;; entries++) {
1620 snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1621 if ((cp = variable_get(name)) == NULL)
1623 if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1624 msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1625 status = DITEM_FAILURE;
1631 if (!strcmp(typ, "swap")) {
1633 strcpy(mpoint, "SWAP");
1635 type = PART_FILESYSTEM;
1636 if (!strcmp(mpoint, "/"))
1637 flags |= CHUNK_IS_ROOT;
1640 sz = space_free(c1);
1641 if (sz > space_free(c1)) {
1642 msgConfirm("Not enough free space to create partition: %s", mpoint);
1643 status = DITEM_FAILURE;
1646 if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1647 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1648 msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1649 status = DITEM_FAILURE;
1653 pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1654 tmp->private_free = safe_free;
1655 pi->newfs_data.newfs_ufs.softupdates = soft;
1656 if (!strcmp(typ, "ufs1"))
1657 pi->newfs_data.newfs_ufs.ufs1 = TRUE;
1662 /* Must be something we can set a mountpoint for */
1663 cp = variable_get(c1->name);
1665 char mpoint[50], do_newfs[8];
1666 Boolean newfs = FALSE;
1669 if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1670 msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1671 status = DITEM_FAILURE;
1674 newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1675 if (c1->private_data) {
1676 p = c1->private_data;
1677 p->do_newfs = newfs;
1678 strcpy(p->mountpoint, mpoint);
1681 c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1682 c1->private_free = safe_free;
1684 if (!strcmp(mpoint, "/"))
1685 c1->flags |= CHUNK_IS_ROOT;
1687 c1->flags &= ~CHUNK_IS_ROOT;
1691 if (status == DITEM_SUCCESS)
1692 variable_set2(DISK_LABELLED, "yes", 0);