5 * Jordan Hubbard. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer,
12 * verbatim and that no modifications are made prior to this
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include <sys/disklabel.h>
37 #include <sys/param.h>
38 #include <sys/sysctl.h>
40 #define AUTO_HOME 0 /* do not create /home automatically */
43 * Everything to do with editing the contents of disk labels.
46 /* A nice message we use a lot in the disklabel editor */
47 #define MSG_NOT_APPLICABLE "That option is not applicable here"
49 /* Where to start printing the freebsd slices */
50 #define CHUNK_SLICE_START_ROW 2
51 #define CHUNK_PART_START_ROW 11
53 /* The smallest filesystem we're willing to create */
54 #define FS_MIN_SIZE ONE_MEG
57 * Minimum partition sizes
59 #if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
60 #define ROOT_MIN_SIZE 128
62 #define ROOT_MIN_SIZE 118
64 #define SWAP_MIN_SIZE 32
65 #define USR_MIN_SIZE 128
66 #define VAR_MIN_SIZE 20
67 #define TMP_MIN_SIZE 20
68 #define HOME_MIN_SIZE 20
71 * Swap size limit for auto-partitioning (4G).
73 #define SWAP_AUTO_LIMIT_SIZE 4096
76 * Default partition sizes. If we do not have sufficient disk space
77 * for this configuration we scale things relative to the NOM vs DEFAULT
78 * sizes. If the disk is larger then /home will get any remaining space.
80 #define ROOT_DEFAULT_SIZE 512
81 #define USR_DEFAULT_SIZE 8192
82 #define VAR_DEFAULT_SIZE 1024
83 #define TMP_DEFAULT_SIZE 512
84 #define HOME_DEFAULT_SIZE USR_DEFAULT_SIZE
87 * Nominal partition sizes. These are used to scale the default sizes down
88 * when we have insufficient disk space. If this isn't sufficient we scale
89 * down using the MIN sizes instead.
91 #define ROOT_NOMINAL_SIZE 256
92 #define USR_NOMINAL_SIZE 1536
93 #define VAR_NOMINAL_SIZE 128
94 #define TMP_NOMINAL_SIZE 128
95 #define HOME_NOMINAL_SIZE USR_NOMINAL_SIZE
97 /* The bottom-most row we're allowed to scribble on */
98 #define CHUNK_ROW_MAX 16
101 /* All the chunks currently displayed on the screen */
105 } label_chunk_info[MAX_CHUNKS + 1];
108 /*** with this value we try to track the most recently added label ***/
109 static int label_focus = 0, pslice_focus = 0;
111 static int diskLabel(Device *dev);
112 static int diskLabelNonInteractive(Device *dev);
113 static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
116 labelHook(dialogMenuItem *selected)
118 Device **devs = NULL;
120 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
122 msgConfirm("Unable to find disk %s!", selected->prompt);
123 return DITEM_FAILURE;
125 /* Toggle enabled status? */
126 if (!devs[0]->enabled) {
127 devs[0]->enabled = TRUE;
131 devs[0]->enabled = FALSE;
132 return DITEM_SUCCESS;
136 labelCheck(dialogMenuItem *selected)
138 Device **devs = NULL;
140 devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
141 if (!devs || devs[0]->enabled == FALSE)
147 diskLabelEditor(dialogMenuItem *self)
154 cnt = diskGetSelectCount(&devs);
156 msgConfirm("No disks found! Please verify that your disk controller is being\n"
157 "properly probed at boot time. See the Hardware Guide on the\n"
158 "Documentation menu for clues on diagnosing this type of problem.");
159 return DITEM_FAILURE;
162 /* Some are already selected */
163 if (variable_get(VAR_NONINTERACTIVE) &&
164 !variable_get(VAR_DISKINTERACTIVE))
165 i = diskLabelNonInteractive(NULL);
170 /* No disks are selected, fall-back case now */
171 cnt = deviceCount(devs);
173 devs[0]->enabled = TRUE;
174 if (variable_get(VAR_NONINTERACTIVE) &&
175 !variable_get(VAR_DISKINTERACTIVE))
176 i = diskLabelNonInteractive(devs[0]);
178 i = diskLabel(devs[0]);
181 menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
183 msgConfirm("No devices suitable for installation found!\n\n"
184 "Please verify that your disk controller (and attached drives)\n"
185 "were detected properly. This can be done by pressing the\n"
186 "[Scroll Lock] key and using the Arrow keys to move back to\n"
187 "the boot messages. Press [Scroll Lock] again to return.");
191 i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
196 if (DITEM_STATUS(i) != DITEM_FAILURE) {
197 if (variable_cmp(DISK_LABELLED, "written"))
198 variable_set2(DISK_LABELLED, "yes", 0);
204 diskLabelCommit(dialogMenuItem *self)
210 if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
213 msgConfirm("You must assign disk labels before this option can be used.");
216 /* The routine will guard against redundant writes, just as this one does */
217 else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
219 else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
222 msgInfo("All filesystem information written successfully.");
223 variable_set2(DISK_LABELLED, "written", 0);
229 /* See if we're already using a desired partition name */
231 check_conflict(char *name)
235 for (i = 0; label_chunk_info[i].c; i++)
236 if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT
237 || label_chunk_info[i].type == PART_EFI) && label_chunk_info[i].c->private_data
238 && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
243 /* How much space is in this FreeBSD slice? */
245 space_free(struct chunk *c)
248 daddr_t sz = c->size;
250 for (c1 = c->part; c1; c1 = c1->next) {
251 if (c1->type != unused)
255 msgFatal("Partitions are larger than actual chunk??");
259 /* Snapshot the current situation into the displayed chunks structure */
261 record_label_chunks(Device **devs, Device *dev)
264 struct chunk *c1, *c2;
268 /* First buzz through and pick up the FreeBSD slices */
269 for (i = 0; devs[i]; i++) {
270 if ((dev && devs[i] != dev) || !devs[i]->enabled)
272 d = (Disk *)devs[i]->private;
274 msgFatal("No chunk list found for %s!", d->name);
277 label_chunk_info[j].type = PART_SLICE;
278 label_chunk_info[j].c = d->chunks;
282 /* Put the slice entries first */
283 for (c1 = d->chunks->part; c1; c1 = c1->next) {
284 if (c1->type == freebsd) {
285 label_chunk_info[j].type = PART_SLICE;
286 label_chunk_info[j].c = c1;
290 if (c1->type == apple) {
291 label_chunk_info[j].type = PART_SLICE;
292 label_chunk_info[j].c = c1;
299 /* Now run through again and get the FreeBSD partition entries */
300 for (i = 0; devs[i]; i++) {
301 if (!devs[i]->enabled)
303 d = (Disk *)devs[i]->private;
304 /* Then buzz through and pick up the partitions */
305 for (c1 = d->chunks->part; c1; c1 = c1->next) {
306 if (c1->type == freebsd) {
307 for (c2 = c1->part; c2; c2 = c2->next) {
308 if (c2->type == part) {
309 if (c2->subtype == FS_SWAP)
310 label_chunk_info[j].type = PART_SWAP;
312 label_chunk_info[j].type = PART_FILESYSTEM;
313 label_chunk_info[j].c = c2;
318 else if (c1->type == fat) {
319 label_chunk_info[j].type = PART_FAT;
320 label_chunk_info[j].c = c1;
324 else if (c1->type == efi) {
325 label_chunk_info[j].type = PART_EFI;
326 label_chunk_info[j].c = c1;
329 else if (c1->type == part) {
330 if (c1->subtype == FS_SWAP)
331 label_chunk_info[j].type = PART_SWAP;
333 label_chunk_info[j].type = PART_FILESYSTEM;
334 label_chunk_info[j].c = c1;
339 else if (c1->type == apple) {
340 for (c2 = c1->part; c2; c2 = c2->next) {
341 if (c2->type == part) {
342 if (c2->subtype == FS_SWAP)
343 label_chunk_info[j].type = PART_SWAP;
345 label_chunk_info[j].type = PART_FILESYSTEM;
346 label_chunk_info[j].c = c2;
354 label_chunk_info[j].c = NULL;
356 here = j ? j - 1 : 0;
360 /* A new partition entry */
362 new_part(PartType type, char *mpoint, Boolean newfs)
367 mpoint = (type == PART_EFI) ? "/efi" : "/change_me";
369 pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
370 sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
372 pi->do_newfs = newfs;
374 if (type == PART_EFI) {
375 pi->newfs_type = NEWFS_MSDOS;
377 pi->newfs_type = NEWFS_UFS;
378 strcpy(pi->newfs_data.newfs_ufs.user_options, "");
379 pi->newfs_data.newfs_ufs.acls = FALSE;
380 pi->newfs_data.newfs_ufs.multilabel = FALSE;
381 pi->newfs_data.newfs_ufs.softupdates = strcmp(mpoint, "/");
383 pi->newfs_data.newfs_ufs.ufs1 = TRUE;
385 pi->newfs_data.newfs_ufs.ufs1 = FALSE;
392 /* Get the mountpoint for a partition and save it away */
394 get_mountpoint(PartType type, struct chunk *old)
400 if (old && old->private_data)
401 tmp = old->private_data;
404 val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
405 val = msgGetInput(val, "Please specify a mount point for the partition");
410 free(old->private_data);
411 old->private_data = NULL;
416 /* Is it just the same value? */
417 if (tmp && !strcmp(tmp->mountpoint, val))
420 /* Did we use it already? */
421 if (check_conflict(val)) {
422 msgConfirm("You already have a mount point for %s assigned!", val);
428 msgConfirm("Mount point must start with a / character");
432 /* Is it going to be mounted on root? */
433 if (!strcmp(val, "/")) {
435 old->flags |= CHUNK_IS_ROOT;
438 old->flags &= ~CHUNK_IS_ROOT;
442 newfs = tmp->do_newfs;
445 val = string_skipwhite(string_prune(val));
446 tmp = new_part(type, val, newfs);
448 old->private_data = tmp;
449 old->private_free = safe_free;
454 /* Get the type of the new partiton */
456 get_partition_type(void)
460 static unsigned char *fs_types[] = {
462 "EFI", "An EFI system partition",
464 "FS", "A file system",
465 "Swap", "A swap partition.",
467 WINDOW *w = savescr();
469 i = dialog_menu("Please choose a partition type",
470 "If you want to use this partition for swap space, select Swap.\n"
471 "If you want to put a filesystem on it, choose FS.",
478 fs_types, selection, NULL, NULL);
482 if (!strcmp(selection, "EFI"))
485 if (!strcmp(selection, "FS"))
486 return PART_FILESYSTEM;
487 else if (!strcmp(selection, "Swap"))
493 /* If the user wants a special newfs command for this, set it */
495 getNewfsCmd(PartInfo *p)
497 char buffer[NEWFS_CMD_ARGS_MAX];
500 switch (p->newfs_type) {
502 snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
503 NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ? "-U" : "",
504 p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
505 p->newfs_data.newfs_ufs.user_options);
508 snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
511 strcpy(buffer, p->newfs_data.newfs_custom.command);
515 val = msgGetInput(buffer,
516 "Please enter the newfs command and options you'd like to use in\n"
517 "creating this file system.");
519 p->newfs_type = NEWFS_CUSTOM;
520 strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
525 getNewfsOptionalArguments(PartInfo *p)
527 char buffer[NEWFS_CMD_ARGS_MAX];
530 /* Must be UFS, per argument checking in I/O routines. */
532 strlcpy(buffer, p->newfs_data.newfs_ufs.user_options,
534 val = msgGetInput(buffer,
535 "Please enter any additional UFS newfs options you'd like to\n"
536 "use in creating this file system.");
538 strlcpy(p->newfs_data.newfs_ufs.user_options, val,
542 #define MAX_MOUNT_NAME 9
544 #define PART_PART_COL 0
545 #define PART_MOUNT_COL 10
546 #define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
547 #define PART_NEWFS_COL (PART_SIZE_COL + 8)
550 #define TOTAL_AVAIL_LINES (10)
551 #define PSLICE_SHOWABLE (4)
554 /* stick this all up on the screen */
556 print_label_chunks(void)
558 int i, j, srow, prow, pcol;
561 int ChunkPartStartRow;
564 /********************************************************/
565 /*** These values are for controling screen resources ***/
566 /*** Each label line holds up to 2 labels, so beware! ***/
567 /*** strategy will be to try to always make sure the ***/
568 /*** highlighted label is in the active display area. ***/
569 /********************************************************/
570 int pslice_max, label_max;
571 int pslice_count, label_count, label_focus_found, pslice_focus_found;
574 mvaddstr(0, 25, "FreeBSD Disklabel Editor");
577 /*** Count the number of parition slices ***/
579 for (i = 0; label_chunk_info[i].c ; i++) {
580 if (label_chunk_info[i].type == PART_SLICE)
583 pslice_max = pslice_count;
585 /*** 4 line max for partition slices ***/
586 if (pslice_max > PSLICE_SHOWABLE) {
587 pslice_max = PSLICE_SHOWABLE;
589 ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
591 /*** View partition slices modulo pslice_max ***/
592 label_max = TOTAL_AVAIL_LINES - pslice_max;
594 for (i = 0; i < 2; i++) {
595 mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
596 mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
598 mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
599 mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
601 mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
602 mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
604 mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
605 mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
607 srow = CHUNK_SLICE_START_ROW;
611 /*** these variables indicate that the focused item is shown currently ***/
612 label_focus_found = 0;
613 pslice_focus_found = 0;
617 mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
618 mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
620 ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
623 /*** wrefresh(ChunkWin); ***/
625 for (i = 0; label_chunk_info[i].c; i++) {
626 /* Is it a slice entry displayed at the top? */
627 if (label_chunk_info[i].type == PART_SLICE) {
628 /*** This causes the new pslice to replace the previous display ***/
629 /*** focus must remain on the most recently active pslice ***/
630 if (pslice_count == pslice_max) {
631 if (pslice_focus_found) {
632 /*** This is where we can mark the more following ***/
634 mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
639 /*** this is where we set the more previous ***/
641 mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
644 srow = CHUNK_SLICE_START_ROW;
648 sz = space_free(label_chunk_info[i].c);
650 attrset(ATTR_SELECTED);
651 if (i == pslice_focus)
652 pslice_focus_found = -1;
654 if (label_chunk_info[i].c->type == whole) {
655 if (sz >= 100 * ONE_GIG)
657 "Disk: %s\t\tFree: %jd blocks (%jdGB)",
658 label_chunk_info[i].c->disk->name, (intmax_t)sz,
659 (intmax_t)(sz / ONE_GIG));
662 "Disk: %s\t\tFree: %jd blocks (%jdMB)",
663 label_chunk_info[i].c->disk->name, (intmax_t)sz,
664 (intmax_t)(sz / ONE_MEG));
666 if (sz >= 100 * ONE_GIG)
668 "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
669 label_chunk_info[i].c->disk->name,
670 label_chunk_info[i].c->name,
671 (intmax_t)sz, (intmax_t)(sz / ONE_GIG));
674 "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
675 label_chunk_info[i].c->disk->name,
676 label_chunk_info[i].c->name,
677 (intmax_t)sz, (intmax_t)(sz / ONE_MEG));
685 /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
687 char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
690 * We copy this into a blank-padded string so that it looks like
691 * a solid bar in reverse-video
693 memset(onestr, ' ', PART_OFF - 1);
694 onestr[PART_OFF - 1] = '\0';
696 /*** Track how many labels have been displayed ***/
697 if (label_count == ((label_max - 1 ) * 2)) {
698 if (label_focus_found) {
708 /* Go for two columns if we've written one full columns worth */
709 /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
710 if (label_count == label_max - 1) {
714 memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
715 /* If it's a filesystem, display the mountpoint */
716 if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
717 || label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
718 mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
719 else if (label_chunk_info[i].type == PART_SWAP)
722 mountpoint = "<none>";
724 /* Now display the newfs field */
725 if (label_chunk_info[i].type == PART_FAT)
726 strcpy(newfs, "DOS");
727 #if defined(__ia64__)
728 else if (label_chunk_info[i].type == PART_EFI) {
729 strcpy(newfs, "EFI");
730 if (label_chunk_info[i].c->private_data) {
732 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
733 strcat(newfs, pi->do_newfs ? " Y" : " N");
737 else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
738 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
740 switch (pi->newfs_type) {
742 strcpy(newfs, NEWFS_UFS_STRING);
743 if (pi->newfs_data.newfs_ufs.ufs1)
747 if (pi->newfs_data.newfs_ufs.softupdates)
754 strcpy(newfs, "FAT");
757 strcpy(newfs, "CUST");
760 strcat(newfs, pi->do_newfs ? " Y" : " N ");
762 else if (label_chunk_info[i].type == PART_SWAP)
763 strcpy(newfs, "SWAP");
766 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
767 onestr[PART_MOUNT_COL + j] = mountpoint[j];
768 if (label_chunk_info[i].c->size == 0)
769 snprintf(num, 10, "%5dMB", 0);
770 else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
771 snprintf(num, 10, "%5jdMB",
772 (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
774 snprintf(num, 10, "%5jdGB",
775 (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
776 memcpy(onestr + PART_SIZE_COL, num, strlen(num));
777 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
778 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
779 if (i == label_focus) {
780 label_focus_found = -1;
781 wattrset(ChunkWin, A_BOLD);
784 wattrset(ChunkWin, ATTR_SELECTED);
786 /*** lazy man's way of expensively padding this string ***/
787 while (strlen(onestr) < 37)
790 mvwaddstr(ChunkWin, prow, pcol, onestr);
791 wattrset(ChunkWin, A_NORMAL);
798 /*** this will erase all the extra stuff ***/
799 memset(clrmsg, ' ', 37);
802 while (pslice_count < pslice_max) {
803 mvprintw(srow++, 0, clrmsg);
807 while (label_count < (2 * (label_max - 1))) {
808 mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
810 if (prow == (label_max - 1)) {
820 print_command_summary(void)
822 mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
823 mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
824 mvprintw(18, 56, "W = Write");
825 mvprintw(19, 0, "N = Newfs Opts Q = Finish S = Toggle SoftUpdates Z = Custom Newfs");
826 mvprintw(20, 0, "T = Toggle Newfs U = Undo A = Auto Defaults R = Delete+Merge");
827 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
836 print_label_chunks();
840 diskLabel(Device *dev)
849 WINDOW *w = savescr();
855 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
857 msgConfirm("No disks found!");
859 return DITEM_FAILURE;
862 keypad(stdscr, TRUE);
863 record_label_chunks(devs, dev);
868 int rflags = DELCHUNK_NORMAL;
870 print_label_chunks();
871 print_command_summary();
873 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
885 switch (toupper(key)) {
887 static char _msg[40];
889 case '\014': /* ^L */
893 case '\020': /* ^P */
899 while (label_chunk_info[here + 1].c)
903 case '\016': /* ^N */
908 if (label_chunk_info[here + 1].c)
919 while (label_chunk_info[here + 1].c)
925 systemDisplayHelp("partition");
930 if (label_chunk_info[here].type == PART_FILESYSTEM) {
932 ((PartInfo *)label_chunk_info[here].c->private_data);
935 (pi->newfs_type == NEWFS_UFS)) {
936 pi->newfs_data.newfs_ufs.ufs1 = true;
938 msg = MSG_NOT_APPLICABLE;
940 msg = MSG_NOT_APPLICABLE;
945 if (label_chunk_info[here].type == PART_FILESYSTEM) {
947 ((PartInfo *)label_chunk_info[here].c->private_data);
950 (pi->newfs_type == NEWFS_UFS)) {
951 pi->newfs_data.newfs_ufs.ufs1 = false;
953 msg = MSG_NOT_APPLICABLE;
955 msg = MSG_NOT_APPLICABLE;
960 if (label_chunk_info[here].type != PART_SLICE) {
961 msg = "You can only do this in a disk slice (at top of screen)";
965 * Generate standard partitions automatically. If we do not
966 * have sufficient space we attempt to scale-down the size
967 * of the partitions within certain bounds.
973 for (perc = 100; perc > 0; perc -= 5) {
974 req = 0; /* reset for each loop */
975 if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
980 msgConfirm("%s", msg);
989 if (label_chunk_info[here].type != PART_SLICE) {
990 msg = "You can only do this in a master partition (see top of screen)";
993 sz = space_free(label_chunk_info[here].c);
994 if (sz <= FS_MIN_SIZE) {
995 msg = "Not enough space to create an additional FreeBSD partition";
1006 /* Always use the maximum size for apple partitions */
1007 if (label_chunk_info[here].c->type == apple)
1011 sprintf(osize, "%jd", (intmax_t)sz);
1012 val = msgGetInput(osize,
1013 "Please specify the partition size in blocks or append a trailing G for\n"
1015 "gigabytes, M for megabytes.\n"
1017 "gigabytes, M for megabytes, or C for cylinders.\n"
1019 "%jd blocks (%jdMB) are free.",
1020 (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1021 if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
1027 if (toupper(*cp) == 'M')
1029 else if (toupper(*cp) == 'G')
1032 else if (toupper(*cp) == 'C')
1033 size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1036 if (size <= FS_MIN_SIZE) {
1037 msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1044 type = get_partition_type();
1045 if (type == PART_NONE) {
1051 if (type == PART_FILESYSTEM || type == PART_EFI) {
1052 if ((p = get_mountpoint(type, NULL)) == NULL) {
1057 else if (!strcmp(p->mountpoint, "/")) {
1058 if (type != PART_FILESYSTEM) {
1064 flags |= CHUNK_IS_ROOT;
1067 flags &= ~CHUNK_IS_ROOT;
1072 if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1073 msgConfirm("Warning: This is smaller than the recommended size for a\n"
1074 "root partition. For a variety of reasons, root\n"
1075 "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1077 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1078 label_chunk_info[here].c, size,
1080 (type == PART_EFI) ? efi : part,
1081 (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1083 part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1087 msgConfirm("Unable to create the partition. Too big?");
1094 * SRM requires that the root partition is at the
1095 * begining of the disk and cannot boot otherwise.
1096 * Warn Alpha users if they are about to shoot themselves in
1097 * the foot in this way.
1099 * Since partitions may not start precisely at offset 0 we
1100 * check for a "close to 0" instead. :-(
1102 if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
1103 msgConfirm("Your root partition `a' does not seem to be the first\n"
1104 "partition. The Alpha's firmware can only boot from the\n"
1105 "first partition. So it is unlikely that your current\n"
1106 "disk layout will be bootable boot after installation.\n"
1108 "Please allocate the root partition before allocating\n"
1113 tmp->private_data = p;
1114 tmp->private_free = safe_free;
1115 if (variable_cmp(DISK_LABELLED, "written"))
1116 variable_set2(DISK_LABELLED, "yes", 0);
1117 record_label_chunks(devs, dev);
1119 /* This is where we assign focus to new label so it shows. */
1123 for (i = 0; label_chunk_info[i].c; ++i) {
1124 if (label_chunk_info[i].c == tmp) {
1129 if (label_focus == -1)
1130 label_focus = i - 1;
1136 case 'R': /* recover space (delete w/ recover) */
1138 * Delete the partition w/ space recovery.
1140 rflags = DELCHUNK_RECOVER;
1142 case 'D': /* delete */
1143 if (label_chunk_info[here].type == PART_SLICE) {
1144 msg = MSG_NOT_APPLICABLE;
1147 else if (label_chunk_info[here].type == PART_FAT) {
1148 msg = "Use the Disk Partition Editor to delete DOS partitions";
1151 Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1152 if (variable_cmp(DISK_LABELLED, "written"))
1153 variable_set2(DISK_LABELLED, "yes", 0);
1154 record_label_chunks(devs, dev);
1157 case 'M': /* mount */
1158 switch(label_chunk_info[here].type) {
1160 msg = MSG_NOT_APPLICABLE;
1164 msg = "You don't need to specify a mountpoint for a swap partition.";
1169 case PART_FILESYSTEM:
1170 oldp = label_chunk_info[here].c->private_data;
1171 p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1174 p->do_newfs = FALSE;
1175 if ((label_chunk_info[here].type == PART_FAT ||
1176 label_chunk_info[here].type == PART_EFI) &&
1177 (!strcmp(p->mountpoint, "/") ||
1178 !strcmp(p->mountpoint, "/usr") ||
1179 !strcmp(p->mountpoint, "/var"))) {
1180 msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1181 strcpy(p->mountpoint, "/bogus");
1184 if (variable_cmp(DISK_LABELLED, "written"))
1185 variable_set2(DISK_LABELLED, "yes", 0);
1186 record_label_chunks(devs, dev);
1191 msgFatal("Bogus partition under cursor???");
1196 case 'N': /* Set newfs options */
1197 if (label_chunk_info[here].c->private_data &&
1198 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1199 getNewfsOptionalArguments(
1200 label_chunk_info[here].c->private_data);
1202 msg = MSG_NOT_APPLICABLE;
1206 case 'S': /* Toggle soft updates flag */
1207 if (label_chunk_info[here].type == PART_FILESYSTEM) {
1208 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1210 pi->newfs_type == NEWFS_UFS)
1211 pi->newfs_data.newfs_ufs.softupdates =
1212 !pi->newfs_data.newfs_ufs.softupdates;
1214 msg = MSG_NOT_APPLICABLE;
1217 msg = MSG_NOT_APPLICABLE;
1220 case 'T': /* Toggle newfs state */
1221 if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1222 label_chunk_info[here].type == PART_EFI) &&
1223 (label_chunk_info[here].c->private_data)) {
1224 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1226 label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1228 label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1230 label_chunk_info[here].c->private_data =
1231 new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1234 pi->newfs_type == NEWFS_UFS) {
1235 PartInfo *pi_new = label_chunk_info[here].c->private_data;
1237 pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1240 label_chunk_info[here].c->private_free = safe_free;
1241 if (variable_cmp(DISK_LABELLED, "written"))
1242 variable_set2(DISK_LABELLED, "yes", 0);
1245 msg = MSG_NOT_APPLICABLE;
1250 if (!variable_cmp(DISK_LABELLED, "written")) {
1251 msgConfirm("You've already written out your changes -\n"
1252 "it's too late to undo!");
1254 else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1255 variable_unset(DISK_PARTITIONED);
1256 variable_unset(DISK_LABELLED);
1257 for (i = 0; devs[i]; i++) {
1260 if (!devs[i]->enabled)
1262 else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1263 Free_Disk(devs[i]->private);
1264 devs[i]->private = d;
1266 diskPartition(devs[i]);
1270 record_label_chunks(devs, dev);
1276 if (!variable_cmp(DISK_LABELLED, "written")) {
1277 msgConfirm("You've already written out your changes - if you\n"
1278 "wish to overwrite them, you'll have to restart\n"
1279 "%s first.", ProgName);
1281 else if (!msgNoYes("WARNING: You are about to modify an EXISTING\n"
1283 "Are you absolutely sure you want to continue?")) {
1284 variable_set2(DISK_LABELLED, "yes", 0);
1285 diskLabelCommit(NULL);
1290 case 'Z': /* Set newfs command line */
1291 if (label_chunk_info[here].c->private_data &&
1292 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1293 getNewfsCmd(label_chunk_info[here].c->private_data);
1295 msg = MSG_NOT_APPLICABLE;
1301 if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1302 "This is an entirely undocumented feature which you are not\n"
1303 "expected to understand!")) {
1309 DialogActive = FALSE;
1310 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1312 msgConfirm("Can't find any disk devices!");
1315 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1316 if (devs[i]->enabled)
1317 slice_wizard(((Disk *)devs[i]->private));
1319 if (variable_cmp(DISK_LABELLED, "written"))
1320 variable_set2(DISK_LABELLED, "yes", 0);
1321 DialogActive = TRUE;
1322 record_label_chunks(devs, dev);
1326 msg = "A most prudent choice!";
1330 case '\033': /* ESC */
1337 sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1341 if (label_chunk_info[here].type == PART_SLICE)
1342 pslice_focus = here;
1347 return DITEM_SUCCESS;
1350 static __inline daddr_t
1351 requested_part_size(char *varName, daddr_t nom, int def, int perc)
1356 if ((cp = variable_get(varName)) != NULL)
1357 sz = strtoimax(cp, NULL, 0);
1359 sz = nom + (def - nom) * perc / 100;
1360 return(sz * ONE_MEG);
1364 * Attempt to auto-label the disk. 'perc' (0-100) scales
1365 * the size of the various partitions within appropriate
1366 * bounds (NOMINAL through DEFAULT sizes). The procedure
1367 * succeeds of NULL is returned. A non-null return message
1368 * is either a failure-status message (*req == 0), or
1369 * a confirmation requestor (*req == 1). *req is 0 on
1370 * entry to this call.
1372 * As a special exception to the usual sizing rules, /var is given
1373 * additional space equal to the amount of physical memory present
1374 * if perc == 100 in order to ensure that users with large hard drives
1375 * will have enough space to store a crashdump in /var/crash.
1377 * We autolabel the following partitions: /, swap, /var, /tmp, /usr,
1378 * and /home. /home receives any extra left over disk space.
1381 try_auto_label(Device **devs, Device *dev, int perc, int *req)
1384 Chunk *AutoHome, *AutoRoot, *AutoSwap;
1385 Chunk *AutoTmp, *AutoUsr, *AutoVar;
1390 unsigned long physmem;
1394 sz = space_free(label_chunk_info[here].c);
1395 if (sz <= FS_MIN_SIZE)
1396 return("Not enough free space to create a new partition in the slice");
1398 (void)checkLabels(FALSE);
1399 AutoHome = AutoRoot = AutoSwap = NULL;
1400 AutoTmp = AutoUsr = AutoVar = NULL;
1404 if (EfiChunk == NULL) {
1406 AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1407 label_chunk_info[here].c, sz, efi, 0, 0);
1408 if (AutoEfi == NULL) {
1410 msg = "Unable to create the EFI system partition. Too big?";
1413 AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1414 AutoEfi->private_free = safe_free;
1415 AutoEfi->flags |= CHUNK_NEWFS;
1416 record_label_chunks(devs, dev);
1420 if (RootChunk == NULL) {
1421 sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1423 AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1424 label_chunk_info[here].c, sz, part,
1425 FS_BSDFFS, CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1428 msg = "Unable to create the root partition. Too big?";
1431 AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1432 AutoRoot->private_free = safe_free;
1433 AutoRoot->flags |= CHUNK_NEWFS;
1434 record_label_chunks(devs, dev);
1436 if (SwapChunk == NULL) {
1437 sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1443 mib[1] = HW_PHYSMEM;
1444 size = sizeof physmem;
1445 sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1446 def = 2 * (int)(physmem / 512);
1447 if (def < SWAP_MIN_SIZE * ONE_MEG)
1448 def = SWAP_MIN_SIZE * ONE_MEG;
1449 if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1450 def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1451 nom = (int)(physmem / 512) / 8;
1452 sz = nom + (def - nom) * perc / 100;
1454 AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1455 label_chunk_info[here].c, sz, part,
1456 FS_SWAP, CHUNK_AUTO_SIZE);
1459 msg = "Unable to create the swap partition. Too big?";
1462 AutoSwap->private_data = 0;
1463 AutoSwap->private_free = safe_free;
1464 record_label_chunks(devs, dev);
1466 if (VarChunk == NULL) {
1467 /* Work out how much extra space we want for a crash dump */
1468 unsigned long crashdumpsz;
1471 mib[1] = HW_PHYSMEM;
1472 size = sizeof(physmem);
1473 sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1476 crashdumpsz = physmem / 1048576;
1480 sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, \
1481 VAR_DEFAULT_SIZE + crashdumpsz, perc);
1483 AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1484 label_chunk_info[here].c, sz, part,
1485 FS_BSDFFS, CHUNK_AUTO_SIZE);
1488 msg = "Not enough free space for /var - you will need to\n"
1489 "partition your disk manually with a custom install!";
1492 AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1493 AutoVar->private_free = safe_free;
1494 AutoVar->flags |= CHUNK_NEWFS;
1495 record_label_chunks(devs, dev);
1497 if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1498 sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1500 AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1501 label_chunk_info[here].c, sz, part,
1502 FS_BSDFFS, CHUNK_AUTO_SIZE);
1505 msg = "Not enough free space for /tmp - you will need to\n"
1506 "partition your disk manually with a custom install!";
1509 AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1510 AutoTmp->private_free = safe_free;
1511 AutoTmp->flags |= CHUNK_NEWFS;
1512 record_label_chunks(devs, dev);
1514 if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1515 sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1517 if (sz < space_free(label_chunk_info[here].c))
1518 sz = space_free(label_chunk_info[here].c);
1521 if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1523 msg = "Not enough free space for /usr - you will need to\n"
1524 "partition your disk manually with a custom install!";
1527 AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1528 label_chunk_info[here].c, sz, part,
1529 FS_BSDFFS, CHUNK_AUTO_SIZE);
1531 msg = "Unable to create the /usr partition. Not enough space?\n"
1532 "You will need to partition your disk manually with a custom install!";
1535 AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1536 AutoUsr->private_free = safe_free;
1537 AutoUsr->flags |= CHUNK_NEWFS;
1538 record_label_chunks(devs, dev);
1542 if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1543 sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1544 if (sz < space_free(label_chunk_info[here].c))
1545 sz = space_free(label_chunk_info[here].c);
1547 if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1549 msg = "Not enough free space for /home - you will need to\n"
1550 "partition your disk manually with a custom install!";
1554 AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1555 label_chunk_info[here].c, sz, part,
1556 FS_BSDFFS, CHUNK_AUTO_SIZE);
1558 msg = "Unable to create the /home partition. Not enough space?\n"
1559 "You will need to partition your disk manually with a custom install!";
1562 AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1563 AutoHome->private_free = safe_free;
1564 AutoHome->flags |= CHUNK_NEWFS;
1565 record_label_chunks(devs, dev);
1570 /* At this point, we're reasonably "labelled" */
1571 if (variable_cmp(DISK_LABELLED, "written"))
1572 variable_set2(DISK_LABELLED, "yes", 0);
1576 if (AutoRoot != NULL)
1577 Delete_Chunk(AutoRoot->disk, AutoRoot);
1578 if (AutoSwap != NULL)
1579 Delete_Chunk(AutoSwap->disk, AutoSwap);
1580 if (AutoVar != NULL)
1581 Delete_Chunk(AutoVar->disk, AutoVar);
1582 if (AutoTmp != NULL)
1583 Delete_Chunk(AutoTmp->disk, AutoTmp);
1584 if (AutoUsr != NULL)
1585 Delete_Chunk(AutoUsr->disk, AutoUsr);
1586 if (AutoHome != NULL)
1587 Delete_Chunk(AutoHome->disk, AutoHome);
1588 record_label_chunks(devs, dev);
1594 diskLabelNonInteractive(Device *dev)
1604 status = DITEM_SUCCESS;
1605 cp = variable_get(VAR_DISK);
1607 msgConfirm("diskLabel: No disk selected - can't label automatically.");
1608 return DITEM_FAILURE;
1610 devs = deviceFind(cp, DEVICE_TYPE_DISK);
1612 msgConfirm("diskLabel: No disk device %s found!", cp);
1613 return DITEM_FAILURE;
1618 d = devs[0]->private;
1619 record_label_chunks(devs, dev);
1620 for (i = 0; label_chunk_info[i].c; i++) {
1621 Chunk *c1 = label_chunk_info[i].c;
1623 if (label_chunk_info[i].type == PART_SLICE) {
1625 char typ[10], mpoint[50];
1628 for (entries = 1;; entries++) {
1631 snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1632 if ((cp = variable_get(name)) == NULL)
1634 if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1635 msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1636 status = DITEM_FAILURE;
1642 if (!strcmp(typ, "swap")) {
1644 strcpy(mpoint, "SWAP");
1646 type = PART_FILESYSTEM;
1647 if (!strcmp(mpoint, "/"))
1648 flags |= CHUNK_IS_ROOT;
1651 sz = space_free(c1);
1652 if (sz > space_free(c1)) {
1653 msgConfirm("Not enough free space to create partition: %s", mpoint);
1654 status = DITEM_FAILURE;
1657 if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1658 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1659 msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1660 status = DITEM_FAILURE;
1664 pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1665 tmp->private_free = safe_free;
1666 pi->newfs_data.newfs_ufs.softupdates = soft;
1671 /* Must be something we can set a mountpoint for */
1672 cp = variable_get(c1->name);
1674 char mpoint[50], do_newfs[8];
1675 Boolean newfs = FALSE;
1678 if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1679 msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1680 status = DITEM_FAILURE;
1683 newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1684 if (c1->private_data) {
1685 p = c1->private_data;
1686 p->do_newfs = newfs;
1687 strcpy(p->mountpoint, mpoint);
1690 c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1691 c1->private_free = safe_free;
1693 if (!strcmp(mpoint, "/"))
1694 c1->flags |= CHUNK_IS_ROOT;
1696 c1->flags &= ~CHUNK_IS_ROOT;
1700 if (status == DITEM_SUCCESS)
1701 variable_set2(DISK_LABELLED, "yes", 0);