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>
44 #include "diskeditor.h"
47 struct pmetadata_head part_metadata;
49 static int sade_mode = 0;
51 static int apply_changes(struct gmesh *mesh);
52 static void apply_workaround(struct gmesh *mesh);
53 static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems);
54 static void add_geom_children(struct ggeom *gp, int recurse,
55 struct partedit_item **items, int *nitems);
56 static void init_fstab_metadata(void);
57 static void get_mount_points(struct partedit_item *items, int nitems);
58 static int validate_setup(void);
61 sigint_handler(int sig)
65 /* Revert all changes and exit dialog-mode cleanly on SIGINT */
67 gpart_revert_all(&mesh);
68 geom_deletetree(&mesh);
78 main(int argc, const char **argv)
80 struct partition_metadata *md;
81 const char *progname, *prompt, *tmpdir;
82 struct partedit_item *items = NULL;
84 int i, op, nitems, nscroll;
87 progname = getprogname();
88 if (strcmp(progname, "sade") == 0)
91 TAILQ_INIT(&part_metadata);
93 tmpdir = getenv("TMPDIR");
96 tmpdfd = open(tmpdir, O_DIRECTORY);
98 err(EX_OSERR, "%s", tmpdir);
99 unlinkat(tmpdfd, "bsdinstall-esps", 0);
101 init_fstab_metadata();
103 init_dialog(stdin, stdout);
105 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
106 dialog_vars.item_help = TRUE;
109 /* Revert changes on SIGINT */
110 signal(SIGINT, sigint_handler);
112 if (strcmp(progname, "autopart") == 0) { /* Guided */
113 prompt = "Please review the disk setup. When complete, press "
114 "the Finish button.";
115 /* Experimental ZFS autopartition support */
116 if (argc > 1 && strcmp(argv[1], "zfs") == 0) {
121 } else if (strcmp(progname, "scriptedpart") == 0) {
122 error = scripted_editor(argc, argv);
129 prompt = "Create partitions for FreeBSD. No changes will be "
130 "made until you select Finish.";
133 /* Show the part editor either immediately, or to confirm wizard */
134 while (prompt != NULL) {
138 error = geom_gettree(&mesh);
140 items = read_geom_mesh(&mesh, &nitems);
141 if (error || items == NULL) {
142 dialog_msgbox("Error", "No disks found. If you need to "
143 "install a kernel driver, choose Shell at the "
144 "installation menu.", 0, 0, TRUE);
148 get_mount_points(items, nitems);
152 op = diskeditor_show("Partition Editor", prompt,
153 items, nitems, &i, &nscroll);
157 gpart_create((struct gprovider *)(items[i].cookie),
158 NULL, NULL, NULL, NULL, 1);
161 gpart_delete((struct gprovider *)(items[i].cookie));
164 gpart_edit((struct gprovider *)(items[i].cookie));
167 gpart_revert_all(&mesh);
168 while ((md = TAILQ_FIRST(&part_metadata)) != NULL) {
169 if (md->fstab != NULL) {
170 free(md->fstab->fs_spec);
171 free(md->fstab->fs_file);
172 free(md->fstab->fs_vfstype);
173 free(md->fstab->fs_mntops);
174 free(md->fstab->fs_type);
177 if (md->newfs != NULL)
181 TAILQ_REMOVE(&part_metadata, md, metadata);
184 init_fstab_metadata();
192 if (op == 5) { /* Finished */
193 dialog_vars.ok_label = __DECONST(char *, "Commit");
194 dialog_vars.extra_label =
195 __DECONST(char *, "Revert & Exit");
196 dialog_vars.extra_button = TRUE;
197 dialog_vars.cancel_label = __DECONST(char *, "Back");
198 op = dialog_yesno("Confirmation", "Your changes will "
199 "now be written to disk. If you have chosen to "
200 "overwrite existing data, it will be PERMANENTLY "
201 "ERASED. Are you sure you want to commit your "
203 dialog_vars.ok_label = NULL;
204 dialog_vars.extra_button = FALSE;
205 dialog_vars.cancel_label = NULL;
207 if (op == 0 && validate_setup()) { /* Save */
208 error = apply_changes(&mesh);
210 apply_workaround(&mesh);
212 } else if (op == 3) { /* Quit */
213 gpart_revert_all(&mesh);
219 geom_deletetree(&mesh);
223 if (prompt == NULL) {
224 error = geom_gettree(&mesh);
225 if (validate_setup()) {
226 error = apply_changes(&mesh);
228 gpart_revert_all(&mesh);
233 geom_deletetree(&mesh);
241 struct partition_metadata *
242 get_part_metadata(const char *name, int create)
244 struct partition_metadata *md;
246 TAILQ_FOREACH(md, &part_metadata, metadata)
247 if (md->name != NULL && strcmp(md->name, name) == 0)
250 if (md == NULL && create) {
251 md = calloc(1, sizeof(*md));
252 md->name = strdup(name);
253 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
260 delete_part_metadata(const char *name)
262 struct partition_metadata *md;
264 TAILQ_FOREACH(md, &part_metadata, metadata) {
265 if (md->name != NULL && strcmp(md->name, name) == 0) {
266 if (md->fstab != NULL) {
267 free(md->fstab->fs_spec);
268 free(md->fstab->fs_file);
269 free(md->fstab->fs_vfstype);
270 free(md->fstab->fs_mntops);
271 free(md->fstab->fs_type);
274 if (md->newfs != NULL)
278 TAILQ_REMOVE(&part_metadata, md, metadata);
288 struct partition_metadata *md, *root = NULL;
291 TAILQ_FOREACH(md, &part_metadata, metadata) {
292 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0)
295 /* XXX: Check for duplicate mountpoints */
299 dialog_msgbox("Error", "No root partition was found. "
300 "The root FreeBSD partition must have a mountpoint of '/'.",
306 * Check for root partitions that we aren't formatting, which is
309 if (root->newfs == NULL && !sade_mode) {
310 dialog_vars.defaultno = TRUE;
311 cancel = dialog_yesno("Warning", "The chosen root partition "
312 "has a preexisting filesystem. If it contains an existing "
313 "FreeBSD system, please update it with freebsd-update "
314 "instead of installing a new system on it. The partition "
315 "can also be erased by pressing \"No\" and then deleting "
316 "and recreating it. Are you sure you want to proceed?",
318 dialog_vars.defaultno = FALSE;
327 mountpoint_sorter(const void *xa, const void *xb)
329 struct partition_metadata *a = *(struct partition_metadata **)xa;
330 struct partition_metadata *b = *(struct partition_metadata **)xb;
332 if (a->fstab == NULL && b->fstab == NULL)
334 if (a->fstab == NULL)
336 if (b->fstab == NULL)
339 return strcmp(a->fstab->fs_file, b->fstab->fs_file);
343 apply_changes(struct gmesh *mesh)
345 struct partition_metadata *md;
347 int i, nitems, error;
349 const char *fstab_path;
352 nitems = 1; /* Partition table changes */
353 TAILQ_FOREACH(md, &part_metadata, metadata) {
354 if (md->newfs != NULL)
357 items = calloc(nitems * 2, sizeof(const char *));
358 items[0] = "Writing partition tables";
359 items[1] = "7"; /* In progress */
361 TAILQ_FOREACH(md, &part_metadata, metadata) {
362 if (md->newfs != NULL) {
365 sprintf(item, "Initializing %s", md->name);
367 items[i*2 + 1] = "Pending";
373 dialog_mixedgauge("Initializing",
374 "Initializing file systems. Please wait.", 0, 0, i*100/nitems,
375 nitems, __DECONST(char **, items));
377 items[i*2 + 1] = "3";
380 if (getenv("BSDINSTALL_LOG") == NULL)
381 setenv("BSDINSTALL_LOG", "/dev/null", 1);
383 TAILQ_FOREACH(md, &part_metadata, metadata) {
384 if (md->newfs != NULL) {
385 items[i*2 + 1] = "7"; /* In progress */
386 dialog_mixedgauge("Initializing",
387 "Initializing file systems. Please wait.", 0, 0,
388 i*100/nitems, nitems, __DECONST(char **, items));
389 sprintf(message, "(echo %s; %s) >>%s 2>>%s",
390 md->newfs, md->newfs, getenv("BSDINSTALL_LOG"),
391 getenv("BSDINSTALL_LOG"));
392 error = system(message);
393 items[i*2 + 1] = (error == 0) ? "3" : "1";
397 dialog_mixedgauge("Initializing",
398 "Initializing file systems. Please wait.", 0, 0,
399 i*100/nitems, nitems, __DECONST(char **, items));
401 for (i = 1; i < nitems; i++)
402 free(__DECONST(char *, items[i*2]));
405 /* Sort filesystems for fstab so that mountpoints are ordered */
407 struct partition_metadata **tobesorted;
408 struct partition_metadata *tmp;
410 TAILQ_FOREACH(md, &part_metadata, metadata)
412 tobesorted = malloc(sizeof(struct partition_metadata *)*nparts);
414 TAILQ_FOREACH_SAFE(md, &part_metadata, metadata, tmp) {
415 tobesorted[nparts++] = md;
416 TAILQ_REMOVE(&part_metadata, md, metadata);
418 qsort(tobesorted, nparts, sizeof(tobesorted[0]),
421 /* Now re-add everything */
423 TAILQ_INSERT_HEAD(&part_metadata,
424 tobesorted[nparts], metadata);
428 if (getenv("PATH_FSTAB") != NULL)
429 fstab_path = getenv("PATH_FSTAB");
431 fstab_path = "/etc/fstab";
432 fstab = fopen(fstab_path, "w+");
434 sprintf(message, "Cannot open fstab file %s for writing (%s)\n",
435 getenv("PATH_FSTAB"), strerror(errno));
436 dialog_msgbox("Error", message, 0, 0, TRUE);
439 fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n");
440 TAILQ_FOREACH(md, &part_metadata, metadata) {
441 if (md->fstab != NULL)
442 fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n",
443 md->fstab->fs_spec, md->fstab->fs_file,
444 md->fstab->fs_vfstype, md->fstab->fs_mntops,
445 md->fstab->fs_freq, md->fstab->fs_passno);
453 apply_workaround(struct gmesh *mesh)
455 struct gclass *classp;
458 const char *scheme = NULL, *modified = NULL;
460 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
461 if (strcmp(classp->lg_name, "PART") == 0)
465 if (strcmp(classp->lg_name, "PART") != 0) {
466 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
470 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
471 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
472 if (strcmp(gc->lg_name, "scheme") == 0) {
474 } else if (strcmp(gc->lg_name, "modified") == 0) {
475 modified = gc->lg_val;
479 if (scheme && strcmp(scheme, "GPT") == 0 &&
480 modified && strcmp(modified, "true") == 0) {
481 if (getenv("WORKAROUND_LENOVO"))
482 gpart_set_root(gp->lg_name, "lenovofix");
483 if (getenv("WORKAROUND_GPTACTIVE"))
484 gpart_set_root(gp->lg_name, "active");
489 static struct partedit_item *
490 read_geom_mesh(struct gmesh *mesh, int *nitems)
492 struct gclass *classp;
494 struct partedit_item *items;
500 * Build the device table. First add all disks (and CDs).
503 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
504 if (strcmp(classp->lg_name, "DISK") != 0 &&
505 strcmp(classp->lg_name, "MD") != 0)
508 /* Now recurse into all children */
509 LIST_FOREACH(gp, &classp->lg_geom, lg_geom)
510 add_geom_children(gp, 0, &items, nitems);
517 add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items,
520 struct gconsumer *cp;
521 struct gprovider *pp;
524 if (strcmp(gp->lg_class->lg_name, "PART") == 0 &&
525 !LIST_EMPTY(&gp->lg_config)) {
526 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
527 if (strcmp(gc->lg_name, "scheme") == 0)
528 (*items)[*nitems-1].type = gc->lg_val;
532 if (LIST_EMPTY(&gp->lg_provider))
535 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
536 if (strcmp(gp->lg_class->lg_name, "LABEL") == 0)
539 /* Skip WORM media */
540 if (strncmp(pp->lg_name, "cd", 2) == 0)
543 *items = realloc(*items,
544 (*nitems+1)*sizeof(struct partedit_item));
545 (*items)[*nitems].indentation = recurse;
546 (*items)[*nitems].name = pp->lg_name;
547 (*items)[*nitems].size = pp->lg_mediasize;
548 (*items)[*nitems].mountpoint = NULL;
549 (*items)[*nitems].type = "";
550 (*items)[*nitems].cookie = pp;
552 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
553 if (strcmp(gc->lg_name, "type") == 0)
554 (*items)[*nitems].type = gc->lg_val;
557 /* Skip swap-backed MD devices */
558 if (strcmp(gp->lg_class->lg_name, "MD") == 0 &&
559 strcmp((*items)[*nitems].type, "swap") == 0)
564 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
565 add_geom_children(cp->lg_geom, recurse+1, items,
568 /* Only use first provider for acd */
569 if (strcmp(gp->lg_class->lg_name, "ACD") == 0)
575 init_fstab_metadata(void)
578 struct partition_metadata *md;
581 while ((fstab = getfsent()) != NULL) {
582 md = calloc(1, sizeof(struct partition_metadata));
585 if (strncmp(fstab->fs_spec, "/dev/", 5) == 0)
586 md->name = strdup(&fstab->fs_spec[5]);
588 md->fstab = malloc(sizeof(struct fstab));
589 md->fstab->fs_spec = strdup(fstab->fs_spec);
590 md->fstab->fs_file = strdup(fstab->fs_file);
591 md->fstab->fs_vfstype = strdup(fstab->fs_vfstype);
592 md->fstab->fs_mntops = strdup(fstab->fs_mntops);
593 md->fstab->fs_type = strdup(fstab->fs_type);
594 md->fstab->fs_freq = fstab->fs_freq;
595 md->fstab->fs_passno = fstab->fs_passno;
599 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
604 get_mount_points(struct partedit_item *items, int nitems)
606 struct partition_metadata *md;
609 for (i = 0; i < nitems; i++) {
610 TAILQ_FOREACH(md, &part_metadata, metadata) {
611 if (md->name != NULL && md->fstab != NULL &&
612 strcmp(md->name, items[i].name) == 0) {
613 items[i].mountpoint = md->fstab->fs_file;