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(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
65 #define ROOT_MIN_SIZE 128
67 #define ROOT_MIN_SIZE 118
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 512
86 #define USR_DEFAULT_SIZE 8192
87 #define VAR_DEFAULT_SIZE 1024
88 #define TMP_DEFAULT_SIZE 512
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 256
97 #define USR_NOMINAL_SIZE 1536
98 #define VAR_NOMINAL_SIZE 128
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, "/");
388 pi->newfs_data.newfs_ufs.ufs1 = TRUE;
390 pi->newfs_data.newfs_ufs.ufs1 = FALSE;
397 /* Get the mountpoint for a partition and save it away */
399 get_mountpoint(PartType type, struct chunk *old)
405 if (old && old->private_data)
406 tmp = old->private_data;
409 val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
410 val = msgGetInput(val, "Please specify a mount point for the partition");
415 free(old->private_data);
416 old->private_data = NULL;
421 /* Is it just the same value? */
422 if (tmp && !strcmp(tmp->mountpoint, val))
425 /* Did we use it already? */
426 if (check_conflict(val)) {
427 msgConfirm("You already have a mount point for %s assigned!", val);
433 msgConfirm("Mount point must start with a / character");
437 /* Is it going to be mounted on root? */
438 if (!strcmp(val, "/")) {
440 old->flags |= CHUNK_IS_ROOT;
443 old->flags &= ~CHUNK_IS_ROOT;
447 newfs = tmp->do_newfs;
450 val = string_skipwhite(string_prune(val));
451 tmp = new_part(type, val, newfs);
453 old->private_data = tmp;
454 old->private_free = safe_free;
459 /* Get the type of the new partiton */
461 get_partition_type(void)
465 static unsigned char *fs_types[] = {
467 "EFI", "An EFI system partition",
469 "FS", "A file system",
470 "Swap", "A swap partition.",
472 WINDOW *w = savescr();
474 i = dialog_menu("Please choose a partition type",
475 "If you want to use this partition for swap space, select Swap.\n"
476 "If you want to put a filesystem on it, choose FS.",
483 fs_types, selection, NULL, NULL);
487 if (!strcmp(selection, "EFI"))
490 if (!strcmp(selection, "FS"))
491 return PART_FILESYSTEM;
492 else if (!strcmp(selection, "Swap"))
498 /* If the user wants a special newfs command for this, set it */
500 getNewfsCmd(PartInfo *p)
502 char buffer[NEWFS_CMD_ARGS_MAX];
505 switch (p->newfs_type) {
507 snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
508 NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ? "-U" : "",
509 p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
510 p->newfs_data.newfs_ufs.user_options);
513 snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
516 strcpy(buffer, p->newfs_data.newfs_custom.command);
520 val = msgGetInput(buffer,
521 "Please enter the newfs command and options you'd like to use in\n"
522 "creating this file system.");
524 p->newfs_type = NEWFS_CUSTOM;
525 strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
530 getNewfsOptionalArguments(PartInfo *p)
532 char buffer[NEWFS_CMD_ARGS_MAX];
535 /* Must be UFS, per argument checking in I/O routines. */
537 strlcpy(buffer, p->newfs_data.newfs_ufs.user_options,
539 val = msgGetInput(buffer,
540 "Please enter any additional UFS newfs options you'd like to\n"
541 "use in creating this file system.");
543 strlcpy(p->newfs_data.newfs_ufs.user_options, val,
547 #define MAX_MOUNT_NAME 9
549 #define PART_PART_COL 0
550 #define PART_MOUNT_COL 10
551 #define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
552 #define PART_NEWFS_COL (PART_SIZE_COL + 8)
555 #define TOTAL_AVAIL_LINES (10)
556 #define PSLICE_SHOWABLE (4)
559 /* stick this all up on the screen */
561 print_label_chunks(void)
563 int i, j, srow, prow, pcol;
566 int ChunkPartStartRow;
569 /********************************************************/
570 /*** These values are for controling screen resources ***/
571 /*** Each label line holds up to 2 labels, so beware! ***/
572 /*** strategy will be to try to always make sure the ***/
573 /*** highlighted label is in the active display area. ***/
574 /********************************************************/
575 int pslice_max, label_max;
576 int pslice_count, label_count, label_focus_found, pslice_focus_found;
579 mvaddstr(0, 25, "FreeBSD Disklabel Editor");
582 /*** Count the number of parition slices ***/
584 for (i = 0; label_chunk_info[i].c ; i++) {
585 if (label_chunk_info[i].type == PART_SLICE)
588 pslice_max = pslice_count;
590 /*** 4 line max for partition slices ***/
591 if (pslice_max > PSLICE_SHOWABLE) {
592 pslice_max = PSLICE_SHOWABLE;
594 ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
596 /*** View partition slices modulo pslice_max ***/
597 label_max = TOTAL_AVAIL_LINES - pslice_max;
599 for (i = 0; i < 2; i++) {
600 mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
601 mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
603 mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
604 mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
606 mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
607 mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
609 mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
610 mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
612 srow = CHUNK_SLICE_START_ROW;
616 /*** these variables indicate that the focused item is shown currently ***/
617 label_focus_found = 0;
618 pslice_focus_found = 0;
622 mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
623 mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
625 ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
628 /*** wrefresh(ChunkWin); ***/
630 for (i = 0; label_chunk_info[i].c; i++) {
631 /* Is it a slice entry displayed at the top? */
632 if (label_chunk_info[i].type == PART_SLICE) {
633 /*** This causes the new pslice to replace the previous display ***/
634 /*** focus must remain on the most recently active pslice ***/
635 if (pslice_count == pslice_max) {
636 if (pslice_focus_found) {
637 /*** This is where we can mark the more following ***/
639 mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
644 /*** this is where we set the more previous ***/
646 mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
649 srow = CHUNK_SLICE_START_ROW;
653 sz = space_free(label_chunk_info[i].c);
655 attrset(ATTR_SELECTED);
656 if (i == pslice_focus)
657 pslice_focus_found = -1;
659 if (label_chunk_info[i].c->type == whole) {
660 if (sz >= 100 * ONE_GIG)
662 "Disk: %s\t\tFree: %jd blocks (%jdGB)",
663 label_chunk_info[i].c->disk->name, (intmax_t)sz,
664 (intmax_t)(sz / ONE_GIG));
667 "Disk: %s\t\tFree: %jd blocks (%jdMB)",
668 label_chunk_info[i].c->disk->name, (intmax_t)sz,
669 (intmax_t)(sz / ONE_MEG));
671 if (sz >= 100 * ONE_GIG)
673 "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
674 label_chunk_info[i].c->disk->name,
675 label_chunk_info[i].c->name,
676 (intmax_t)sz, (intmax_t)(sz / ONE_GIG));
679 "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
680 label_chunk_info[i].c->disk->name,
681 label_chunk_info[i].c->name,
682 (intmax_t)sz, (intmax_t)(sz / ONE_MEG));
690 /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
692 char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
695 * We copy this into a blank-padded string so that it looks like
696 * a solid bar in reverse-video
698 memset(onestr, ' ', PART_OFF - 1);
699 onestr[PART_OFF - 1] = '\0';
701 /*** Track how many labels have been displayed ***/
702 if (label_count == ((label_max - 1 ) * 2)) {
703 if (label_focus_found) {
713 /* Go for two columns if we've written one full columns worth */
714 /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
715 if (label_count == label_max - 1) {
719 memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
720 /* If it's a filesystem, display the mountpoint */
721 if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
722 || label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
723 mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
724 else if (label_chunk_info[i].type == PART_SWAP)
727 mountpoint = "<none>";
729 /* Now display the newfs field */
730 if (label_chunk_info[i].type == PART_FAT)
731 strcpy(newfs, "DOS");
732 #if defined(__ia64__)
733 else if (label_chunk_info[i].type == PART_EFI) {
734 strcpy(newfs, "EFI");
735 if (label_chunk_info[i].c->private_data) {
737 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
738 strcat(newfs, pi->do_newfs ? " Y" : " N");
742 else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
743 PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
745 switch (pi->newfs_type) {
747 strcpy(newfs, NEWFS_UFS_STRING);
748 if (pi->newfs_data.newfs_ufs.ufs1)
752 if (pi->newfs_data.newfs_ufs.softupdates)
759 strcpy(newfs, "FAT");
762 strcpy(newfs, "CUST");
765 strcat(newfs, pi->do_newfs ? " Y" : " N ");
767 else if (label_chunk_info[i].type == PART_SWAP)
768 strcpy(newfs, "SWAP");
771 for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
772 onestr[PART_MOUNT_COL + j] = mountpoint[j];
773 if (label_chunk_info[i].c->size == 0)
774 snprintf(num, 10, "%5dMB", 0);
775 else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
776 snprintf(num, 10, "%5jdMB",
777 (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
779 snprintf(num, 10, "%5jdGB",
780 (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
781 memcpy(onestr + PART_SIZE_COL, num, strlen(num));
782 memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
783 onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
784 if (i == label_focus) {
785 label_focus_found = -1;
786 wattrset(ChunkWin, A_BOLD);
789 wattrset(ChunkWin, ATTR_SELECTED);
791 /*** lazy man's way of expensively padding this string ***/
792 while (strlen(onestr) < 37)
795 mvwaddstr(ChunkWin, prow, pcol, onestr);
796 wattrset(ChunkWin, A_NORMAL);
803 /*** this will erase all the extra stuff ***/
804 memset(clrmsg, ' ', 37);
807 while (pslice_count < pslice_max) {
808 mvprintw(srow++, 0, clrmsg);
812 while (label_count < (2 * (label_max - 1))) {
813 mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
815 if (prow == (label_max - 1)) {
825 print_command_summary(void)
827 mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
828 mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
830 mvprintw(18, 56, "W = Write");
831 mvprintw(19, 0, "N = Newfs Opts Q = Finish S = Toggle SoftUpdates Z = Custom Newfs");
832 mvprintw(20, 0, "T = Toggle Newfs U = Undo A = Auto Defaults R = Delete+Merge");
833 mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
841 print_label_chunks();
845 diskLabel(Device *dev)
854 WINDOW *w = savescr();
860 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
862 msgConfirm("No disks found!");
864 return DITEM_FAILURE;
867 keypad(stdscr, TRUE);
868 record_label_chunks(devs, dev);
873 int rflags = DELCHUNK_NORMAL;
875 print_label_chunks();
876 print_command_summary();
878 attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
890 switch (toupper(key)) {
892 static char _msg[40];
894 case '\014': /* ^L */
898 case '\020': /* ^P */
904 while (label_chunk_info[here + 1].c)
908 case '\016': /* ^N */
913 if (label_chunk_info[here + 1].c)
924 while (label_chunk_info[here + 1].c)
930 systemDisplayHelp("partition");
935 if (label_chunk_info[here].type == PART_FILESYSTEM) {
937 ((PartInfo *)label_chunk_info[here].c->private_data);
940 (pi->newfs_type == NEWFS_UFS)) {
941 pi->newfs_data.newfs_ufs.ufs1 = true;
943 msg = MSG_NOT_APPLICABLE;
945 msg = MSG_NOT_APPLICABLE;
950 if (label_chunk_info[here].type == PART_FILESYSTEM) {
952 ((PartInfo *)label_chunk_info[here].c->private_data);
955 (pi->newfs_type == NEWFS_UFS)) {
956 pi->newfs_data.newfs_ufs.ufs1 = false;
958 msg = MSG_NOT_APPLICABLE;
960 msg = MSG_NOT_APPLICABLE;
965 if (label_chunk_info[here].type != PART_SLICE) {
966 msg = "You can only do this in a disk slice (at top of screen)";
970 * Generate standard partitions automatically. If we do not
971 * have sufficient space we attempt to scale-down the size
972 * of the partitions within certain bounds.
978 for (perc = 100; perc > 0; perc -= 5) {
979 req = 0; /* reset for each loop */
980 if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
994 if (label_chunk_info[here].type != PART_SLICE) {
995 msg = "You can only do this in a master partition (see top of screen)";
998 sz = space_free(label_chunk_info[here].c);
999 if (sz <= FS_MIN_SIZE) {
1000 msg = "Not enough space to create an additional FreeBSD partition";
1011 /* Always use the maximum size for apple partitions */
1012 if (label_chunk_info[here].c->type == apple)
1016 sprintf(osize, "%jd", (intmax_t)sz);
1017 val = msgGetInput(osize,
1018 "Please specify the partition size in blocks or append a trailing G for\n"
1020 "gigabytes, M for megabytes.\n"
1022 "gigabytes, M for megabytes, or C for cylinders.\n"
1024 "%jd blocks (%jdMB) are free.",
1025 (intmax_t)sz, (intmax_t)sz / ONE_MEG);
1026 if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
1032 if (toupper(*cp) == 'M')
1034 else if (toupper(*cp) == 'G')
1037 else if (toupper(*cp) == 'C')
1038 size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
1041 if (size <= FS_MIN_SIZE) {
1042 msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
1049 type = get_partition_type();
1050 if (type == PART_NONE) {
1056 if (type == PART_FILESYSTEM || type == PART_EFI) {
1057 if ((p = get_mountpoint(type, NULL)) == NULL) {
1062 else if (!strcmp(p->mountpoint, "/")) {
1063 if (type != PART_FILESYSTEM) {
1069 flags |= CHUNK_IS_ROOT;
1072 flags &= ~CHUNK_IS_ROOT;
1077 if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
1078 msgConfirm("Warning: This is smaller than the recommended size for a\n"
1079 "root partition. For a variety of reasons, root\n"
1080 "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
1082 tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1083 label_chunk_info[here].c, size,
1085 (type == PART_EFI) ? efi : part,
1086 (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1088 part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
1092 msgConfirm("Unable to create the partition. Too big?");
1099 * SRM requires that the root partition is at the
1100 * begining of the disk and cannot boot otherwise.
1101 * Warn Alpha users if they are about to shoot themselves in
1102 * the foot in this way.
1104 * Since partitions may not start precisely at offset 0 we
1105 * check for a "close to 0" instead. :-(
1107 if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
1108 msgConfirm("Your root partition `a' does not seem to be the first\n"
1109 "partition. The Alpha's firmware can only boot from the\n"
1110 "first partition. So it is unlikely that your current\n"
1111 "disk layout will be bootable boot after installation.\n"
1113 "Please allocate the root partition before allocating\n"
1118 tmp->private_data = p;
1119 tmp->private_free = safe_free;
1120 if (variable_cmp(DISK_LABELLED, "written"))
1121 variable_set2(DISK_LABELLED, "yes", 0);
1122 record_label_chunks(devs, dev);
1124 /* This is where we assign focus to new label so it shows. */
1128 for (i = 0; label_chunk_info[i].c; ++i) {
1129 if (label_chunk_info[i].c == tmp) {
1134 if (label_focus == -1)
1135 label_focus = i - 1;
1141 case 'R': /* recover space (delete w/ recover) */
1143 * Delete the partition w/ space recovery.
1145 rflags = DELCHUNK_RECOVER;
1147 case 'D': /* delete */
1148 if (label_chunk_info[here].type == PART_SLICE) {
1149 msg = MSG_NOT_APPLICABLE;
1152 else if (label_chunk_info[here].type == PART_FAT) {
1153 msg = "Use the Disk Partition Editor to delete DOS partitions";
1156 Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
1157 if (variable_cmp(DISK_LABELLED, "written"))
1158 variable_set2(DISK_LABELLED, "yes", 0);
1159 record_label_chunks(devs, dev);
1162 case 'M': /* mount */
1163 switch(label_chunk_info[here].type) {
1165 msg = MSG_NOT_APPLICABLE;
1169 msg = "You don't need to specify a mountpoint for a swap partition.";
1174 case PART_FILESYSTEM:
1175 oldp = label_chunk_info[here].c->private_data;
1176 p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
1179 p->do_newfs = FALSE;
1180 if ((label_chunk_info[here].type == PART_FAT ||
1181 label_chunk_info[here].type == PART_EFI) &&
1182 (!strcmp(p->mountpoint, "/") ||
1183 !strcmp(p->mountpoint, "/usr") ||
1184 !strcmp(p->mountpoint, "/var"))) {
1185 msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
1186 strcpy(p->mountpoint, "/bogus");
1189 if (variable_cmp(DISK_LABELLED, "written"))
1190 variable_set2(DISK_LABELLED, "yes", 0);
1191 record_label_chunks(devs, dev);
1196 msgFatal("Bogus partition under cursor???");
1201 case 'N': /* Set newfs options */
1202 if (label_chunk_info[here].c->private_data &&
1203 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1204 getNewfsOptionalArguments(
1205 label_chunk_info[here].c->private_data);
1207 msg = MSG_NOT_APPLICABLE;
1211 case 'S': /* Toggle soft updates flag */
1212 if (label_chunk_info[here].type == PART_FILESYSTEM) {
1213 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1215 pi->newfs_type == NEWFS_UFS)
1216 pi->newfs_data.newfs_ufs.softupdates =
1217 !pi->newfs_data.newfs_ufs.softupdates;
1219 msg = MSG_NOT_APPLICABLE;
1222 msg = MSG_NOT_APPLICABLE;
1225 case 'T': /* Toggle newfs state */
1226 if ((label_chunk_info[here].type == PART_FILESYSTEM ||
1227 label_chunk_info[here].type == PART_EFI) &&
1228 (label_chunk_info[here].c->private_data)) {
1229 PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
1231 label_chunk_info[here].c->flags |= CHUNK_NEWFS;
1233 label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
1235 label_chunk_info[here].c->private_data =
1236 new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
1239 pi->newfs_type == NEWFS_UFS) {
1240 PartInfo *pi_new = label_chunk_info[here].c->private_data;
1242 pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
1245 label_chunk_info[here].c->private_free = safe_free;
1246 if (variable_cmp(DISK_LABELLED, "written"))
1247 variable_set2(DISK_LABELLED, "yes", 0);
1250 msg = MSG_NOT_APPLICABLE;
1255 if (!variable_cmp(DISK_LABELLED, "written")) {
1256 msgConfirm("You've already written out your changes -\n"
1257 "it's too late to undo!");
1259 else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
1260 variable_unset(DISK_PARTITIONED);
1261 variable_unset(DISK_LABELLED);
1262 for (i = 0; devs[i]; i++) {
1265 if (!devs[i]->enabled)
1267 else if ((d = Open_Disk(devs[i]->name)) != NULL) {
1268 Free_Disk(devs[i]->private);
1269 devs[i]->private = d;
1271 diskPartition(devs[i]);
1275 record_label_chunks(devs, dev);
1281 if (!variable_cmp(DISK_LABELLED, "written")) {
1282 msgConfirm("You've already written out your changes - if you\n"
1283 "wish to overwrite them, you'll have to restart\n"
1284 "%s first.", ProgName);
1286 else if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
1287 "installation. If you are installing FreeBSD for the first time\n"
1288 "then you should simply type Q when you're finished here and your\n"
1289 "changes will be committed in one batch automatically at the end of\n"
1290 "these questions.\n\n"
1291 "Are you absolutely sure you want to do this now?")) {
1292 variable_set2(DISK_LABELLED, "yes", 0);
1293 diskLabelCommit(NULL);
1298 case 'Z': /* Set newfs command line */
1299 if (label_chunk_info[here].c->private_data &&
1300 ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
1301 getNewfsCmd(label_chunk_info[here].c->private_data);
1303 msg = MSG_NOT_APPLICABLE;
1309 if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
1310 "This is an entirely undocumented feature which you are not\n"
1311 "expected to understand!")) {
1317 DialogActive = FALSE;
1318 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1320 msgConfirm("Can't find any disk devices!");
1323 for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
1324 if (devs[i]->enabled)
1325 slice_wizard(((Disk *)devs[i]->private));
1327 if (variable_cmp(DISK_LABELLED, "written"))
1328 variable_set2(DISK_LABELLED, "yes", 0);
1329 DialogActive = TRUE;
1330 record_label_chunks(devs, dev);
1334 msg = "A most prudent choice!";
1338 case '\033': /* ESC */
1345 sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
1349 if (label_chunk_info[here].type == PART_SLICE)
1350 pslice_focus = here;
1355 return DITEM_SUCCESS;
1358 static __inline daddr_t
1359 requested_part_size(char *varName, daddr_t nom, int def, int perc)
1364 if ((cp = variable_get(varName)) != NULL)
1365 sz = strtoimax(cp, NULL, 0);
1367 sz = nom + (def - nom) * perc / 100;
1368 return(sz * ONE_MEG);
1372 * Attempt to auto-label the disk. 'perc' (0-100) scales
1373 * the size of the various partitions within appropriate
1374 * bounds (NOMINAL through DEFAULT sizes). The procedure
1375 * succeeds of NULL is returned. A non-null return message
1376 * is either a failure-status message (*req == 0), or
1377 * a confirmation requestor (*req == 1). *req is 0 on
1378 * entry to this call.
1380 * As a special exception to the usual sizing rules, /var is given
1381 * additional space equal to the amount of physical memory present
1382 * if perc == 100 in order to ensure that users with large hard drives
1383 * will have enough space to store a crashdump in /var/crash.
1385 * We autolabel the following partitions: /, swap, /var, /tmp, /usr,
1386 * and /home. /home receives any extra left over disk space.
1389 try_auto_label(Device **devs, Device *dev, int perc, int *req)
1392 Chunk *AutoHome, *AutoRoot, *AutoSwap;
1393 Chunk *AutoTmp, *AutoUsr, *AutoVar;
1398 unsigned long physmem;
1402 sz = space_free(label_chunk_info[here].c);
1403 if (sz <= FS_MIN_SIZE)
1404 return("Not enough free space to create a new partition in the slice");
1406 (void)checkLabels(FALSE);
1407 AutoHome = AutoRoot = AutoSwap = NULL;
1408 AutoTmp = AutoUsr = AutoVar = NULL;
1412 if (EfiChunk == NULL) {
1414 AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1415 label_chunk_info[here].c, sz, efi, 0, 0);
1416 if (AutoEfi == NULL) {
1418 msg = "Unable to create the EFI system partition. Too big?";
1421 AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
1422 AutoEfi->private_free = safe_free;
1423 AutoEfi->flags |= CHUNK_NEWFS;
1424 record_label_chunks(devs, dev);
1428 if (RootChunk == NULL) {
1429 sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
1431 AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1432 label_chunk_info[here].c, sz, part,
1433 FS_BSDFFS, CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
1436 msg = "Unable to create the root partition. Too big?";
1439 AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
1440 AutoRoot->private_free = safe_free;
1441 AutoRoot->flags |= CHUNK_NEWFS;
1442 record_label_chunks(devs, dev);
1444 if (SwapChunk == NULL) {
1445 sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
1451 mib[1] = HW_PHYSMEM;
1452 size = sizeof physmem;
1453 sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1454 def = 2 * (int)(physmem / 512);
1455 if (def < SWAP_MIN_SIZE * ONE_MEG)
1456 def = SWAP_MIN_SIZE * ONE_MEG;
1457 if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
1458 def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
1459 nom = (int)(physmem / 512) / 8;
1460 sz = nom + (def - nom) * perc / 100;
1462 AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1463 label_chunk_info[here].c, sz, part,
1464 FS_SWAP, CHUNK_AUTO_SIZE);
1467 msg = "Unable to create the swap partition. Too big?";
1470 AutoSwap->private_data = 0;
1471 AutoSwap->private_free = safe_free;
1472 record_label_chunks(devs, dev);
1474 if (VarChunk == NULL) {
1475 /* Work out how much extra space we want for a crash dump */
1476 unsigned long crashdumpsz;
1479 mib[1] = HW_PHYSMEM;
1480 size = sizeof(physmem);
1481 sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
1484 crashdumpsz = physmem / 1048576;
1488 sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, \
1489 VAR_DEFAULT_SIZE + crashdumpsz, perc);
1491 AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1492 label_chunk_info[here].c, sz, part,
1493 FS_BSDFFS, CHUNK_AUTO_SIZE);
1496 msg = "Not enough free space for /var - you will need to\n"
1497 "partition your disk manually with a custom install!";
1500 AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
1501 AutoVar->private_free = safe_free;
1502 AutoVar->flags |= CHUNK_NEWFS;
1503 record_label_chunks(devs, dev);
1505 if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
1506 sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
1508 AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1509 label_chunk_info[here].c, sz, part,
1510 FS_BSDFFS, CHUNK_AUTO_SIZE);
1513 msg = "Not enough free space for /tmp - you will need to\n"
1514 "partition your disk manually with a custom install!";
1517 AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
1518 AutoTmp->private_free = safe_free;
1519 AutoTmp->flags |= CHUNK_NEWFS;
1520 record_label_chunks(devs, dev);
1522 if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
1523 sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
1525 if (sz < space_free(label_chunk_info[here].c))
1526 sz = space_free(label_chunk_info[here].c);
1529 if (sz < (USR_MIN_SIZE * ONE_MEG)) {
1531 msg = "Not enough free space for /usr - you will need to\n"
1532 "partition your disk manually with a custom install!";
1535 AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1536 label_chunk_info[here].c, sz, part,
1537 FS_BSDFFS, CHUNK_AUTO_SIZE);
1539 msg = "Unable to create the /usr partition. Not enough space?\n"
1540 "You will need to partition your disk manually with a custom install!";
1543 AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
1544 AutoUsr->private_free = safe_free;
1545 AutoUsr->flags |= CHUNK_NEWFS;
1546 record_label_chunks(devs, dev);
1550 if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
1551 sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
1552 if (sz < space_free(label_chunk_info[here].c))
1553 sz = space_free(label_chunk_info[here].c);
1555 if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
1557 msg = "Not enough free space for /home - you will need to\n"
1558 "partition your disk manually with a custom install!";
1562 AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
1563 label_chunk_info[here].c, sz, part,
1564 FS_BSDFFS, CHUNK_AUTO_SIZE);
1566 msg = "Unable to create the /home partition. Not enough space?\n"
1567 "You will need to partition your disk manually with a custom install!";
1570 AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
1571 AutoHome->private_free = safe_free;
1572 AutoHome->flags |= CHUNK_NEWFS;
1573 record_label_chunks(devs, dev);
1578 /* At this point, we're reasonably "labelled" */
1579 if (variable_cmp(DISK_LABELLED, "written"))
1580 variable_set2(DISK_LABELLED, "yes", 0);
1584 if (AutoRoot != NULL)
1585 Delete_Chunk(AutoRoot->disk, AutoRoot);
1586 if (AutoSwap != NULL)
1587 Delete_Chunk(AutoSwap->disk, AutoSwap);
1588 if (AutoVar != NULL)
1589 Delete_Chunk(AutoVar->disk, AutoVar);
1590 if (AutoTmp != NULL)
1591 Delete_Chunk(AutoTmp->disk, AutoTmp);
1592 if (AutoUsr != NULL)
1593 Delete_Chunk(AutoUsr->disk, AutoUsr);
1594 if (AutoHome != NULL)
1595 Delete_Chunk(AutoHome->disk, AutoHome);
1596 record_label_chunks(devs, dev);
1602 diskLabelNonInteractive(Device *dev)
1612 status = DITEM_SUCCESS;
1613 cp = variable_get(VAR_DISK);
1615 msgConfirm("diskLabel: No disk selected - can't label automatically.");
1616 return DITEM_FAILURE;
1618 devs = deviceFind(cp, DEVICE_TYPE_DISK);
1620 msgConfirm("diskLabel: No disk device %s found!", cp);
1621 return DITEM_FAILURE;
1626 d = devs[0]->private;
1627 record_label_chunks(devs, dev);
1628 for (i = 0; label_chunk_info[i].c; i++) {
1629 Chunk *c1 = label_chunk_info[i].c;
1631 if (label_chunk_info[i].type == PART_SLICE) {
1633 char typ[10], mpoint[50];
1636 for (entries = 1;; entries++) {
1639 snprintf(name, sizeof name, "%s-%d", c1->name, entries);
1640 if ((cp = variable_get(name)) == NULL)
1642 if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
1643 msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1644 status = DITEM_FAILURE;
1650 if (!strcmp(typ, "swap")) {
1652 strcpy(mpoint, "SWAP");
1654 type = PART_FILESYSTEM;
1655 if (!strcmp(mpoint, "/"))
1656 flags |= CHUNK_IS_ROOT;
1659 sz = space_free(c1);
1660 if (sz > space_free(c1)) {
1661 msgConfirm("Not enough free space to create partition: %s", mpoint);
1662 status = DITEM_FAILURE;
1665 if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
1666 (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
1667 msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
1668 status = DITEM_FAILURE;
1672 pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
1673 tmp->private_free = safe_free;
1674 pi->newfs_data.newfs_ufs.softupdates = soft;
1679 /* Must be something we can set a mountpoint for */
1680 cp = variable_get(c1->name);
1682 char mpoint[50], do_newfs[8];
1683 Boolean newfs = FALSE;
1686 if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
1687 msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
1688 status = DITEM_FAILURE;
1691 newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
1692 if (c1->private_data) {
1693 p = c1->private_data;
1694 p->do_newfs = newfs;
1695 strcpy(p->mountpoint, mpoint);
1698 c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
1699 c1->private_free = safe_free;
1701 if (!strcmp(mpoint, "/"))
1702 c1->flags |= CHUNK_IS_ROOT;
1704 c1->flags &= ~CHUNK_IS_ROOT;
1708 if (status == DITEM_SUCCESS)
1709 variable_set2(DISK_LABELLED, "yes", 0);