2 * Copyright (c) 2011 Nathan Whitehorn
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
40 #include "diskeditor.h"
43 struct pmetadata_head part_metadata;
44 static int sade_mode = 0;
46 static int apply_changes(struct gmesh *mesh);
47 static void apply_workaround(struct gmesh *mesh);
48 static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems);
49 static void add_geom_children(struct ggeom *gp, int recurse,
50 struct partedit_item **items, int *nitems);
51 static void init_fstab_metadata(void);
52 static void get_mount_points(struct partedit_item *items, int nitems);
53 static int validate_setup(void);
56 sigint_handler(int sig)
60 /* Revert all changes and exit dialog-mode cleanly on SIGINT */
62 gpart_revert_all(&mesh);
63 geom_deletetree(&mesh);
71 main(int argc, const char **argv)
73 struct partition_metadata *md;
75 struct partedit_item *items = NULL;
77 int i, op, nitems, nscroll;
80 if (strcmp(basename(argv[0]), "sade") == 0)
83 TAILQ_INIT(&part_metadata);
85 init_fstab_metadata();
87 init_dialog(stdin, stdout);
89 dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer");
90 dialog_vars.item_help = TRUE;
93 /* Revert changes on SIGINT */
94 signal(SIGINT, sigint_handler);
96 if (strcmp(basename(argv[0]), "autopart") == 0) { /* Guided */
97 prompt = "Please review the disk setup. When complete, press "
99 /* Experimental ZFS autopartition support */
100 if (argc > 1 && strcmp(argv[1], "zfs") == 0) {
105 } else if (strcmp(basename(argv[0]), "scriptedpart") == 0) {
106 error = scripted_editor(argc, argv);
113 prompt = "Create partitions for FreeBSD. No changes will be "
114 "made until you select Finish.";
117 /* Show the part editor either immediately, or to confirm wizard */
118 while (prompt != NULL) {
122 error = geom_gettree(&mesh);
124 items = read_geom_mesh(&mesh, &nitems);
125 if (error || items == NULL) {
126 dialog_msgbox("Error", "No disks found. If you need to "
127 "install a kernel driver, choose Shell at the "
128 "installation menu.", 0, 0, TRUE);
132 get_mount_points(items, nitems);
136 op = diskeditor_show("Partition Editor", prompt,
137 items, nitems, &i, &nscroll);
141 gpart_create((struct gprovider *)(items[i].cookie),
142 NULL, NULL, NULL, NULL, 1);
145 gpart_delete((struct gprovider *)(items[i].cookie));
148 gpart_edit((struct gprovider *)(items[i].cookie));
151 gpart_revert_all(&mesh);
152 while ((md = TAILQ_FIRST(&part_metadata)) != NULL) {
153 if (md->fstab != NULL) {
154 free(md->fstab->fs_spec);
155 free(md->fstab->fs_file);
156 free(md->fstab->fs_vfstype);
157 free(md->fstab->fs_mntops);
158 free(md->fstab->fs_type);
161 if (md->newfs != NULL)
165 TAILQ_REMOVE(&part_metadata, md, metadata);
168 init_fstab_metadata();
176 if (op == 5) { /* Finished */
177 dialog_vars.ok_label = __DECONST(char *, "Commit");
178 dialog_vars.extra_label =
179 __DECONST(char *, "Revert & Exit");
180 dialog_vars.extra_button = TRUE;
181 dialog_vars.cancel_label = __DECONST(char *, "Back");
182 op = dialog_yesno("Confirmation", "Your changes will "
183 "now be written to disk. If you have chosen to "
184 "overwrite existing data, it will be PERMANENTLY "
185 "ERASED. Are you sure you want to commit your "
187 dialog_vars.ok_label = NULL;
188 dialog_vars.extra_button = FALSE;
189 dialog_vars.cancel_label = NULL;
191 if (op == 0 && validate_setup()) { /* Save */
192 error = apply_changes(&mesh);
194 apply_workaround(&mesh);
196 } else if (op == 3) { /* Quit */
197 gpart_revert_all(&mesh);
203 geom_deletetree(&mesh);
207 if (prompt == NULL) {
208 error = geom_gettree(&mesh);
209 if (validate_setup()) {
210 error = apply_changes(&mesh);
212 gpart_revert_all(&mesh);
217 geom_deletetree(&mesh);
224 struct partition_metadata *
225 get_part_metadata(const char *name, int create)
227 struct partition_metadata *md;
229 TAILQ_FOREACH(md, &part_metadata, metadata)
230 if (md->name != NULL && strcmp(md->name, name) == 0)
233 if (md == NULL && create) {
234 md = calloc(1, sizeof(*md));
235 md->name = strdup(name);
236 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
243 delete_part_metadata(const char *name)
245 struct partition_metadata *md;
247 TAILQ_FOREACH(md, &part_metadata, metadata) {
248 if (md->name != NULL && strcmp(md->name, name) == 0) {
249 if (md->fstab != NULL) {
250 free(md->fstab->fs_spec);
251 free(md->fstab->fs_file);
252 free(md->fstab->fs_vfstype);
253 free(md->fstab->fs_mntops);
254 free(md->fstab->fs_type);
257 if (md->newfs != NULL)
261 TAILQ_REMOVE(&part_metadata, md, metadata);
271 struct partition_metadata *md, *root = NULL;
274 TAILQ_FOREACH(md, &part_metadata, metadata) {
275 if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0)
278 /* XXX: Check for duplicate mountpoints */
282 dialog_msgbox("Error", "No root partition was found. "
283 "The root FreeBSD partition must have a mountpoint of '/'.",
289 * Check for root partitions that we aren't formatting, which is
292 if (root->newfs == NULL && !sade_mode) {
293 dialog_vars.defaultno = TRUE;
294 cancel = dialog_yesno("Warning", "The chosen root partition "
295 "has a preexisting filesystem. If it contains an existing "
296 "FreeBSD system, please update it with freebsd-update "
297 "instead of installing a new system on it. The partition "
298 "can also be erased by pressing \"No\" and then deleting "
299 "and recreating it. Are you sure you want to proceed?",
301 dialog_vars.defaultno = FALSE;
310 apply_changes(struct gmesh *mesh)
312 struct partition_metadata *md;
314 int i, nitems, error;
316 const char *fstab_path;
319 nitems = 1; /* Partition table changes */
320 TAILQ_FOREACH(md, &part_metadata, metadata) {
321 if (md->newfs != NULL)
324 items = calloc(nitems * 2, sizeof(const char *));
325 items[0] = "Writing partition tables";
326 items[1] = "7"; /* In progress */
328 TAILQ_FOREACH(md, &part_metadata, metadata) {
329 if (md->newfs != NULL) {
332 sprintf(item, "Initializing %s", md->name);
334 items[i*2 + 1] = "Pending";
340 dialog_mixedgauge("Initializing",
341 "Initializing file systems. Please wait.", 0, 0, i*100/nitems,
342 nitems, __DECONST(char **, items));
344 items[i*2 + 1] = "3";
347 if (getenv("BSDINSTALL_LOG") == NULL)
348 setenv("BSDINSTALL_LOG", "/dev/null", 1);
350 TAILQ_FOREACH(md, &part_metadata, metadata) {
351 if (md->newfs != NULL) {
352 items[i*2 + 1] = "7"; /* In progress */
353 dialog_mixedgauge("Initializing",
354 "Initializing file systems. Please wait.", 0, 0,
355 i*100/nitems, nitems, __DECONST(char **, items));
356 sprintf(message, "(echo %s; %s) >>%s 2>>%s",
357 md->newfs, md->newfs, getenv("BSDINSTALL_LOG"),
358 getenv("BSDINSTALL_LOG"));
359 error = system(message);
360 items[i*2 + 1] = (error == 0) ? "3" : "1";
364 dialog_mixedgauge("Initializing",
365 "Initializing file systems. Please wait.", 0, 0,
366 i*100/nitems, nitems, __DECONST(char **, items));
368 for (i = 1; i < nitems; i++)
369 free(__DECONST(char *, items[i*2]));
372 if (getenv("PATH_FSTAB") != NULL)
373 fstab_path = getenv("PATH_FSTAB");
375 fstab_path = "/etc/fstab";
376 fstab = fopen(fstab_path, "w+");
378 sprintf(message, "Cannot open fstab file %s for writing (%s)\n",
379 getenv("PATH_FSTAB"), strerror(errno));
380 dialog_msgbox("Error", message, 0, 0, TRUE);
383 fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n");
384 TAILQ_FOREACH(md, &part_metadata, metadata) {
385 if (md->fstab != NULL)
386 fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n",
387 md->fstab->fs_spec, md->fstab->fs_file,
388 md->fstab->fs_vfstype, md->fstab->fs_mntops,
389 md->fstab->fs_freq, md->fstab->fs_passno);
397 apply_workaround(struct gmesh *mesh)
399 struct gclass *classp;
402 const char *scheme = NULL, *modified = NULL;
404 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
405 if (strcmp(classp->lg_name, "PART") == 0)
409 if (strcmp(classp->lg_name, "PART") != 0) {
410 dialog_msgbox("Error", "gpart not found!", 0, 0, TRUE);
414 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
415 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
416 if (strcmp(gc->lg_name, "scheme") == 0) {
418 } else if (strcmp(gc->lg_name, "modified") == 0) {
419 modified = gc->lg_val;
423 if (scheme && strcmp(scheme, "GPT") == 0 &&
424 modified && strcmp(modified, "true") == 0) {
425 if (getenv("WORKAROUND_LENOVO"))
426 gpart_set_root(gp->lg_name, "lenovofix");
427 if (getenv("WORKAROUND_GPTACTIVE"))
428 gpart_set_root(gp->lg_name, "active");
433 static struct partedit_item *
434 read_geom_mesh(struct gmesh *mesh, int *nitems)
436 struct gclass *classp;
438 struct partedit_item *items;
444 * Build the device table. First add all disks (and CDs).
447 LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
448 if (strcmp(classp->lg_name, "DISK") != 0 &&
449 strcmp(classp->lg_name, "MD") != 0)
452 /* Now recurse into all children */
453 LIST_FOREACH(gp, &classp->lg_geom, lg_geom)
454 add_geom_children(gp, 0, &items, nitems);
461 add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items,
464 struct gconsumer *cp;
465 struct gprovider *pp;
468 if (strcmp(gp->lg_class->lg_name, "PART") == 0 &&
469 !LIST_EMPTY(&gp->lg_config)) {
470 LIST_FOREACH(gc, &gp->lg_config, lg_config) {
471 if (strcmp(gc->lg_name, "scheme") == 0)
472 (*items)[*nitems-1].type = gc->lg_val;
476 if (LIST_EMPTY(&gp->lg_provider))
479 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
480 if (strcmp(gp->lg_class->lg_name, "LABEL") == 0)
483 /* Skip WORM media */
484 if (strncmp(pp->lg_name, "cd", 2) == 0)
487 *items = realloc(*items,
488 (*nitems+1)*sizeof(struct partedit_item));
489 (*items)[*nitems].indentation = recurse;
490 (*items)[*nitems].name = pp->lg_name;
491 (*items)[*nitems].size = pp->lg_mediasize;
492 (*items)[*nitems].mountpoint = NULL;
493 (*items)[*nitems].type = "";
494 (*items)[*nitems].cookie = pp;
496 LIST_FOREACH(gc, &pp->lg_config, lg_config) {
497 if (strcmp(gc->lg_name, "type") == 0)
498 (*items)[*nitems].type = gc->lg_val;
501 /* Skip swap-backed MD devices */
502 if (strcmp(gp->lg_class->lg_name, "MD") == 0 &&
503 strcmp((*items)[*nitems].type, "swap") == 0)
508 LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers)
509 add_geom_children(cp->lg_geom, recurse+1, items,
512 /* Only use first provider for acd */
513 if (strcmp(gp->lg_class->lg_name, "ACD") == 0)
519 init_fstab_metadata(void)
522 struct partition_metadata *md;
525 while ((fstab = getfsent()) != NULL) {
526 md = calloc(1, sizeof(struct partition_metadata));
529 if (strncmp(fstab->fs_spec, "/dev/", 5) == 0)
530 md->name = strdup(&fstab->fs_spec[5]);
532 md->fstab = malloc(sizeof(struct fstab));
533 md->fstab->fs_spec = strdup(fstab->fs_spec);
534 md->fstab->fs_file = strdup(fstab->fs_file);
535 md->fstab->fs_vfstype = strdup(fstab->fs_vfstype);
536 md->fstab->fs_mntops = strdup(fstab->fs_mntops);
537 md->fstab->fs_type = strdup(fstab->fs_type);
538 md->fstab->fs_freq = fstab->fs_freq;
539 md->fstab->fs_passno = fstab->fs_passno;
543 TAILQ_INSERT_TAIL(&part_metadata, md, metadata);
548 get_mount_points(struct partedit_item *items, int nitems)
550 struct partition_metadata *md;
553 for (i = 0; i < nitems; i++) {
554 TAILQ_FOREACH(md, &part_metadata, metadata) {
555 if (md->name != NULL && md->fstab != NULL &&
556 strcmp(md->name, items[i].name) == 0) {
557 items[i].mountpoint = md->fstab->fs_file;