2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2011 Nathan Whitehorn
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 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/param.h>
42 #include "diskeditor.h"
45 struct pmetadata_head part_metadata;
46 static int sade_mode = 0;
48 static int apply_changes(struct gmesh *mesh);
49 static void apply_workaround(struct gmesh *mesh);
50 static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems);
51 static void add_geom_children(struct ggeom *gp, int recurse,
52 struct partedit_item **items, int *nitems);
53 static void init_fstab_metadata(void);
54 static void get_mount_points(struct partedit_item *items, int nitems);
55 static int validate_setup(void);
58 sigint_handler(int sig)
62 /* Revert all changes and exit dialog-mode cleanly on SIGINT */
64 gpart_revert_all(&mesh);
65 geom_deletetree(&mesh);
73 main(int argc, const char **argv)
75 struct partition_metadata *md;
76 const char *progname, *prompt;
77 struct partedit_item *items = NULL;
79 int i, op, nitems, nscroll;
82 progname = getprogname();
83 if (strcmp(progname, "sade") == 0)
86 TAILQ_INIT(&part_metadata);
88 init_fstab_metadata();
90 init_dialog(stdin, stdout);
92 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
93 dialog_vars.item_help = TRUE;
96 /* Revert changes on SIGINT */
97 signal(SIGINT, sigint_handler);
99 if (strcmp(progname, "autopart") == 0) { /* Guided */
100 prompt = "Please review the disk setup. When complete, press "
101 "the Finish button.";
102 /* Experimental ZFS autopartition support */
103 if (argc > 1 && strcmp(argv[1], "zfs") == 0) {
108 } else if (strcmp(progname, "scriptedpart") == 0) {
109 error = scripted_editor(argc, argv);
116 prompt = "Create partitions for FreeBSD. No changes will be "
117 "made until you select Finish.";
120 /* Show the part editor either immediately, or to confirm wizard */
121 while (prompt != NULL) {
125 error = geom_gettree(&mesh);
127 items = read_geom_mesh(&mesh, &nitems);
128 if (error || items == NULL) {
129 dialog_msgbox("Error", "No disks found. If you need to "
130 "install a kernel driver, choose Shell at the "
131 "installation menu.", 0, 0, TRUE);
135 get_mount_points(items, nitems);
139 op = diskeditor_show("Partition Editor", prompt,
140 items, nitems, &i, &nscroll);
144 gpart_create((struct gprovider *)(items[i].cookie),
145 NULL, NULL, NULL, NULL, 1);
148 gpart_delete((struct gprovider *)(items[i].cookie));
151 gpart_edit((struct gprovider *)(items[i].cookie));
154 gpart_revert_all(&mesh);
155 while ((md = TAILQ_FIRST(&part_metadata)) != NULL) {
156 if (md->fstab != NULL) {
157 free(md->fstab->fs_spec);
158 free(md->fstab->fs_file);
159 free(md->fstab->fs_vfstype);
160 free(md->fstab->fs_mntops);
161 free(md->fstab->fs_type);
164 if (md->newfs != NULL)
168 TAILQ_REMOVE(&part_metadata, md, metadata);
171 init_fstab_metadata();
179 if (op == 5) { /* Finished */
180 dialog_vars.ok_label = __DECONST(char *, "Commit");
181 dialog_vars.extra_label =
182 __DECONST(char *, "Revert & Exit");
183 dialog_vars.extra_button = TRUE;
184 dialog_vars.cancel_label = __DECONST(char *, "Back");
185 op = dialog_yesno("Confirmation", "Your changes will "
186 "now be written to disk. If you have chosen to "
187 "overwrite existing data, it will be PERMANENTLY "
188 "ERASED. Are you sure you want to commit your "
190 dialog_vars.ok_label = NULL;
191 dialog_vars.extra_button = FALSE;
192 dialog_vars.cancel_label = NULL;
194 if (op == 0 && validate_setup()) { /* Save */
195 error = apply_changes(&mesh);
197 apply_workaround(&mesh);
199 } else if (op == 3) { /* Quit */
200 gpart_revert_all(&mesh);
206 geom_deletetree(&mesh);
210 if (prompt == NULL) {
211 error = geom_gettree(&mesh);
212 if (validate_setup()) {
213 error = apply_changes(&mesh);
215 gpart_revert_all(&mesh);
220 geom_deletetree(&mesh);
227 struct partition_metadata *
228 get_part_metadata(const char *name, int create)
230 struct partition_metadata *md;
232 TAILQ_FOREACH(md, &part_metadata, metadata)
233 if (md->name != NULL && strcmp(md->name, name) == 0)
236 if (md == NULL && create) {
237 md = calloc(1, sizeof(*md));
238 md->name = strdup(name);
239 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
246 delete_part_metadata(const char *name)
248 struct partition_metadata *md;
250 TAILQ_FOREACH(md, &part_metadata, metadata) {
251 if (md->name != NULL && strcmp(md->name, name) == 0) {
252 if (md->fstab != NULL) {
253 free(md->fstab->fs_spec);
254 free(md->fstab->fs_file);
255 free(md->fstab->fs_vfstype);
256 free(md->fstab->fs_mntops);
257 free(md->fstab->fs_type);
260 if (md->newfs != NULL)
264 TAILQ_REMOVE(&part_metadata, md, metadata);
274 struct partition_metadata *md, *root = NULL;
277 TAILQ_FOREACH(md, &part_metadata, metadata) {
278 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0)
281 /* XXX: Check for duplicate mountpoints */
285 dialog_msgbox("Error", "No root partition was found. "
286 "The root FreeBSD partition must have a mountpoint of '/'.",
292 * Check for root partitions that we aren't formatting, which is
295 if (root->newfs == NULL && !sade_mode) {
296 dialog_vars.defaultno = TRUE;
297 cancel = dialog_yesno("Warning", "The chosen root partition "
298 "has a preexisting filesystem. If it contains an existing "
299 "FreeBSD system, please update it with freebsd-update "
300 "instead of installing a new system on it. The partition "
301 "can also be erased by pressing \"No\" and then deleting "
302 "and recreating it. Are you sure you want to proceed?",
304 dialog_vars.defaultno = FALSE;
313 apply_changes(struct gmesh *mesh)
315 struct partition_metadata *md;
317 int i, nitems, error;
319 const char *fstab_path;
322 nitems = 1; /* Partition table changes */
323 TAILQ_FOREACH(md, &part_metadata, metadata) {
324 if (md->newfs != NULL)
327 items = calloc(nitems * 2, sizeof(const char *));
328 items[0] = "Writing partition tables";
329 items[1] = "7"; /* In progress */
331 TAILQ_FOREACH(md, &part_metadata, metadata) {
332 if (md->newfs != NULL) {
335 sprintf(item, "Initializing %s", md->name);
337 items[i*2 + 1] = "Pending";
343 dialog_mixedgauge("Initializing",
344 "Initializing file systems. Please wait.", 0, 0, i*100/nitems,
345 nitems, __DECONST(char **, items));
347 items[i*2 + 1] = "3";
350 if (getenv("BSDINSTALL_LOG") == NULL)
351 setenv("BSDINSTALL_LOG", "/dev/null", 1);
353 TAILQ_FOREACH(md, &part_metadata, metadata) {
354 if (md->newfs != NULL) {
355 items[i*2 + 1] = "7"; /* In progress */
356 dialog_mixedgauge("Initializing",
357 "Initializing file systems. Please wait.", 0, 0,
358 i*100/nitems, nitems, __DECONST(char **, items));
359 sprintf(message, "(echo %s; %s) >>%s 2>>%s",
360 md->newfs, md->newfs, getenv("BSDINSTALL_LOG"),
361 getenv("BSDINSTALL_LOG"));
362 error = system(message);
363 items[i*2 + 1] = (error == 0) ? "3" : "1";
367 dialog_mixedgauge("Initializing",
368 "Initializing file systems. Please wait.", 0, 0,
369 i*100/nitems, nitems, __DECONST(char **, items));
371 for (i = 1; i < nitems; i++)
372 free(__DECONST(char *, items[i*2]));
375 if (getenv("PATH_FSTAB") != NULL)
376 fstab_path = getenv("PATH_FSTAB");
378 fstab_path = "/etc/fstab";
379 fstab = fopen(fstab_path, "w+");
381 sprintf(message, "Cannot open fstab file %s for writing (%s)\n",
382 getenv("PATH_FSTAB"), strerror(errno));
383 dialog_msgbox("Error", message, 0, 0, TRUE);
386 fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n");
387 TAILQ_FOREACH(md, &part_metadata, metadata) {
388 if (md->fstab != NULL)
389 fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n",
390 md->fstab->fs_spec, md->fstab->fs_file,
391 md->fstab->fs_vfstype, md->fstab->fs_mntops,
392 md->fstab->fs_freq, md->fstab->fs_passno);
400 apply_workaround(struct gmesh *mesh)
402 struct gclass *classp;
405 const char *scheme = NULL, *modified = NULL;
407 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
408 if (strcmp(classp->lg_name, "PART") == 0)
412 if (strcmp(classp->lg_name, "PART") != 0) {
413 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
417 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
418 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
419 if (strcmp(gc->lg_name, "scheme") == 0) {
421 } else if (strcmp(gc->lg_name, "modified") == 0) {
422 modified = gc->lg_val;
426 if (scheme && strcmp(scheme, "GPT") == 0 &&
427 modified && strcmp(modified, "true") == 0) {
428 if (getenv("WORKAROUND_LENOVO"))
429 gpart_set_root(gp->lg_name, "lenovofix");
430 if (getenv("WORKAROUND_GPTACTIVE"))
431 gpart_set_root(gp->lg_name, "active");
436 static struct partedit_item *
437 read_geom_mesh(struct gmesh *mesh, int *nitems)
439 struct gclass *classp;
441 struct partedit_item *items;
447 * Build the device table. First add all disks (and CDs).
450 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
451 if (strcmp(classp->lg_name, "DISK") != 0 &&
452 strcmp(classp->lg_name, "MD") != 0)
455 /* Now recurse into all children */
456 LIST_FOREACH(gp, &classp->lg_geom, lg_geom)
457 add_geom_children(gp, 0, &items, nitems);
464 add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items,
467 struct gconsumer *cp;
468 struct gprovider *pp;
471 if (strcmp(gp->lg_class->lg_name, "PART") == 0 &&
472 !LIST_EMPTY(&gp->lg_config)) {
473 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
474 if (strcmp(gc->lg_name, "scheme") == 0)
475 (*items)[*nitems-1].type = gc->lg_val;
479 if (LIST_EMPTY(&gp->lg_provider))
482 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
483 if (strcmp(gp->lg_class->lg_name, "LABEL") == 0)
486 /* Skip WORM media */
487 if (strncmp(pp->lg_name, "cd", 2) == 0)
490 *items = realloc(*items,
491 (*nitems+1)*sizeof(struct partedit_item));
492 (*items)[*nitems].indentation = recurse;
493 (*items)[*nitems].name = pp->lg_name;
494 (*items)[*nitems].size = pp->lg_mediasize;
495 (*items)[*nitems].mountpoint = NULL;
496 (*items)[*nitems].type = "";
497 (*items)[*nitems].cookie = pp;
499 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
500 if (strcmp(gc->lg_name, "type") == 0)
501 (*items)[*nitems].type = gc->lg_val;
504 /* Skip swap-backed MD devices */
505 if (strcmp(gp->lg_class->lg_name, "MD") == 0 &&
506 strcmp((*items)[*nitems].type, "swap") == 0)
511 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
512 add_geom_children(cp->lg_geom, recurse+1, items,
515 /* Only use first provider for acd */
516 if (strcmp(gp->lg_class->lg_name, "ACD") == 0)
522 init_fstab_metadata(void)
525 struct partition_metadata *md;
528 while ((fstab = getfsent()) != NULL) {
529 md = calloc(1, sizeof(struct partition_metadata));
532 if (strncmp(fstab->fs_spec, "/dev/", 5) == 0)
533 md->name = strdup(&fstab->fs_spec[5]);
535 md->fstab = malloc(sizeof(struct fstab));
536 md->fstab->fs_spec = strdup(fstab->fs_spec);
537 md->fstab->fs_file = strdup(fstab->fs_file);
538 md->fstab->fs_vfstype = strdup(fstab->fs_vfstype);
539 md->fstab->fs_mntops = strdup(fstab->fs_mntops);
540 md->fstab->fs_type = strdup(fstab->fs_type);
541 md->fstab->fs_freq = fstab->fs_freq;
542 md->fstab->fs_passno = fstab->fs_passno;
546 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
551 get_mount_points(struct partedit_item *items, int nitems)
553 struct partition_metadata *md;
556 for (i = 0; i < nitems; i++) {
557 TAILQ_FOREACH(md, &part_metadata, metadata) {
558 if (md->name != NULL && md->fstab != NULL &&
559 strcmp(md->name, items[i].name) == 0) {
560 items[i].mountpoint = md->fstab->fs_file;