diff --git a/usr.sbin/bsdinstall/partedit/part_wizard.c b/usr.sbin/bsdinstall/partedit/part_wizard.c index db78cea768aa..e7b513f3193a 100644 --- a/usr.sbin/bsdinstall/partedit/part_wizard.c +++ b/usr.sbin/bsdinstall/partedit/part_wizard.c @@ -1,395 +1,407 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "partedit.h" #define MIN_FREE_SPACE (1024*1024*1024) /* 1 GB */ #define SWAP_SIZE(available) MIN(available/20, 4*1024*1024*1024LL) static char *wizard_partition(struct gmesh *mesh, const char *disk); int part_wizard(const char *fsreq) { char *disk, *schemeroot; const char *fstype; struct gmesh mesh; int error; struct bsddialog_conf conf; bsddialog_initconf(&conf); if (fsreq != NULL) fstype = fsreq; else fstype = "ufs"; startwizard: error = geom_gettree(&mesh); + if (error != 0) + return (1); bsddialog_backtitle(&conf, "FreeBSD Installer"); - error = geom_gettree(&mesh); disk = boot_disk_select(&mesh); - if (disk == NULL) + if (disk == NULL) { + geom_deletetree(&mesh); return (1); + } bsddialog_clearterminal(); bsddialog_backtitle(&conf, "FreeBSD Installer"); schemeroot = wizard_partition(&mesh, disk); free(disk); + geom_deletetree(&mesh); if (schemeroot == NULL) return (1); - geom_deletetree(&mesh); bsddialog_clearterminal(); bsddialog_backtitle(&conf, "FreeBSD Installer"); error = geom_gettree(&mesh); + if (error != 0) { + free(schemeroot); + return (1); + } error = wizard_makeparts(&mesh, schemeroot, fstype, 1); - if (error) - goto startwizard; free(schemeroot); - geom_deletetree(&mesh); + if (error) + goto startwizard; return (0); } char * boot_disk_select(struct gmesh *mesh) { struct gclass *classp; struct gconfig *gc; struct ggeom *gp; struct gprovider *pp; struct bsddialog_menuitem *disks = NULL; const char *type, *desc; char diskdesc[512]; char *chosen; int i, button, fd, selected, n = 0; struct bsddialog_conf conf; bsddialog_initconf(&conf); LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, "DISK") != 0 && strcmp(classp->lg_name, "RAID") != 0 && strcmp(classp->lg_name, "MD") != 0) continue; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (LIST_EMPTY(&gp->lg_provider)) continue; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { desc = type = NULL; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "type") == 0) type = gc->lg_val; if (strcmp(gc->lg_name, "descr") == 0) desc = gc->lg_val; } /* Skip swap-backed md and WORM devices */ if (strcmp(classp->lg_name, "MD") == 0 && type != NULL && strcmp(type, "swap") == 0) continue; if (strncmp(pp->lg_name, "cd", 2) == 0) continue; /* * Check if the disk is available to be opened for * write operations, it helps prevent the USB * stick used to boot from being listed as an option */ fd = g_open(pp->lg_name, 1); if (fd == -1) { continue; } g_close(fd); disks = realloc(disks, (++n)*sizeof(disks[0])); disks[n-1].name = pp->lg_name; humanize_number(diskdesc, 7, pp->lg_mediasize, "B", HN_AUTOSCALE, HN_DECIMAL); if (strncmp(pp->lg_name, "ad", 2) == 0) strcat(diskdesc, " ATA Hard Disk"); else if (strncmp(pp->lg_name, "md", 2) == 0) strcat(diskdesc, " Memory Disk"); else strcat(diskdesc, " Disk"); if (desc != NULL) snprintf(diskdesc, sizeof(diskdesc), "%s <%s>", diskdesc, desc); disks[n-1].prefix = ""; disks[n-1].on = false; disks[n-1].depth = 0; disks[n-1].desc = strdup(diskdesc); disks[n-1].bottomdesc = ""; } } } if (n > 1) { conf.title = "Partitioning"; button = bsddialog_menu(&conf, "Select the disk on which to install " OSNAME ".", 0, 0, 0, n, disks, &selected); chosen = (button == BSDDIALOG_OK) ? strdup(disks[selected].name) : NULL; } else if (n == 1) { chosen = strdup(disks[0].name); } else { chosen = NULL; } for (i = 0; i < n; i++) free((char*)disks[i].desc); return (chosen); } static struct gprovider * provider_for_name(struct gmesh *mesh, const char *name) { struct gclass *classp; struct gprovider *pp = NULL; struct ggeom *gp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (LIST_EMPTY(&gp->lg_provider)) continue; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) if (strcmp(pp->lg_name, name) == 0) break; if (pp != NULL) break; } if (pp != NULL) break; } return (pp); } static char * wizard_partition(struct gmesh *mesh, const char *disk) { struct gclass *classp; struct ggeom *gpart = NULL; struct gconfig *gc; char *retval = NULL; const char *scheme = NULL; char message[512]; int choice; struct bsddialog_conf conf; bsddialog_initconf(&conf); LIST_FOREACH(classp, &mesh->lg_class, lg_class) if (strcmp(classp->lg_name, "PART") == 0) break; if (classp != NULL) { LIST_FOREACH(gpart, &classp->lg_geom, lg_geom) if (strcmp(gpart->lg_name, disk) == 0) break; } if (gpart != NULL) { LIST_FOREACH(gc, &gpart->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; break; } } } /* Treat uncommitted scheme deletions as no scheme */ if (scheme != NULL && strcmp(scheme, "(none)") == 0) scheme = NULL; query: conf.button.ok_label = "Entire Disk"; conf.button.cancel_label = "Partition"; if (gpart != NULL) conf.button.default_cancel = true; snprintf(message, sizeof(message), "Would you like to use this entire " "disk (%s) for " OSNAME " or partition it to share it with other " "operating systems? Using the entire disk will erase any data " "currently stored there.", disk); conf.title = "Partition"; choice = bsddialog_yesno(&conf, message, 9, 45); conf.button.ok_label = NULL; conf.button.cancel_label = NULL; conf.button.default_cancel = false; if (choice == BSDDIALOG_NO && scheme != NULL && !is_scheme_bootable(scheme)) { char warning[512]; int subchoice; sprintf(warning, "The existing partition scheme on this " "disk (%s) is not bootable on this platform. To install " OSNAME ", it must be repartitioned. This will destroy all " "data on the disk. Are you sure you want to proceed?", scheme); conf.title = "Non-bootable Disk"; subchoice = bsddialog_yesno(&conf, warning, 0, 0); if (subchoice != BSDDIALOG_YES) goto query; gpart_destroy(gpart); scheme = choose_part_type(default_scheme()); if (scheme == NULL) return NULL; gpart_partition(disk, scheme); } if (scheme == NULL || choice == 0) { if (gpart != NULL && scheme != NULL) { /* Erase partitioned disk */ conf.title = "Confirmation"; choice = bsddialog_yesno(&conf, "This will erase " "the disk. Are you sure you want to proceed?", 0, 0); if (choice != BSDDIALOG_YES) goto query; gpart_destroy(gpart); } scheme = choose_part_type(default_scheme()); if (scheme == NULL) return NULL; gpart_partition(disk, scheme); } if (strcmp(scheme, "MBR") == 0) { struct gmesh submesh; - geom_gettree(&submesh); - gpart_create(provider_for_name(&submesh, disk), - "freebsd", NULL, NULL, &retval, - choice /* Non-interactive for "Entire Disk" */); - geom_deletetree(&submesh); + + if (geom_gettree(&submesh) == 0) { + gpart_create(provider_for_name(&submesh, disk), + "freebsd", NULL, NULL, &retval, + choice /* Non-interactive for "Entire Disk" */); + geom_deletetree(&submesh); + } } else { retval = strdup(disk); } return (retval); } int wizard_makeparts(struct gmesh *mesh, const char *disk, const char *fstype, int interactive) { struct gclass *classp; struct ggeom *gp; struct gprovider *pp; char *fsnames[] = {"freebsd-ufs", "freebsd-zfs"}; char *fsname; struct gmesh submesh; char swapsizestr[10], rootsizestr[10]; intmax_t swapsize, available; - int retval; + int error, retval; struct bsddialog_conf conf; if (strcmp(fstype, "zfs") == 0) { fsname = fsnames[1]; } else { /* default to UFS */ fsname = fsnames[0]; } LIST_FOREACH(classp, &mesh->lg_class, lg_class) if (strcmp(classp->lg_name, "PART") == 0) break; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) if (strcmp(gp->lg_name, disk) == 0) break; pp = provider_for_name(mesh, disk); available = gpart_max_free(gp, NULL)*pp->lg_sectorsize; if (interactive && available < MIN_FREE_SPACE) { char availablestr[10], neededstr[10], message[512]; humanize_number(availablestr, 7, available, "B", HN_AUTOSCALE, HN_DECIMAL); humanize_number(neededstr, 7, MIN_FREE_SPACE, "B", HN_AUTOSCALE, HN_DECIMAL); sprintf(message, "There is not enough free space on %s to " "install " OSNAME " (%s free, %s required). Would you like " "to choose another disk or to open the partition editor?", disk, availablestr, neededstr); bsddialog_initconf(&conf); conf.button.ok_label = "Another Disk"; conf.button.cancel_label = "Editor"; conf.title = "Warning"; retval = bsddialog_yesno(&conf, message, 0, 0); return (!retval); /* Editor -> return 0 */ } swapsize = SWAP_SIZE(available); humanize_number(swapsizestr, 7, swapsize, "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); humanize_number(rootsizestr, 7, available - swapsize - 1024*1024, "B", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL); - geom_gettree(&submesh); + error = geom_gettree(&submesh); + if (error != 0) + return (error); pp = provider_for_name(&submesh, disk); gpart_create(pp, fsname, rootsizestr, "/", NULL, 0); geom_deletetree(&submesh); - geom_gettree(&submesh); + error = geom_gettree(&submesh); + if (error != 0) + return (error); pp = provider_for_name(&submesh, disk); gpart_create(pp, "freebsd-swap", swapsizestr, NULL, NULL, 0); geom_deletetree(&submesh); return (0); } diff --git a/usr.sbin/bsdinstall/partedit/partedit.c b/usr.sbin/bsdinstall/partedit/partedit.c index f26aa0085748..ece258b2c070 100644 --- a/usr.sbin/bsdinstall/partedit/partedit.c +++ b/usr.sbin/bsdinstall/partedit/partedit.c @@ -1,625 +1,627 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "diskmenu.h" #include "partedit.h" struct pmetadata_head part_metadata; static int sade_mode = 0; static int apply_changes(struct gmesh *mesh); static void apply_workaround(struct gmesh *mesh); static struct partedit_item *read_geom_mesh(struct gmesh *mesh, int *nitems); static void add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items, int *nitems); static void init_fstab_metadata(void); static void get_mount_points(struct partedit_item *items, int nitems); static int validate_setup(void); static void sigint_handler(int sig) { struct gmesh mesh; /* Revert all changes and exit dialog-mode cleanly on SIGINT */ - geom_gettree(&mesh); - gpart_revert_all(&mesh); - geom_deletetree(&mesh); + if (geom_gettree(&mesh) == 0) { + gpart_revert_all(&mesh); + geom_deletetree(&mesh); + } bsddialog_end(); exit(1); } int main(int argc, const char **argv) { struct partition_metadata *md; const char *progname, *prompt; struct partedit_item *items = NULL; struct gmesh mesh; int i, op, nitems; int error; struct bsddialog_conf conf; progname = getprogname(); if (strcmp(progname, "sade") == 0) sade_mode = 1; TAILQ_INIT(&part_metadata); init_fstab_metadata(); if (bsddialog_init() == BSDDIALOG_ERROR) err(1, "%s", bsddialog_geterror()); bsddialog_initconf(&conf); if (!sade_mode) bsddialog_backtitle(&conf, OSNAME " Installer"); i = 0; /* Revert changes on SIGINT */ signal(SIGINT, sigint_handler); if (strcmp(progname, "autopart") == 0) { /* Guided */ prompt = "Please review the disk setup. When complete, press " "the Finish button."; /* Experimental ZFS autopartition support */ if (argc > 1 && strcmp(argv[1], "zfs") == 0) { part_wizard("zfs"); } else { part_wizard("ufs"); } } else if (strcmp(progname, "scriptedpart") == 0) { error = scripted_editor(argc, argv); prompt = NULL; if (error != 0) { bsddialog_end(); return (error); } } else { prompt = "Create partitions for " OSNAME ", F1 for help.\n" "No changes will be made until you select Finish."; } /* Show the part editor either immediately, or to confirm wizard */ while (prompt != NULL) { bsddialog_clearterminal(); if (!sade_mode) bsddialog_backtitle(&conf, "FreeBSD Installer"); error = geom_gettree(&mesh); if (error == 0) items = read_geom_mesh(&mesh, &nitems); if (error || items == NULL) { conf.title = "Error"; bsddialog_msgbox(&conf, "No disks found. If you need " "to install a kernel driver, choose Shell at the " "installation menu.", 0, 0); break; } get_mount_points(items, nitems); if (i >= nitems) i = nitems - 1; op = diskmenu_show("Partition Editor", prompt, items, nitems, &i); switch (op) { case BUTTON_CREATE: gpart_create((struct gprovider *)(items[i].cookie), NULL, NULL, NULL, NULL, 1); break; case BUTTON_DELETE: gpart_delete((struct gprovider *)(items[i].cookie)); break; case BUTTON_MODIFY: gpart_edit((struct gprovider *)(items[i].cookie)); break; case BUTTON_REVERT: gpart_revert_all(&mesh); while ((md = TAILQ_FIRST(&part_metadata)) != NULL) { if (md->fstab != NULL) { free(md->fstab->fs_spec); free(md->fstab->fs_file); free(md->fstab->fs_vfstype); free(md->fstab->fs_mntops); free(md->fstab->fs_type); free(md->fstab); } if (md->newfs != NULL) free(md->newfs); free(md->name); TAILQ_REMOVE(&part_metadata, md, metadata); free(md); } init_fstab_metadata(); break; case BUTTON_AUTO: part_wizard("ufs"); break; } error = 0; if (op == BUTTON_FINISH) { conf.button.ok_label = "Commit"; conf.button.with_extra = true; conf.button.extra_label = "Revert & Exit"; conf.button.cancel_label = "Back"; conf.title = "Confirmation"; op = bsddialog_yesno(&conf, "Your changes will now be " "written to disk. If you have chosen to overwrite " "existing data, it will be PERMANENTLY ERASED. Are " "you sure you want to commit your changes?", 0, 0); conf.button.ok_label = NULL; conf.button.with_extra = false; conf.button.extra_label = NULL; conf.button.cancel_label = NULL; if (op == BSDDIALOG_OK && validate_setup()) { /* Save */ error = apply_changes(&mesh); if (!error) apply_workaround(&mesh); break; } else if (op == BSDDIALOG_EXTRA) { /* Quit */ gpart_revert_all(&mesh); error = -1; break; } } geom_deletetree(&mesh); free(items); } if (prompt == NULL) { error = geom_gettree(&mesh); - if (validate_setup()) { - error = apply_changes(&mesh); - } else { - gpart_revert_all(&mesh); - error = -1; + if (error != 0) { + if (validate_setup()) { + error = apply_changes(&mesh); + } else { + gpart_revert_all(&mesh); + error = -1; + } + geom_deletetree(&mesh); } } - geom_deletetree(&mesh); - free(items); bsddialog_end(); return (error); } struct partition_metadata * get_part_metadata(const char *name, int create) { struct partition_metadata *md; TAILQ_FOREACH(md, &part_metadata, metadata) if (md->name != NULL && strcmp(md->name, name) == 0) break; if (md == NULL && create) { md = calloc(1, sizeof(*md)); md->name = strdup(name); TAILQ_INSERT_TAIL(&part_metadata, md, metadata); } return (md); } void delete_part_metadata(const char *name) { struct partition_metadata *md; TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->name != NULL && strcmp(md->name, name) == 0) { if (md->fstab != NULL) { free(md->fstab->fs_spec); free(md->fstab->fs_file); free(md->fstab->fs_vfstype); free(md->fstab->fs_mntops); free(md->fstab->fs_type); free(md->fstab); } if (md->newfs != NULL) free(md->newfs); free(md->name); TAILQ_REMOVE(&part_metadata, md, metadata); free(md); break; } } } static int validate_setup(void) { struct partition_metadata *md, *root = NULL; int button; struct bsddialog_conf conf; TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->fstab != NULL && strcmp(md->fstab->fs_file, "/") == 0) root = md; /* XXX: Check for duplicate mountpoints */ } bsddialog_initconf(&conf); if (root == NULL) { conf.title = "Error"; bsddialog_msgbox(&conf, "No root partition was found. " "The root " OSNAME " partition must have a mountpoint " "of '/'.", 0, 0); return (false); } /* * Check for root partitions that we aren't formatting, which is * usually a mistake */ if (root->newfs == NULL && !sade_mode) { conf.button.default_cancel = true; conf.title = "Warning"; button = bsddialog_yesno(&conf, "The chosen root partition " "has a preexisting filesystem. If it contains an existing " OSNAME " system, please update it with freebsd-update " "instead of installing a new system on it. The partition " "can also be erased by pressing \"No\" and then deleting " "and recreating it. Are you sure you want to proceed?", 0, 0); if (button == BSDDIALOG_CANCEL) return (false); } return (true); } static int mountpoint_sorter(const void *xa, const void *xb) { struct partition_metadata *a = *(struct partition_metadata **)xa; struct partition_metadata *b = *(struct partition_metadata **)xb; if (a->fstab == NULL && b->fstab == NULL) return 0; if (a->fstab == NULL) return 1; if (b->fstab == NULL) return -1; return strcmp(a->fstab->fs_file, b->fstab->fs_file); } static int apply_changes(struct gmesh *mesh) { struct partition_metadata *md; char message[512]; int i, nitems, error, *miniperc; const char **minilabel; const char *fstab_path; FILE *fstab; struct bsddialog_conf conf; nitems = 1; /* Partition table changes */ TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->newfs != NULL) nitems++; } minilabel = calloc(nitems, sizeof(const char *)); miniperc = calloc(nitems, sizeof(int)); minilabel[0] = "Writing partition tables"; miniperc[0] = BSDDIALOG_MG_INPROGRESS; i = 1; TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->newfs != NULL) { char *item; item = malloc(255); sprintf(item, "Initializing %s", md->name); minilabel[i] = item; miniperc[i] = BSDDIALOG_MG_PENDING; i++; } } i = 0; bsddialog_initconf(&conf); conf.title = "Initializing"; bsddialog_mixedgauge(&conf, "Initializing file systems. Please wait.", 0, 0, i * 100 / nitems, nitems, minilabel, miniperc); gpart_commit(mesh); miniperc[i] = BSDDIALOG_MG_COMPLETED; i++; if (getenv("BSDINSTALL_LOG") == NULL) setenv("BSDINSTALL_LOG", "/dev/null", 1); TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->newfs != NULL) { miniperc[i] = BSDDIALOG_MG_INPROGRESS; bsddialog_mixedgauge(&conf, "Initializing file systems. Please wait.", 0, 0, i * 100 / nitems, nitems, minilabel, miniperc); sprintf(message, "(echo %s; %s) >>%s 2>>%s", md->newfs, md->newfs, getenv("BSDINSTALL_LOG"), getenv("BSDINSTALL_LOG")); error = system(message); miniperc[i] = (error == 0) ? BSDDIALOG_MG_COMPLETED : BSDDIALOG_MG_FAILED; i++; } } bsddialog_mixedgauge(&conf, "Initializing file systems. Please wait.", 0, 0, i * 100 / nitems, nitems, minilabel, miniperc); for (i = 1; i < nitems; i++) free(__DECONST(char *, minilabel[i])); free(minilabel); free(miniperc); /* Sort filesystems for fstab so that mountpoints are ordered */ { struct partition_metadata **tobesorted; struct partition_metadata *tmp; int nparts = 0; TAILQ_FOREACH(md, &part_metadata, metadata) nparts++; tobesorted = malloc(sizeof(struct partition_metadata *)*nparts); nparts = 0; TAILQ_FOREACH_SAFE(md, &part_metadata, metadata, tmp) { tobesorted[nparts++] = md; TAILQ_REMOVE(&part_metadata, md, metadata); } qsort(tobesorted, nparts, sizeof(tobesorted[0]), mountpoint_sorter); /* Now re-add everything */ while (nparts-- > 0) TAILQ_INSERT_HEAD(&part_metadata, tobesorted[nparts], metadata); free(tobesorted); } if (getenv("PATH_FSTAB") != NULL) fstab_path = getenv("PATH_FSTAB"); else fstab_path = "/etc/fstab"; fstab = fopen(fstab_path, "w+"); if (fstab == NULL) { sprintf(message, "Cannot open fstab file %s for writing (%s)\n", getenv("PATH_FSTAB"), strerror(errno)); conf.title = "Error"; bsddialog_msgbox(&conf, message, 0, 0); return (-1); } fprintf(fstab, "# Device\tMountpoint\tFStype\tOptions\tDump\tPass#\n"); TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->fstab != NULL) fprintf(fstab, "%s\t%s\t\t%s\t%s\t%d\t%d\n", md->fstab->fs_spec, md->fstab->fs_file, md->fstab->fs_vfstype, md->fstab->fs_mntops, md->fstab->fs_freq, md->fstab->fs_passno); } fclose(fstab); return (0); } static void apply_workaround(struct gmesh *mesh) { struct gclass *classp; struct ggeom *gp; struct gconfig *gc; const char *scheme = NULL, *modified = NULL; struct bsddialog_conf conf; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, "PART") == 0) break; } if (strcmp(classp->lg_name, "PART") != 0) { bsddialog_initconf(&conf); conf.title = "Error"; bsddialog_msgbox(&conf, "gpart not found!", 0, 0); return; } LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) { scheme = gc->lg_val; } else if (strcmp(gc->lg_name, "modified") == 0) { modified = gc->lg_val; } } if (scheme && strcmp(scheme, "GPT") == 0 && modified && strcmp(modified, "true") == 0) { if (getenv("WORKAROUND_LENOVO")) gpart_set_root(gp->lg_name, "lenovofix"); if (getenv("WORKAROUND_GPTACTIVE")) gpart_set_root(gp->lg_name, "active"); } } } static struct partedit_item * read_geom_mesh(struct gmesh *mesh, int *nitems) { struct gclass *classp; struct ggeom *gp; struct partedit_item *items; *nitems = 0; items = NULL; /* * Build the device table. First add all disks (and CDs). */ LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, "DISK") != 0 && strcmp(classp->lg_name, "MD") != 0) continue; /* Now recurse into all children */ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) add_geom_children(gp, 0, &items, nitems); } return (items); } static void add_geom_children(struct ggeom *gp, int recurse, struct partedit_item **items, int *nitems) { struct gconsumer *cp; struct gprovider *pp; struct gconfig *gc; if (strcmp(gp->lg_class->lg_name, "PART") == 0 && !LIST_EMPTY(&gp->lg_config)) { LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (strcmp(gc->lg_name, "scheme") == 0) (*items)[*nitems-1].type = gc->lg_val; } } if (LIST_EMPTY(&gp->lg_provider)) return; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { if (strcmp(gp->lg_class->lg_name, "LABEL") == 0) continue; /* Skip WORM media */ if (strncmp(pp->lg_name, "cd", 2) == 0) continue; *items = realloc(*items, (*nitems+1)*sizeof(struct partedit_item)); (*items)[*nitems].indentation = recurse; (*items)[*nitems].name = pp->lg_name; (*items)[*nitems].size = pp->lg_mediasize; (*items)[*nitems].mountpoint = NULL; (*items)[*nitems].type = ""; (*items)[*nitems].cookie = pp; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "type") == 0) (*items)[*nitems].type = gc->lg_val; } /* Skip swap-backed MD devices */ if (strcmp(gp->lg_class->lg_name, "MD") == 0 && strcmp((*items)[*nitems].type, "swap") == 0) continue; (*nitems)++; LIST_FOREACH(cp, &pp->lg_consumers, lg_consumers) add_geom_children(cp->lg_geom, recurse+1, items, nitems); /* Only use first provider for acd */ if (strcmp(gp->lg_class->lg_name, "ACD") == 0) break; } } static void init_fstab_metadata(void) { struct fstab *fstab; struct partition_metadata *md; setfsent(); while ((fstab = getfsent()) != NULL) { md = calloc(1, sizeof(struct partition_metadata)); md->name = NULL; if (strncmp(fstab->fs_spec, "/dev/", 5) == 0) md->name = strdup(&fstab->fs_spec[5]); md->fstab = malloc(sizeof(struct fstab)); md->fstab->fs_spec = strdup(fstab->fs_spec); md->fstab->fs_file = strdup(fstab->fs_file); md->fstab->fs_vfstype = strdup(fstab->fs_vfstype); md->fstab->fs_mntops = strdup(fstab->fs_mntops); md->fstab->fs_type = strdup(fstab->fs_type); md->fstab->fs_freq = fstab->fs_freq; md->fstab->fs_passno = fstab->fs_passno; md->newfs = NULL; TAILQ_INSERT_TAIL(&part_metadata, md, metadata); } } static void get_mount_points(struct partedit_item *items, int nitems) { struct partition_metadata *md; int i; for (i = 0; i < nitems; i++) { TAILQ_FOREACH(md, &part_metadata, metadata) { if (md->name != NULL && md->fstab != NULL && strcmp(md->name, items[i].name) == 0) { items[i].mountpoint = md->fstab->fs_file; break; } } } } diff --git a/usr.sbin/bsdinstall/partedit/scripted.c b/usr.sbin/bsdinstall/partedit/scripted.c index 62c36724d7c5..2f79d643fd08 100644 --- a/usr.sbin/bsdinstall/partedit/scripted.c +++ b/usr.sbin/bsdinstall/partedit/scripted.c @@ -1,221 +1,235 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include "partedit.h" static struct gprovider * provider_for_name(struct gmesh *mesh, const char *name) { struct gclass *classp; struct gprovider *pp = NULL; struct ggeom *gp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (LIST_EMPTY(&gp->lg_provider)) continue; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) if (strcmp(pp->lg_name, name) == 0) break; if (pp != NULL) break; } if (pp != NULL) break; } return (pp); } static int part_config(char *disk, const char *scheme, char *config) { char *partition, *ap, *size = NULL, *type = NULL, *mount = NULL; struct gclass *classp; struct gmesh mesh; struct ggeom *gpart = NULL; int error; if (scheme == NULL) scheme = default_scheme(); error = geom_gettree(&mesh); + if (error != 0) + return (-1); if (provider_for_name(&mesh, disk) == NULL) { fprintf(stderr, "GEOM provider %s not found\n", disk); geom_deletetree(&mesh); return (-1); } /* Remove any existing partitioning and create new scheme */ LIST_FOREACH(classp, &mesh.lg_class, lg_class) if (strcmp(classp->lg_name, "PART") == 0) break; if (classp != NULL) { LIST_FOREACH(gpart, &classp->lg_geom, lg_geom) if (strcmp(gpart->lg_name, disk) == 0) break; } if (gpart != NULL) gpart_destroy(gpart); gpart_partition(disk, scheme); if (strcmp(scheme, "MBR") == 0) { struct gmesh submesh; - geom_gettree(&submesh); - gpart_create(provider_for_name(&submesh, disk), - "freebsd", NULL, NULL, &disk, 0); - geom_deletetree(&submesh); + + if (geom_gettree(&submesh) == 0) { + gpart_create(provider_for_name(&submesh, disk), + "freebsd", NULL, NULL, &disk, 0); + geom_deletetree(&submesh); + } } else { - disk= strdup(disk); + disk = strdup(disk); } geom_deletetree(&mesh); error = geom_gettree(&mesh); + if (error != 0) { + free(disk); + return (-1); + } /* Create partitions */ if (config == NULL) { wizard_makeparts(&mesh, disk, "ufs", 0); goto finished; } while ((partition = strsep(&config, ",")) != NULL) { while ((ap = strsep(&partition, " \t\n")) != NULL) { if (*ap == '\0') continue; if (size == NULL) size = ap; else if (type == NULL) type = ap; else if (mount == NULL) mount = ap; } if (size == NULL) continue; if (strcmp(size, "auto") == 0) size = NULL; gpart_create(provider_for_name(&mesh, disk), type, size, mount, NULL, 0); geom_deletetree(&mesh); error = geom_gettree(&mesh); + if (error != 0) { + free(disk); + return (-1); + } size = type = mount = NULL; } finished: geom_deletetree(&mesh); free(disk); return (0); } -static -int parse_disk_config(char *input) +static int +parse_disk_config(char *input) { char *ap; char *disk = NULL, *scheme = NULL, *partconfig = NULL; while (input != NULL && *input != 0) { if (isspace(*input)) { input++; continue; } switch(*input) { case '{': input++; partconfig = strchr(input, '}'); if (partconfig == NULL) { fprintf(stderr, "Malformed partition setup " "string: %s\n", input); return (1); } *partconfig = '\0'; ap = partconfig+1; partconfig = input; input = ap; break; default: if (disk == NULL) disk = strsep(&input, " \t\n"); else if (scheme == NULL) scheme = strsep(&input, " \t\n"); else { fprintf(stderr, "Unknown directive: %s\n", strsep(&input, " \t\n")); return (1); } } } while (input != NULL && *input != 0); if (disk == NULL || strcmp(disk, "DEFAULT") == 0) { struct gmesh mesh; - geom_gettree(&mesh); - disk = boot_disk_select(&mesh); - geom_deletetree(&mesh); + + if (geom_gettree(&mesh) == 0) { + disk = boot_disk_select(&mesh); + geom_deletetree(&mesh); + } } return (part_config(disk, scheme, partconfig)); } int scripted_editor(int argc, const char **argv) { FILE *fp; char *input, *token; size_t len; int i, error = 0; fp = open_memstream(&input, &len); fputs(argv[1], fp); for (i = 2; i < argc; i++) { fprintf(fp, " %s", argv[i]); } fclose(fp); while ((token = strsep(&input, ";")) != NULL) { error = parse_disk_config(token); if (error != 0) { free(input); return (error); } } free(input); return (0); }