diff --git a/lib/geom/mirror/geom_mirror.c b/lib/geom/mirror/geom_mirror.c index a1b399338814..2b1860eb7548 100644 --- a/lib/geom/mirror/geom_mirror.c +++ b/lib/geom/mirror/geom_mirror.c @@ -1,500 +1,498 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2009 Pawel Jakub Dawidek * 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 AUTHORS 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 AUTHORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_MIRROR_VERSION; #define GMIRROR_BALANCE "load" #define GMIRROR_SLICE "4096" #define GMIRROR_PRIORITY "0" static void mirror_main(struct gctl_req *req, unsigned flags); static void mirror_activate(struct gctl_req *req); static void mirror_clear(struct gctl_req *req); static void mirror_dump(struct gctl_req *req); static void mirror_label(struct gctl_req *req); static void mirror_resize(struct gctl_req *req, unsigned flags); struct g_command class_commands[] = { { "activate", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS, "[-v] name prov ..." }, { "clear", G_FLAG_VERBOSE, mirror_main, G_NULL_OPTS, "[-v] prov ..." }, { "configure", G_FLAG_VERBOSE, NULL, { { 'a', "autosync", NULL, G_TYPE_BOOL }, { 'b', "balance", "", G_TYPE_STRING }, { 'd', "dynamic", NULL, G_TYPE_BOOL }, { 'f', "failsync", NULL, G_TYPE_BOOL }, { 'F', "nofailsync", NULL, G_TYPE_BOOL }, { 'h', "hardcode", NULL, G_TYPE_BOOL }, { 'n', "noautosync", NULL, G_TYPE_BOOL }, { 'p', "priority", "-1", G_TYPE_NUMBER }, { 's', "slice", "-1", G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-adfFhnv] [-b balance] [-s slice] name\n" "[-v] -p priority name prov" }, { "create", G_FLAG_VERBOSE, NULL, { { 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING }, { 'F', "nofailsync", NULL, G_TYPE_BOOL }, { 'n', "noautosync", NULL, G_TYPE_BOOL }, { 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-Fnv] [-b balance] [-s slice] name prov ..." }, { "deactivate", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "[-v] name prov ..." }, { "destroy", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-fv] name ..." }, { "dump", 0, mirror_main, G_NULL_OPTS, "prov ..." }, { "forget", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "name ..." }, { "label", G_FLAG_VERBOSE, mirror_main, { { 'b', "balance", GMIRROR_BALANCE, G_TYPE_STRING }, { 'F', "nofailsync", NULL, G_TYPE_BOOL }, { 'h', "hardcode", NULL, G_TYPE_BOOL }, { 'n', "noautosync", NULL, G_TYPE_BOOL }, { 's', "slice", GMIRROR_SLICE, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-Fhnv] [-b balance] [-s slice] name prov ..." }, { "insert", G_FLAG_VERBOSE, NULL, { { 'h', "hardcode", NULL, G_TYPE_BOOL }, { 'i', "inactive", NULL, G_TYPE_BOOL }, { 'p', "priority", GMIRROR_PRIORITY, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-hiv] [-p priority] name prov ..." }, { "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "[-v] name prov ..." }, { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "[-v] name prov ..." }, { "resize", G_FLAG_VERBOSE, mirror_resize, { { 's', "size", "*", G_TYPE_STRING }, G_OPT_SENTINEL }, "[-s size] [-v] name" }, { "stop", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-fv] name ..." }, G_CMD_SENTINEL }; static int verbose = 0; static void mirror_main(struct gctl_req *req, unsigned flags) { const char *name; if ((flags & G_FLAG_VERBOSE) != 0) verbose = 1; name = gctl_get_ascii(req, "verb"); if (name == NULL) { gctl_error(req, "No '%s' argument.", "verb"); return; } if (strcmp(name, "label") == 0) mirror_label(req); else if (strcmp(name, "clear") == 0) mirror_clear(req); else if (strcmp(name, "dump") == 0) mirror_dump(req); else if (strcmp(name, "activate") == 0) mirror_activate(req); else gctl_error(req, "Unknown command: %s.", name); } static void mirror_label(struct gctl_req *req) { struct g_mirror_metadata md; u_char sector[512]; const char *str; unsigned sectorsize; off_t mediasize; intmax_t val; int error, i, nargs, bal, hardcode; bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); if (nargs < 2) { gctl_error(req, "Too few arguments."); return; } strlcpy(md.md_magic, G_MIRROR_MAGIC, sizeof(md.md_magic)); md.md_version = G_MIRROR_VERSION; str = gctl_get_ascii(req, "arg0"); strlcpy(md.md_name, str, sizeof(md.md_name)); md.md_mid = arc4random(); md.md_all = nargs - 1; md.md_mflags = 0; md.md_dflags = 0; md.md_genid = 0; md.md_syncid = 1; md.md_sync_offset = 0; val = gctl_get_intmax(req, "slice"); md.md_slice = val; str = gctl_get_ascii(req, "balance"); bal = balance_id(str); if (bal == -1) { gctl_error(req, "Invalid balance algorithm."); return; } md.md_balance = bal; if (gctl_get_int(req, "noautosync")) md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOAUTOSYNC; if (gctl_get_int(req, "nofailsync")) md.md_mflags |= G_MIRROR_DEVICE_FLAG_NOFAILSYNC; hardcode = gctl_get_int(req, "hardcode"); /* * Calculate sectorsize by finding least common multiple from * sectorsizes of every disk and find the smallest mediasize. */ mediasize = 0; sectorsize = 0; for (i = 1; i < nargs; i++) { unsigned ssize; off_t msize; str = gctl_get_ascii(req, "arg%d", i); msize = g_get_mediasize(str); ssize = g_get_sectorsize(str); if (msize == 0 || ssize == 0) { gctl_error(req, "Can't get informations about %s: %s.", str, strerror(errno)); return; } msize -= ssize; if (mediasize == 0 || (mediasize > 0 && msize < mediasize)) mediasize = msize; if (sectorsize == 0) sectorsize = ssize; else sectorsize = g_lcm(sectorsize, ssize); } md.md_mediasize = mediasize; md.md_sectorsize = sectorsize; md.md_mediasize -= (md.md_mediasize % md.md_sectorsize); /* * Clear last sector first, to spoil all components if device exists. */ for (i = 1; i < nargs; i++) { str = gctl_get_ascii(req, "arg%d", i); error = g_metadata_clear(str, NULL); if (error != 0) { gctl_error(req, "Can't store metadata on %s: %s.", str, strerror(error)); return; } } /* * Ok, store metadata (use disk number as priority). */ for (i = 1; i < nargs; i++) { str = gctl_get_ascii(req, "arg%d", i); md.md_did = arc4random(); md.md_priority = i - 1; md.md_provsize = g_get_mediasize(str); assert(md.md_provsize != 0); if (!hardcode) bzero(md.md_provider, sizeof(md.md_provider)); else { if (strncmp(str, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) str += sizeof(_PATH_DEV) - 1; strlcpy(md.md_provider, str, sizeof(md.md_provider)); } mirror_metadata_encode(&md, sector); error = g_metadata_store(str, sector, sizeof(sector)); if (error != 0) { fprintf(stderr, "Can't store metadata on %s: %s.\n", str, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (verbose) printf("Metadata value stored on %s.\n", str); } } static void mirror_clear(struct gctl_req *req) { const char *name; int error, i, nargs; nargs = gctl_get_int(req, "nargs"); if (nargs < 1) { gctl_error(req, "Too few arguments."); return; } for (i = 0; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); error = g_metadata_clear(name, G_MIRROR_MAGIC); if (error != 0) { fprintf(stderr, "Can't clear metadata on %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (verbose) printf("Metadata cleared on %s.\n", name); } } static void mirror_dump(struct gctl_req *req) { struct g_mirror_metadata md, tmpmd; const char *name; int error, i, nargs; nargs = gctl_get_int(req, "nargs"); if (nargs < 1) { gctl_error(req, "Too few arguments."); return; } for (i = 0; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); error = g_metadata_read(name, (u_char *)&tmpmd, sizeof(tmpmd), G_MIRROR_MAGIC); if (error != 0) { fprintf(stderr, "Can't read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) { fprintf(stderr, "MD5 hash mismatch for %s, skipping.\n", name); gctl_error(req, "Not fully done."); continue; } printf("Metadata on %s:\n", name); mirror_metadata_dump(&md); printf("\n"); } } static void mirror_activate(struct gctl_req *req) { struct g_mirror_metadata md, tmpmd; const char *name, *path; int error, i, nargs; nargs = gctl_get_int(req, "nargs"); if (nargs < 2) { gctl_error(req, "Too few arguments."); return; } name = gctl_get_ascii(req, "arg0"); for (i = 1; i < nargs; i++) { path = gctl_get_ascii(req, "arg%d", i); error = g_metadata_read(path, (u_char *)&tmpmd, sizeof(tmpmd), G_MIRROR_MAGIC); if (error != 0) { fprintf(stderr, "Cannot read metadata from %s: %s.\n", path, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (mirror_metadata_decode((u_char *)&tmpmd, &md) != 0) { fprintf(stderr, "MD5 hash mismatch for provider %s, skipping.\n", path); gctl_error(req, "Not fully done."); continue; } if (strcmp(md.md_name, name) != 0) { fprintf(stderr, "Provider %s is not the mirror %s component.\n", path, name); gctl_error(req, "Not fully done."); continue; } md.md_dflags &= ~G_MIRROR_DISK_FLAG_INACTIVE; mirror_metadata_encode(&md, (u_char *)&tmpmd); error = g_metadata_store(path, (u_char *)&tmpmd, sizeof(tmpmd)); if (error != 0) { fprintf(stderr, "Cannot write metadata from %s: %s.\n", path, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (verbose) printf("Provider %s activated.\n", path); } } static struct gclass * find_class(struct gmesh *mesh, const char *name) { struct gclass *classp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, name) == 0) return (classp); } return (NULL); } static struct ggeom * find_geom(struct gclass *classp, const char *name) { struct ggeom *gp; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (strcmp(gp->lg_name, name) == 0) return (gp); } return (NULL); } static void mirror_resize(struct gctl_req *req, unsigned flags __unused) { struct gmesh mesh; struct gclass *classp; struct ggeom *gp; struct gprovider *pp; struct gconsumer *cp; off_t size; int error, nargs; - const char *name; + const char *name, *g; char ssize[30]; nargs = gctl_get_int(req, "nargs"); - if (nargs < 1) { - gctl_error(req, "Too few arguments."); - return; - } - error = geom_gettree(&mesh); - if (error) - errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); + if (nargs != 1) + errx(EXIT_FAILURE, "Invalid number of arguments."); name = gctl_get_ascii(req, "class"); if (name == NULL) abort(); + g = gctl_get_ascii(req, "arg0"); + if (g == NULL) + abort(); + error = geom_gettree_geom(&mesh, name, g, 1); + if (error) + errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, name); if (classp == NULL) errx(EXIT_FAILURE, "Class %s not found.", name); - name = gctl_get_ascii(req, "arg0"); - if (name == NULL) - abort(); - gp = find_geom(classp, name); + gp = find_geom(classp, g); if (gp == NULL) - errx(EXIT_FAILURE, "No such geom: %s.", name); + errx(EXIT_FAILURE, "No such geom: %s.", g); pp = LIST_FIRST(&gp->lg_provider); if (pp == NULL) - errx(EXIT_FAILURE, "Provider of geom %s not found.", name); + errx(EXIT_FAILURE, "Provider of geom %s not found.", g); size = pp->lg_mediasize; name = gctl_get_ascii(req, "size"); if (name == NULL) errx(EXIT_FAILURE, "The size is not specified."); if (*name == '*') { #define CSZ(c) ((c)->lg_provider->lg_mediasize - \ (c)->lg_provider->lg_sectorsize) /* Find the maximum possible size */ LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) { if (CSZ(cp) > size) size = CSZ(cp); } LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) { if (CSZ(cp) < size) size = CSZ(cp); } #undef CSZ if (size == pp->lg_mediasize) errx(EXIT_FAILURE, "Cannot expand provider %s\n", pp->lg_name); } else { error = g_parse_lba(name, pp->lg_sectorsize, &size); if (error) errc(EXIT_FAILURE, error, "Invalid size param"); size *= pp->lg_sectorsize; } snprintf(ssize, sizeof(ssize), "%ju", (uintmax_t)size); gctl_change_param(req, "size", -1, ssize); geom_deletetree(&mesh); gctl_issue(req); } diff --git a/lib/geom/part/geom_part.c b/lib/geom/part/geom_part.c index 330a4708251d..2d8f02053a69 100644 --- a/lib/geom/part/geom_part.c +++ b/lib/geom/part/geom_part.c @@ -1,1348 +1,1352 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2007, 2008 Marcel Moolenaar * 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 AUTHORS 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 AUTHORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/geom.h" #include "misc/subr.h" #ifdef STATIC_GEOM_CLASSES #define PUBSYM(x) gpart_##x #else #define PUBSYM(x) x #endif uint32_t PUBSYM(lib_version) = G_LIB_VERSION; uint32_t PUBSYM(version) = 0; static char sstart[32]; static char ssize[32]; volatile sig_atomic_t undo_restore; #define GPART_AUTOFILL "*" #define GPART_FLAGS "C" #define GPART_PARAM_BOOTCODE "bootcode" #define GPART_PARAM_INDEX "index" #define GPART_PARAM_PARTCODE "partcode" #define GPART_PARAM_SKIP_DSN "skip_dsn" static struct gclass *find_class(struct gmesh *, const char *); static struct ggeom * find_geom(struct gclass *, const char *); static int geom_is_withered(struct ggeom *); static const char *find_geomcfg(struct ggeom *, const char *); static const char *find_provcfg(struct gprovider *, const char *); static struct gprovider *find_provider(struct ggeom *, off_t); static const char *fmtsize(int64_t); static int gpart_autofill(struct gctl_req *); static int gpart_autofill_resize(struct gctl_req *); static void gpart_bootcode(struct gctl_req *, unsigned int); static void *gpart_bootfile_read(const char *, ssize_t *); static _Noreturn void gpart_issue(struct gctl_req *, unsigned int); static void gpart_show(struct gctl_req *, unsigned int); static void gpart_show_geom(struct ggeom *, const char *, int); static int gpart_show_hasopt(struct gctl_req *, const char *, const char *); static void gpart_write_partcode(struct ggeom *, int, void *, ssize_t); static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *); static void gpart_print_error(const char *); static void gpart_backup(struct gctl_req *, unsigned int); static void gpart_restore(struct gctl_req *, unsigned int); struct g_command PUBSYM(class_commands)[] = { { "add", 0, gpart_issue, { { 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING }, { 'b', "start", GPART_AUTOFILL, G_TYPE_STRING }, { 's', "size", GPART_AUTOFILL, G_TYPE_STRING }, { 't', "type", NULL, G_TYPE_STRING }, { 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER }, { 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "-t type [-a alignment] [-b start] [-s size] [-i index] " "[-l label] [-f flags] geom" }, { "backup", 0, gpart_backup, G_NULL_OPTS, "geom" }, { "bootcode", 0, gpart_bootcode, { { 'b', GPART_PARAM_BOOTCODE, G_VAL_OPTIONAL, G_TYPE_STRING }, { 'p', GPART_PARAM_PARTCODE, G_VAL_OPTIONAL, G_TYPE_STRING }, { 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, { 'N', GPART_PARAM_SKIP_DSN, NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-N] [-b bootcode] [-p partcode -i index] [-f flags] geom" }, { "commit", 0, gpart_issue, G_NULL_OPTS, "geom" }, { "create", 0, gpart_issue, { { 's', "scheme", NULL, G_TYPE_STRING }, { 'n', "entries", G_VAL_OPTIONAL, G_TYPE_NUMBER }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "-s scheme [-n entries] [-f flags] provider" }, { "delete", 0, gpart_issue, { { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "-i index [-f flags] geom" }, { "destroy", 0, gpart_issue, { { 'F', "force", NULL, G_TYPE_BOOL }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "[-F] [-f flags] geom" }, { "modify", 0, gpart_issue, { { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, { 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING }, { 't', "type", G_VAL_OPTIONAL, G_TYPE_STRING }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "-i index [-l label] [-t type] [-f flags] geom" }, { "set", 0, gpart_issue, { { 'a', "attrib", NULL, G_TYPE_STRING }, { 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "-a attrib [-i index] [-f flags] geom" }, { "show", 0, gpart_show, { { 'l', "show_label", NULL, G_TYPE_BOOL }, { 'r', "show_rawtype", NULL, G_TYPE_BOOL }, { 'p', "show_providers", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-l | -r] [-p] [geom ...]" }, { "undo", 0, gpart_issue, G_NULL_OPTS, "geom" }, { "unset", 0, gpart_issue, { { 'a', "attrib", NULL, G_TYPE_STRING }, { 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "-a attrib [-i index] [-f flags] geom" }, { "resize", 0, gpart_issue, { { 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING }, { 's', "size", GPART_AUTOFILL, G_TYPE_STRING }, { 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "-i index [-a alignment] [-s size] [-f flags] geom" }, { "restore", 0, gpart_restore, { { 'F', "force", NULL, G_TYPE_BOOL }, { 'l', "restore_labels", NULL, G_TYPE_BOOL }, { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "[-lF] [-f flags] provider [...]" }, { "recover", 0, gpart_issue, { { 'f', "flags", GPART_FLAGS, G_TYPE_STRING }, G_OPT_SENTINEL }, "[-f flags] geom" }, G_CMD_SENTINEL }; static struct gclass * find_class(struct gmesh *mesh, const char *name) { struct gclass *classp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, name) == 0) return (classp); } return (NULL); } static struct ggeom * find_geom(struct gclass *classp, const char *name) { struct ggeom *gp, *wgp; if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) name += sizeof(_PATH_DEV) - 1; wgp = NULL; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (strcmp(gp->lg_name, name) != 0) continue; if (!geom_is_withered(gp)) return (gp); else wgp = gp; } return (wgp); } static int geom_is_withered(struct ggeom *gp) { struct gconfig *gc; LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (!strcmp(gc->lg_name, "wither")) return (1); } return (0); } static const char * find_geomcfg(struct ggeom *gp, const char *cfg) { struct gconfig *gc; LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (!strcmp(gc->lg_name, cfg)) return (gc->lg_val); } return (NULL); } static const char * find_provcfg(struct gprovider *pp, const char *cfg) { struct gconfig *gc; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (!strcmp(gc->lg_name, cfg)) return (gc->lg_val); } return (NULL); } static struct gprovider * find_provider(struct ggeom *gp, off_t minsector) { struct gprovider *pp, *bestpp; const char *s; off_t sector, bestsector; bestpp = NULL; bestsector = 0; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "start"); sector = (off_t)strtoimax(s, NULL, 0); if (sector < minsector) continue; if (bestpp != NULL && sector >= bestsector) continue; bestpp = pp; bestsector = sector; } return (bestpp); } static const char * fmtsize(int64_t rawsz) { static char buf[5]; humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); return (buf); } static const char * fmtattrib(struct gprovider *pp) { static char buf[128]; struct gconfig *gc; u_int idx; buf[0] = '\0'; idx = 0; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "attrib") != 0) continue; idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s", (idx == 0) ? " [" : ",", gc->lg_val); } if (idx > 0) snprintf(buf + idx, sizeof(buf) - idx, "] "); return (buf); } #define ALIGNDOWN(d, a) ((d) - (d) % (a)) #define ALIGNUP(d, a) ((d) % (a) ? (d) - (d) % (a) + (a): (d)) static int gpart_autofill_resize(struct gctl_req *req) { struct gmesh mesh; struct gclass *cp; struct ggeom *gp; struct gprovider *pp; off_t last, size, start, new_size; off_t lba, new_lba, alignment, offset; - const char *s; + const char *g, *s; int error, idx, has_alignment; idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX); if (idx < 1) errx(EXIT_FAILURE, "invalid partition index"); - error = geom_gettree(&mesh); - if (error) - return (error); s = gctl_get_ascii(req, "class"); if (s == NULL) abort(); + g = gctl_get_ascii(req, "arg0"); + if (g == NULL) + abort(); + error = geom_gettree_geom(&mesh, s, g, 1); + if (error) + return (error); cp = find_class(&mesh, s); if (cp == NULL) errx(EXIT_FAILURE, "Class %s not found.", s); - s = gctl_get_ascii(req, "arg0"); - if (s == NULL) - abort(); - gp = find_geom(cp, s); + gp = find_geom(cp, g); if (gp == NULL) - errx(EXIT_FAILURE, "No such geom: %s.", s); + errx(EXIT_FAILURE, "No such geom: %s.", g); pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; if (pp == NULL) - errx(EXIT_FAILURE, "Provider for geom %s not found.", s); + errx(EXIT_FAILURE, "Provider for geom %s not found.", g); s = gctl_get_ascii(req, "alignment"); has_alignment = (*s == '*') ? 0 : 1; alignment = 1; if (has_alignment) { error = g_parse_lba(s, pp->lg_sectorsize, &alignment); if (error) errc(EXIT_FAILURE, error, "Invalid alignment param"); if (alignment == 0) errx(EXIT_FAILURE, "Invalid alignment param"); } else { lba = pp->lg_stripesize / pp->lg_sectorsize; if (lba > 0) alignment = lba; } error = gctl_delete_param(req, "alignment"); if (error) errc(EXIT_FAILURE, error, "internal error"); s = gctl_get_ascii(req, "size"); if (*s == '*') new_size = 0; else { error = g_parse_lba(s, pp->lg_sectorsize, &new_size); if (error) errc(EXIT_FAILURE, error, "Invalid size param"); /* no autofill necessary. */ if (has_alignment == 0) goto done; } offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment; s = find_geomcfg(gp, "last"); if (s == NULL) errx(EXIT_FAILURE, "Final block not found for geom %s", gp->lg_name); last = (off_t)strtoimax(s, NULL, 0); LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "index"); if (s == NULL) continue; if (atoi(s) == idx) break; } if (pp == NULL) errx(EXIT_FAILURE, "invalid partition index"); s = find_provcfg(pp, "start"); start = (off_t)strtoimax(s, NULL, 0); s = find_provcfg(pp, "end"); lba = (off_t)strtoimax(s, NULL, 0); size = lba - start + 1; pp = find_provider(gp, lba + 1); if (new_size > 0 && (new_size <= size || pp == NULL)) { /* The start offset may be not aligned, so we align the end * offset and then calculate the size. */ new_size = ALIGNDOWN(start + offset + new_size, alignment) - start - offset; goto done; } if (pp == NULL) { new_size = ALIGNDOWN(last + offset + 1, alignment) - start - offset; if (new_size < size) return (ENOSPC); } else { s = find_provcfg(pp, "start"); new_lba = (off_t)strtoimax(s, NULL, 0); /* * Is there any free space between current and * next providers? */ new_lba = ALIGNDOWN(new_lba + offset, alignment) - offset; if (new_lba > lba) new_size = new_lba - start; else { geom_deletetree(&mesh); return (ENOSPC); } } done: snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size); gctl_change_param(req, "size", -1, ssize); geom_deletetree(&mesh); return (0); } static int gpart_autofill(struct gctl_req *req) { struct gmesh mesh; struct gclass *cp; struct ggeom *gp; struct gprovider *pp; off_t first, last, a_first; off_t size, start, a_lba; off_t lba, len, alignment, offset; uintmax_t grade; - const char *s; + const char *g, *s; int error, has_size, has_start, has_alignment; s = gctl_get_ascii(req, "verb"); if (strcmp(s, "resize") == 0) return gpart_autofill_resize(req); if (strcmp(s, "add") != 0) return (0); - error = geom_gettree(&mesh); - if (error) - return (error); s = gctl_get_ascii(req, "class"); if (s == NULL) abort(); + g = gctl_get_ascii(req, "arg0"); + if (g == NULL) + abort(); + error = geom_gettree_geom(&mesh, s, g, 1); + if (error) + return (error); cp = find_class(&mesh, s); if (cp == NULL) errx(EXIT_FAILURE, "Class %s not found.", s); - s = gctl_get_ascii(req, "arg0"); - if (s == NULL) - abort(); - gp = find_geom(cp, s); + gp = find_geom(cp, g); if (gp == NULL) { - if (g_device_path(s) == NULL) { - errx(EXIT_FAILURE, "No such geom %s.", s); + if (g_device_path(g) == NULL) { + errx(EXIT_FAILURE, "No such geom %s.", g); } else { /* * We don't free memory allocated by g_device_path() as * we are about to exit. */ errx(EXIT_FAILURE, "No partitioning scheme found on geom %s. Create one first using 'gpart create'.", - s); + g); } } pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; if (pp == NULL) - errx(EXIT_FAILURE, "Provider for geom %s not found.", s); + errx(EXIT_FAILURE, "Provider for geom %s not found.", g); s = gctl_get_ascii(req, "alignment"); has_alignment = (*s == '*') ? 0 : 1; alignment = 1; if (has_alignment) { error = g_parse_lba(s, pp->lg_sectorsize, &alignment); if (error) errc(EXIT_FAILURE, error, "Invalid alignment param"); if (alignment == 0) errx(EXIT_FAILURE, "Invalid alignment param"); } error = gctl_delete_param(req, "alignment"); if (error) errc(EXIT_FAILURE, error, "internal error"); s = gctl_get_ascii(req, "size"); has_size = (*s == '*') ? 0 : 1; size = 0; if (has_size) { error = g_parse_lba(s, pp->lg_sectorsize, &size); if (error) errc(EXIT_FAILURE, error, "Invalid size param"); } s = gctl_get_ascii(req, "start"); has_start = (*s == '*') ? 0 : 1; start = 0ULL; if (has_start) { error = g_parse_lba(s, pp->lg_sectorsize, &start); if (error) errc(EXIT_FAILURE, error, "Invalid start param"); } /* No autofill necessary. */ if (has_size && has_start && !has_alignment) goto done; len = pp->lg_stripesize / pp->lg_sectorsize; if (len > 0 && !has_alignment) alignment = len; /* Adjust parameters to stripeoffset */ offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment; start = ALIGNUP(start + offset, alignment); if (size > alignment) size = ALIGNDOWN(size, alignment); s = find_geomcfg(gp, "first"); if (s == NULL) errx(EXIT_FAILURE, "Starting block not found for geom %s", gp->lg_name); first = (off_t)strtoimax(s, NULL, 0); s = find_geomcfg(gp, "last"); if (s == NULL) errx(EXIT_FAILURE, "Final block not found for geom %s", gp->lg_name); last = (off_t)strtoimax(s, NULL, 0); grade = ~0ULL; a_first = ALIGNUP(first + offset, alignment); last = ALIGNDOWN(last + offset + 1, alignment) - 1; if (a_first < start) a_first = start; while ((pp = find_provider(gp, first)) != NULL) { s = find_provcfg(pp, "start"); lba = (off_t)strtoimax(s, NULL, 0); a_lba = ALIGNDOWN(lba + offset, alignment); if (first < a_lba && a_first < a_lba) { /* Free space [first, lba> */ len = a_lba - a_first; if (has_size) { if (len >= size && (uintmax_t)(len - size) < grade) { start = a_first; grade = len - size; } } else if (has_start) { if (start >= a_first && start < a_lba) { size = a_lba - start; grade = start - a_first; } } else { if (grade == ~0ULL || len > size) { start = a_first; size = len; grade = 0; } } } s = find_provcfg(pp, "end"); first = (off_t)strtoimax(s, NULL, 0) + 1; if (first + offset > a_first) a_first = ALIGNUP(first + offset, alignment); } if (a_first <= last) { /* Free space [first-last] */ len = ALIGNDOWN(last - a_first + 1, alignment); if (has_size) { if (len >= size && (uintmax_t)(len - size) < grade) { start = a_first; grade = len - size; } } else if (has_start) { if (start >= a_first && start <= last) { size = ALIGNDOWN(last - start + 1, alignment); grade = start - a_first; } } else { if (grade == ~0ULL || len > size) { start = a_first; size = len; grade = 0; } } } if (grade == ~0ULL) { geom_deletetree(&mesh); return (ENOSPC); } start -= offset; /* Return back to real offset */ done: snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size); gctl_change_param(req, "size", -1, ssize); snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start); gctl_change_param(req, "start", -1, sstart); geom_deletetree(&mesh); return (0); } static void gpart_show_geom(struct ggeom *gp, const char *element, int show_providers) { struct gprovider *pp; const char *s, *scheme; off_t first, last, sector, end; off_t length, secsz; int idx, wblocks, wname, wmax; if (geom_is_withered(gp)) return; scheme = find_geomcfg(gp, "scheme"); if (scheme == NULL) errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name); s = find_geomcfg(gp, "first"); if (s == NULL) errx(EXIT_FAILURE, "Starting block not found for geom %s", gp->lg_name); first = (off_t)strtoimax(s, NULL, 0); s = find_geomcfg(gp, "last"); if (s == NULL) errx(EXIT_FAILURE, "Final block not found for geom %s", gp->lg_name); last = (off_t)strtoimax(s, NULL, 0); wblocks = strlen(s); s = find_geomcfg(gp, "state"); if (s == NULL) errx(EXIT_FAILURE, "State not found for geom %s", gp->lg_name); if (s != NULL && *s != 'C') s = NULL; wmax = strlen(gp->lg_name); if (show_providers) { LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { wname = strlen(pp->lg_name); if (wname > wmax) wmax = wname; } } wname = wmax; pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; secsz = pp->lg_sectorsize; printf("=>%*jd %*jd %*s %s (%s)%s\n", wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1), wname, gp->lg_name, scheme, fmtsize(pp->lg_mediasize), s ? " [CORRUPT]": ""); while ((pp = find_provider(gp, first)) != NULL) { s = find_provcfg(pp, "start"); sector = (off_t)strtoimax(s, NULL, 0); s = find_provcfg(pp, "end"); end = (off_t)strtoimax(s, NULL, 0); length = end - sector + 1; s = find_provcfg(pp, "index"); idx = atoi(s); if (first < sector) { printf(" %*jd %*jd %*s - free - (%s)\n", wblocks, (intmax_t)first, wblocks, (intmax_t)(sector - first), wname, "", fmtsize((sector - first) * secsz)); } if (show_providers) { printf(" %*jd %*jd %*s %s %s (%s)\n", wblocks, (intmax_t)sector, wblocks, (intmax_t)length, wname, pp->lg_name, find_provcfg(pp, element), fmtattrib(pp), fmtsize(pp->lg_mediasize)); } else printf(" %*jd %*jd %*d %s %s (%s)\n", wblocks, (intmax_t)sector, wblocks, (intmax_t)length, wname, idx, find_provcfg(pp, element), fmtattrib(pp), fmtsize(pp->lg_mediasize)); first = end + 1; } if (first <= last) { length = last - first + 1; printf(" %*jd %*jd %*s - free - (%s)\n", wblocks, (intmax_t)first, wblocks, (intmax_t)length, wname, "", fmtsize(length * secsz)); } printf("\n"); } static int gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt) { if (!gctl_get_int(req, "%s", opt)) return (0); if (elt != NULL) errx(EXIT_FAILURE, "-l and -r are mutually exclusive"); return (1); } static void gpart_show(struct gctl_req *req, unsigned int fl __unused) { struct gmesh mesh; struct gclass *classp; struct ggeom *gp; const char *element, *name; int error, i, nargs, show_providers; element = NULL; if (gpart_show_hasopt(req, "show_label", element)) element = "label"; if (gpart_show_hasopt(req, "show_rawtype", element)) element = "rawtype"; if (element == NULL) element = "type"; name = gctl_get_ascii(req, "class"); if (name == NULL) abort(); - error = geom_gettree(&mesh); + nargs = gctl_get_int(req, "nargs"); + if (nargs == 1) { + error = geom_gettree_geom(&mesh, name, + gctl_get_ascii(req, "arg0"), 1); + } else + error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, name); if (classp == NULL) { geom_deletetree(&mesh); errx(EXIT_FAILURE, "Class %s not found.", name); } show_providers = gctl_get_int(req, "show_providers"); - nargs = gctl_get_int(req, "nargs"); if (nargs > 0) { for (i = 0; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); gp = find_geom(classp, name); if (gp != NULL) gpart_show_geom(gp, element, show_providers); else errx(EXIT_FAILURE, "No such geom: %s.", name); } } else { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { gpart_show_geom(gp, element, show_providers); } } geom_deletetree(&mesh); } static void gpart_backup(struct gctl_req *req, unsigned int fl __unused) { struct gmesh mesh; struct gclass *classp; struct gprovider *pp; struct ggeom *gp; - const char *s, *scheme; + const char *g, *s, *scheme; off_t sector, end; off_t length; int error, i, windex, wblocks, wtype; if (gctl_get_int(req, "nargs") != 1) errx(EXIT_FAILURE, "Invalid number of arguments."); - error = geom_gettree(&mesh); - if (error != 0) - errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); s = gctl_get_ascii(req, "class"); if (s == NULL) abort(); + g = gctl_get_ascii(req, "arg0"); + if (g == NULL) + abort(); + error = geom_gettree_geom(&mesh, s, g, 0); + if (error != 0) + errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, s); if (classp == NULL) { geom_deletetree(&mesh); errx(EXIT_FAILURE, "Class %s not found.", s); } - s = gctl_get_ascii(req, "arg0"); - if (s == NULL) - abort(); - gp = find_geom(classp, s); + gp = find_geom(classp, g); if (gp == NULL) - errx(EXIT_FAILURE, "No such geom: %s.", s); + errx(EXIT_FAILURE, "No such geom: %s.", g); scheme = find_geomcfg(gp, "scheme"); if (scheme == NULL) abort(); - pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; s = find_geomcfg(gp, "last"); if (s == NULL) abort(); wblocks = strlen(s); wtype = 0; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "type"); i = strlen(s); if (i > wtype) wtype = i; } s = find_geomcfg(gp, "entries"); if (s == NULL) abort(); windex = strlen(s); printf("%s %s\n", scheme, s); LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "start"); sector = (off_t)strtoimax(s, NULL, 0); s = find_provcfg(pp, "end"); end = (off_t)strtoimax(s, NULL, 0); length = end - sector + 1; s = find_provcfg(pp, "label"); printf("%-*s %*s %*jd %*jd %s %s\n", windex, find_provcfg(pp, "index"), wtype, find_provcfg(pp, "type"), wblocks, (intmax_t)sector, wblocks, (intmax_t)length, (s != NULL) ? s: "", fmtattrib(pp)); } geom_deletetree(&mesh); } static int skip_line(const char *p) { while (*p != '\0') { if (*p == '#') return (1); if (isspace(*p) == 0) return (0); p++; } return (1); } static void gpart_sighndl(int sig __unused) { undo_restore = 1; } static void gpart_restore(struct gctl_req *req, unsigned int fl __unused) { struct gmesh mesh; struct gclass *classp; struct gctl_req *r; struct ggeom *gp; struct sigaction si_sa; const char *s, *flags, *errstr, *label; char **ap, *argv[6], line[BUFSIZ], *pline; int error, forced, i, l, nargs, created, rl; intmax_t n; nargs = gctl_get_int(req, "nargs"); if (nargs < 1) errx(EXIT_FAILURE, "Invalid number of arguments."); forced = gctl_get_int(req, "force"); flags = gctl_get_ascii(req, "flags"); rl = gctl_get_int(req, "restore_labels"); s = gctl_get_ascii(req, "class"); if (s == NULL) abort(); error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, s); if (classp == NULL) { geom_deletetree(&mesh); errx(EXIT_FAILURE, "Class %s not found.", s); } sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = gpart_sighndl; if (sigaction(SIGINT, &si_sa, 0) == -1) err(EXIT_FAILURE, "sigaction SIGINT"); if (forced) { /* destroy existent partition table before restore */ for (i = 0; i < nargs; i++) { s = gctl_get_ascii(req, "arg%d", i); gp = find_geom(classp, s); if (gp != NULL) { r = gctl_get_handle(); gctl_ro_param(r, "class", -1, classp->lg_name); gctl_ro_param(r, "verb", -1, "destroy"); gctl_ro_param(r, "flags", -1, "restore"); gctl_ro_param(r, "force", sizeof(forced), &forced); gctl_ro_param(r, "arg0", -1, s); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_print_error(errstr); gctl_free(r); goto backout; } gctl_free(r); } } } created = 0; while (undo_restore == 0 && fgets(line, sizeof(line) - 1, stdin) != NULL) { /* Format of backup entries: * * [label] ['['attrib[,attrib]']'] */ pline = (char *)line; pline[strlen(line) - 1] = 0; if (skip_line(pline)) continue; for (ap = argv; (*ap = strsep(&pline, " \t")) != NULL;) if (**ap != '\0' && ++ap >= &argv[6]) break; l = ap - &argv[0]; label = pline = NULL; if (l == 1 || l == 2) { /* create table */ if (created) errx(EXIT_FAILURE, "Incorrect backup format."); if (l == 2) n = strtoimax(argv[1], NULL, 0); for (i = 0; i < nargs; i++) { s = gctl_get_ascii(req, "arg%d", i); r = gctl_get_handle(); gctl_ro_param(r, "class", -1, classp->lg_name); gctl_ro_param(r, "verb", -1, "create"); gctl_ro_param(r, "scheme", -1, argv[0]); if (l == 2) gctl_ro_param(r, "entries", sizeof(n), &n); gctl_ro_param(r, "flags", -1, "restore"); gctl_ro_param(r, "arg0", -1, s); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_print_error(errstr); gctl_free(r); goto backout; } gctl_free(r); } created = 1; continue; } else if (l < 4 || created == 0) errx(EXIT_FAILURE, "Incorrect backup format."); else if (l == 5) { if (strchr(argv[4], '[') == NULL) label = argv[4]; else pline = argv[4]; } else if (l == 6) { label = argv[4]; pline = argv[5]; } /* Add partitions to each table */ for (i = 0; i < nargs; i++) { s = gctl_get_ascii(req, "arg%d", i); r = gctl_get_handle(); n = strtoimax(argv[0], NULL, 0); gctl_ro_param(r, "class", -1, classp->lg_name); gctl_ro_param(r, "verb", -1, "add"); gctl_ro_param(r, "flags", -1, "restore"); gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n); gctl_ro_param(r, "type", -1, argv[1]); gctl_ro_param(r, "start", -1, argv[2]); gctl_ro_param(r, "size", -1, argv[3]); if (rl != 0 && label != NULL) gctl_ro_param(r, "label", -1, argv[4]); gctl_ro_param(r, "alignment", -1, GPART_AUTOFILL); gctl_ro_param(r, "arg0", -1, s); error = gpart_autofill(r); if (error != 0) errc(EXIT_FAILURE, error, "autofill"); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_print_error(errstr); gctl_free(r); goto backout; } gctl_free(r); } if (pline == NULL || *pline != '[') continue; /* set attributes */ pline++; for (ap = argv; (*ap = strsep(&pline, ",]")) != NULL;) if (**ap != '\0' && ++ap >= &argv[6]) break; for (i = 0; i < nargs; i++) { l = ap - &argv[0]; s = gctl_get_ascii(req, "arg%d", i); while (l > 0) { r = gctl_get_handle(); gctl_ro_param(r, "class", -1, classp->lg_name); gctl_ro_param(r, "verb", -1, "set"); gctl_ro_param(r, "flags", -1, "restore"); gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n); gctl_ro_param(r, "attrib", -1, argv[--l]); gctl_ro_param(r, "arg0", -1, s); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_print_error(errstr); gctl_free(r); goto backout; } gctl_free(r); } } } if (undo_restore) goto backout; /* commit changes if needed */ if (strchr(flags, 'C') != NULL) { for (i = 0; i < nargs; i++) { s = gctl_get_ascii(req, "arg%d", i); r = gctl_get_handle(); gctl_ro_param(r, "class", -1, classp->lg_name); gctl_ro_param(r, "verb", -1, "commit"); gctl_ro_param(r, "arg0", -1, s); errstr = gctl_issue(r); if (errstr != NULL && errstr[0] != '\0') { gpart_print_error(errstr); gctl_free(r); goto backout; } gctl_free(r); } } gctl_free(req); geom_deletetree(&mesh); exit(EXIT_SUCCESS); backout: for (i = 0; i < nargs; i++) { s = gctl_get_ascii(req, "arg%d", i); r = gctl_get_handle(); gctl_ro_param(r, "class", -1, classp->lg_name); gctl_ro_param(r, "verb", -1, "undo"); gctl_ro_param(r, "arg0", -1, s); gctl_issue(r); gctl_free(r); } gctl_free(req); geom_deletetree(&mesh); exit(EXIT_FAILURE); } static void * gpart_bootfile_read(const char *bootfile, ssize_t *size) { struct stat sb; void *code; int fd; if (stat(bootfile, &sb) == -1) err(EXIT_FAILURE, "%s", bootfile); if (!S_ISREG(sb.st_mode)) errx(EXIT_FAILURE, "%s: not a regular file", bootfile); if (sb.st_size == 0) errx(EXIT_FAILURE, "%s: empty file", bootfile); if (*size > 0 && sb.st_size > *size) errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, *size); *size = sb.st_size; fd = open(bootfile, O_RDONLY); if (fd == -1) err(EXIT_FAILURE, "%s", bootfile); code = malloc(*size); if (code == NULL) err(EXIT_FAILURE, NULL); if (read(fd, code, *size) != *size) err(EXIT_FAILURE, "%s", bootfile); close(fd); return (code); } static void gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size) { char dsf[128]; struct gprovider *pp; const char *s; char *buf; off_t bsize; int fd; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "index"); if (s == NULL) continue; if (atoi(s) == idx) break; } if (pp != NULL) { snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); if (pp->lg_mediasize < size) errx(EXIT_FAILURE, "%s: not enough space", dsf); fd = open(dsf, O_WRONLY); if (fd == -1) err(EXIT_FAILURE, "%s", dsf); /* * When writing to a disk device, the write must be * sector aligned and not write to any partial sectors, * so round up the buffer size to the next sector and zero it. */ bsize = (size + pp->lg_sectorsize - 1) / pp->lg_sectorsize * pp->lg_sectorsize; buf = calloc(1, bsize); if (buf == NULL) err(EXIT_FAILURE, "%s", dsf); bcopy(code, buf, size); if (write(fd, buf, bsize) != bsize) err(EXIT_FAILURE, "%s", dsf); free(buf); close(fd); printf("partcode written to %s\n", pp->lg_name); } else errx(EXIT_FAILURE, "invalid partition index"); } static void gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code) { char dsf[128]; struct gprovider *pp; const char *s; int installed, fd; installed = 0; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "index"); if (s == NULL) continue; if (idx != 0 && atoi(s) != idx) continue; snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); if (pp->lg_sectorsize != sizeof(struct vtoc8)) errx(EXIT_FAILURE, "%s: unexpected sector " "size (%d)\n", dsf, pp->lg_sectorsize); if (pp->lg_mediasize < VTOC_BOOTSIZE) continue; fd = open(dsf, O_WRONLY); if (fd == -1) err(EXIT_FAILURE, "%s", dsf); /* * We ignore the first VTOC_BOOTSIZE bytes of boot code in * order to avoid overwriting the label. */ if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) != sizeof(struct vtoc8)) err(EXIT_FAILURE, "%s", dsf); if (write(fd, (caddr_t)code + sizeof(struct vtoc8), VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE - sizeof(struct vtoc8)) err(EXIT_FAILURE, "%s", dsf); installed++; close(fd); if (idx != 0 && atoi(s) == idx) break; } if (installed == 0) errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name); else printf("partcode written to %s\n", idx != 0 ? pp->lg_name: gp->lg_name); } static void gpart_bootcode(struct gctl_req *req, unsigned int fl) { struct gmesh mesh; struct gclass *classp; struct ggeom *gp; - const char *s; + const char *g, *s; void *bootcode, *partcode; size_t bootsize, partsize; int error, idx, vtoc8; + if (gctl_get_int(req, "nargs") != 1) + errx(EXIT_FAILURE, "Invalid number of arguments."); + if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) { s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE); bootsize = 800 * 1024; /* Arbitrary limit. */ bootcode = gpart_bootfile_read(s, &bootsize); error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize, bootcode); if (error) errc(EXIT_FAILURE, error, "internal error"); } else bootcode = NULL; if (!gctl_has_param(req, GPART_PARAM_PARTCODE)) { if (bootcode == NULL) errx(EXIT_FAILURE, "neither -b nor -p specified"); if (gctl_has_param(req, GPART_PARAM_INDEX)) errx(EXIT_FAILURE, "-i is only valid with -p"); goto nopartcode; } s = gctl_get_ascii(req, "class"); if (s == NULL) abort(); - error = geom_gettree(&mesh); + g = gctl_get_ascii(req, "arg0"); + if (g == NULL) + abort(); + error = geom_gettree_geom(&mesh, s, g, 0); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, s); if (classp == NULL) { geom_deletetree(&mesh); errx(EXIT_FAILURE, "Class %s not found.", s); } - if (gctl_get_int(req, "nargs") != 1) - errx(EXIT_FAILURE, "Invalid number of arguments."); - s = gctl_get_ascii(req, "arg0"); - if (s == NULL) - abort(); - gp = find_geom(classp, s); + gp = find_geom(classp, g); if (gp == NULL) - errx(EXIT_FAILURE, "No such geom: %s.", s); + errx(EXIT_FAILURE, "No such geom: %s.", g); s = find_geomcfg(gp, "scheme"); if (s == NULL) errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name); if (strcmp(s, "VTOC8") == 0) vtoc8 = 1; else vtoc8 = 0; if (gctl_has_param(req, GPART_PARAM_INDEX)) { idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX); if (idx < 1) errx(EXIT_FAILURE, "invalid partition index"); error = gctl_delete_param(req, GPART_PARAM_INDEX); if (error) errc(EXIT_FAILURE, error, "internal error"); } else idx = 0; if (gctl_has_param(req, GPART_PARAM_PARTCODE)) { s = gctl_get_ascii(req, GPART_PARAM_PARTCODE); if (vtoc8 != 0) partsize = VTOC_BOOTSIZE; else partsize = 1024 * 1024; /* Arbitrary limit. */ partcode = gpart_bootfile_read(s, &partsize); error = gctl_delete_param(req, GPART_PARAM_PARTCODE); if (error) errc(EXIT_FAILURE, error, "internal error"); if (vtoc8 == 0) { if (idx == 0) errx(EXIT_FAILURE, "missing -i option"); gpart_write_partcode(gp, idx, partcode, partsize); } else { if (partsize != VTOC_BOOTSIZE) errx(EXIT_FAILURE, "invalid bootcode"); gpart_write_partcode_vtoc8(gp, idx, partcode); } free(partcode); } geom_deletetree(&mesh); nopartcode: if (bootcode != NULL) gpart_issue(req, fl); } static void gpart_print_error(const char *errstr) { char *errmsg; int error; error = strtol(errstr, &errmsg, 0); if (errmsg != errstr) { while (errmsg[0] == ' ') errmsg++; if (errmsg[0] != '\0') warnc(error, "%s", errmsg); else warnc(error, NULL); } else warnx("%s", errmsg); } static _Noreturn void gpart_issue(struct gctl_req *req, unsigned int fl __unused) { char buf[4096]; const char *errstr; int error, status; if (gctl_get_int(req, "nargs") != 1) errx(EXIT_FAILURE, "Invalid number of arguments."); (void)gctl_delete_param(req, "nargs"); /* autofill parameters (if applicable). */ error = gpart_autofill(req); if (error) { warnc(error, "autofill"); status = EXIT_FAILURE; goto done; } buf[0] = '\0'; gctl_add_param(req, "output", sizeof(buf), buf, GCTL_PARAM_WR | GCTL_PARAM_ASCII); errstr = gctl_issue(req); if (errstr == NULL || errstr[0] == '\0') { if (buf[0] != '\0') printf("%s", buf); status = EXIT_SUCCESS; goto done; } gpart_print_error(errstr); status = EXIT_FAILURE; done: gctl_free(req); exit(status); } diff --git a/lib/libgeom/geom_getxml.c b/lib/libgeom/geom_getxml.c index 48565f707b03..a6bb130fd5a7 100644 --- a/lib/libgeom/geom_getxml.c +++ b/lib/libgeom/geom_getxml.c @@ -1,89 +1,130 @@ /*- * SPDX-License-Identifier: BSD-3-Clause-FreeBSD * * Copyright (c) 2003 Poul-Henning Kamp * All rights reserved. + * Copyright (c) 2022 Alexander Motin * * 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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 "libgeom.h" /* * Amount of extra space we allocate to try and anticipate the size of * confxml. */ #define GEOM_GETXML_SLACK 4096 /* * Number of times to retry in the face of the size of confxml exceeding * that of our buffer. */ #define GEOM_GETXML_RETRIES 4 +/* + * Size of confxml buffer to request via getxml control request. It is + * expected to be sufficient for single geom and its parents. In case of + * overflow fall back to requesting full confxml via sysctl interface. + */ +#define GEOM_GETXML_BUFFER 65536 + char * geom_getxml(void) { char *p; size_t l = 0; int mib[3]; size_t sizep; int retries; sizep = sizeof(mib) / sizeof(*mib); if (sysctlnametomib("kern.geom.confxml", mib, &sizep) != 0) return (NULL); if (sysctl(mib, sizep, NULL, &l, NULL, 0) != 0) return (NULL); l += GEOM_GETXML_SLACK; for (retries = 0; retries < GEOM_GETXML_RETRIES; retries++) { p = malloc(l); if (p == NULL) return (NULL); if (sysctl(mib, sizep, p, &l, NULL, 0) == 0) return (reallocf(p, strlen(p) + 1)); free(p); if (errno != ENOMEM) return (NULL); /* * Our buffer wasn't big enough. Make it bigger and * try again. */ l *= 2; } return (NULL); } + +char * +geom_getxml_geom(const char *class, const char *geom, int parents) +{ + struct gctl_req *r; + char *p; + const char *errstr; + int nargs = 0; + + p = malloc(GEOM_GETXML_BUFFER); + if (p == NULL) + return (NULL); + r = gctl_get_handle(); + gctl_ro_param(r, "class", -1, class); + gctl_ro_param(r, "verb", -1, "getxml"); + gctl_ro_param(r, "parents", sizeof(parents), &parents); + if (geom) { + gctl_ro_param(r, "arg0", -1, geom); + nargs = 1; + } + gctl_ro_param(r, "nargs", sizeof(nargs), &nargs); + p[0] = '\0'; + gctl_add_param(r, "output", GEOM_GETXML_BUFFER, p, + GCTL_PARAM_WR | GCTL_PARAM_ASCII); + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') { + gctl_free(r); + free(p); + return (geom_getxml()); + } + gctl_free(r); + return (p); +} diff --git a/lib/libgeom/geom_xml2tree.c b/lib/libgeom/geom_xml2tree.c index 824800070933..a6da0e6d163f 100644 --- a/lib/libgeom/geom_xml2tree.c +++ b/lib/libgeom/geom_xml2tree.c @@ -1,533 +1,555 @@ /*- * SPDX-License-Identifier: BSD-3-Clause-FreeBSD * * Copyright (c) 2003 Poul-Henning Kamp * 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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 #include #include #include struct mystate { struct gmesh *mesh; struct gclass *class; struct ggeom *geom; struct gprovider *provider; struct gconsumer *consumer; int level; struct sbuf *sbuf[20]; struct gconf *config; int nident; XML_Parser parser; int error; }; static void StartElement(void *userData, const char *name, const char **attr) { struct mystate *mt; void *id; void *ref; int i; mt = userData; mt->level++; mt->sbuf[mt->level] = sbuf_new_auto(); id = NULL; ref = NULL; for (i = 0; attr[i] != NULL; i += 2) { if (!strcmp(attr[i], "id")) { id = (void *)strtoul(attr[i + 1], NULL, 0); mt->nident++; } else if (!strcmp(attr[i], "ref")) { ref = (void *)strtoul(attr[i + 1], NULL, 0); } else printf("%*.*s[%s = %s]\n", mt->level + 1, mt->level + 1, "", attr[i], attr[i + 1]); } if (!strcmp(name, "class") && mt->class == NULL) { mt->class = calloc(1, sizeof *mt->class); if (mt->class == NULL) { mt->error = errno; XML_StopParser(mt->parser, 0); warn("Cannot allocate memory during processing of '%s' " "element", name); return; } mt->class->lg_id = id; LIST_INSERT_HEAD(&mt->mesh->lg_class, mt->class, lg_class); LIST_INIT(&mt->class->lg_geom); LIST_INIT(&mt->class->lg_config); return; } if (!strcmp(name, "geom") && mt->geom == NULL) { mt->geom = calloc(1, sizeof *mt->geom); if (mt->geom == NULL) { mt->error = errno; XML_StopParser(mt->parser, 0); warn("Cannot allocate memory during processing of '%s' " "element", name); return; } mt->geom->lg_id = id; LIST_INSERT_HEAD(&mt->class->lg_geom, mt->geom, lg_geom); LIST_INIT(&mt->geom->lg_provider); LIST_INIT(&mt->geom->lg_consumer); LIST_INIT(&mt->geom->lg_config); return; } if (!strcmp(name, "class") && mt->geom != NULL) { mt->geom->lg_class = ref; return; } if (!strcmp(name, "consumer") && mt->consumer == NULL) { mt->consumer = calloc(1, sizeof *mt->consumer); if (mt->consumer == NULL) { mt->error = errno; XML_StopParser(mt->parser, 0); warn("Cannot allocate memory during processing of '%s' " "element", name); return; } mt->consumer->lg_id = id; LIST_INSERT_HEAD(&mt->geom->lg_consumer, mt->consumer, lg_consumer); LIST_INIT(&mt->consumer->lg_config); return; } if (!strcmp(name, "geom") && mt->consumer != NULL) { mt->consumer->lg_geom = ref; return; } if (!strcmp(name, "provider") && mt->consumer != NULL) { mt->consumer->lg_provider = ref; return; } if (!strcmp(name, "provider") && mt->provider == NULL) { mt->provider = calloc(1, sizeof *mt->provider); if (mt->provider == NULL) { mt->error = errno; XML_StopParser(mt->parser, 0); warn("Cannot allocate memory during processing of '%s' " "element", name); return; } mt->provider->lg_id = id; LIST_INSERT_HEAD(&mt->geom->lg_provider, mt->provider, lg_provider); LIST_INIT(&mt->provider->lg_consumers); LIST_INIT(&mt->provider->lg_config); return; } if (!strcmp(name, "geom") && mt->provider != NULL) { mt->provider->lg_geom = ref; return; } if (!strcmp(name, "config")) { if (mt->provider != NULL) { mt->config = &mt->provider->lg_config; return; } if (mt->consumer != NULL) { mt->config = &mt->consumer->lg_config; return; } if (mt->geom != NULL) { mt->config = &mt->geom->lg_config; return; } if (mt->class != NULL) { mt->config = &mt->class->lg_config; return; } } } static void EndElement(void *userData, const char *name) { struct mystate *mt; struct gconf *c; struct gconfig *gc; char *p; mt = userData; p = NULL; if (sbuf_finish(mt->sbuf[mt->level]) == 0) p = strdup(sbuf_data(mt->sbuf[mt->level])); sbuf_delete(mt->sbuf[mt->level]); mt->sbuf[mt->level] = NULL; mt->level--; if (p == NULL) { mt->error = errno; XML_StopParser(mt->parser, 0); warn("Cannot allocate memory during processing of '%s' " "element", name); return; } if (strlen(p) == 0) { free(p); p = NULL; } if (!strcmp(name, "name")) { if (mt->provider != NULL) { mt->provider->lg_name = p; return; } else if (mt->geom != NULL) { mt->geom->lg_name = p; return; } else if (mt->class != NULL) { mt->class->lg_name = p; return; } } if (!strcmp(name, "rank") && mt->geom != NULL) { mt->geom->lg_rank = strtoul(p, NULL, 0); free(p); return; } if (!strcmp(name, "mode") && mt->provider != NULL) { mt->provider->lg_mode = p; return; } if (!strcmp(name, "mode") && mt->consumer != NULL) { mt->consumer->lg_mode = p; return; } if (!strcmp(name, "mediasize") && mt->provider != NULL) { mt->provider->lg_mediasize = strtoumax(p, NULL, 0); free(p); return; } if (!strcmp(name, "sectorsize") && mt->provider != NULL) { mt->provider->lg_sectorsize = strtoul(p, NULL, 0); free(p); return; } if (!strcmp(name, "stripesize") && mt->provider != NULL) { mt->provider->lg_stripesize = strtoumax(p, NULL, 0); free(p); return; } if (!strcmp(name, "stripeoffset") && mt->provider != NULL) { mt->provider->lg_stripeoffset = strtoumax(p, NULL, 0); free(p); return; } if (!strcmp(name, "config")) { mt->config = NULL; free(p); return; } if (mt->config != NULL || (!strcmp(name, "wither") && (mt->provider != NULL || mt->geom != NULL))) { if (mt->config != NULL) c = mt->config; else if (mt->provider != NULL) c = &mt->provider->lg_config; else c = &mt->geom->lg_config; gc = calloc(1, sizeof *gc); if (gc == NULL) { mt->error = errno; XML_StopParser(mt->parser, 0); warn("Cannot allocate memory during processing of '%s' " "element", name); free(p); return; } gc->lg_name = strdup(name); if (gc->lg_name == NULL) { mt->error = errno; XML_StopParser(mt->parser, 0); warn("Cannot allocate memory during processing of '%s' " "element", name); free(gc); free(p); return; } gc->lg_val = p; LIST_INSERT_HEAD(c, gc, lg_config); return; } if (p != NULL) { #if DEBUG_LIBGEOM > 0 printf("Unexpected XML: name=%s data=\"%s\"\n", name, p); #endif free(p); } if (!strcmp(name, "consumer") && mt->consumer != NULL) { mt->consumer = NULL; return; } if (!strcmp(name, "provider") && mt->provider != NULL) { mt->provider = NULL; return; } if (!strcmp(name, "geom") && mt->consumer != NULL) { return; } if (!strcmp(name, "geom") && mt->provider != NULL) { return; } if (!strcmp(name, "geom") && mt->geom != NULL) { mt->geom = NULL; return; } if (!strcmp(name, "class") && mt->geom != NULL) { return; } if (!strcmp(name, "class") && mt->class != NULL) { mt->class = NULL; return; } } static void CharData(void *userData , const XML_Char *s , int len) { struct mystate *mt; const char *b, *e; mt = userData; b = s; e = s + len - 1; while (isspace(*b) && b < e) b++; while (isspace(*e) && e > b) e--; if (e != b || (*b && !isspace(*b))) sbuf_bcat(mt->sbuf[mt->level], b, e - b + 1); } struct gident * geom_lookupid(struct gmesh *gmp, const void *id) { struct gident *gip; for (gip = gmp->lg_ident; gip->lg_id != NULL; gip++) if (gip->lg_id == id) return (gip); return (NULL); } +static void * +geom_lookupidptr(struct gmesh *gmp, const void *id) +{ + struct gident *gip; + + gip = geom_lookupid(gmp, id); + if (gip) + return (gip->lg_ptr); + return (NULL); +} + int geom_xml2tree(struct gmesh *gmp, char *p) { XML_Parser parser; struct mystate *mt; struct gclass *cl; struct ggeom *ge; struct gprovider *pr; struct gconsumer *co; int error, i; memset(gmp, 0, sizeof *gmp); LIST_INIT(&gmp->lg_class); parser = XML_ParserCreate(NULL); if (parser == NULL) return (ENOMEM); mt = calloc(1, sizeof *mt); if (mt == NULL) { XML_ParserFree(parser); return (ENOMEM); } mt->mesh = gmp; mt->parser = parser; error = 0; XML_SetUserData(parser, mt); XML_SetElementHandler(parser, StartElement, EndElement); XML_SetCharacterDataHandler(parser, CharData); i = XML_Parse(parser, p, strlen(p), 1); if (mt->error != 0) error = mt->error; else if (i != 1) { error = XML_GetErrorCode(parser) == XML_ERROR_NO_MEMORY ? ENOMEM : EILSEQ; } XML_ParserFree(parser); if (error != 0) { free(mt); return (error); } gmp->lg_ident = calloc(sizeof *gmp->lg_ident, mt->nident + 1); free(mt); if (gmp->lg_ident == NULL) return (ENOMEM); i = 0; /* Collect all identifiers */ LIST_FOREACH(cl, &gmp->lg_class, lg_class) { gmp->lg_ident[i].lg_id = cl->lg_id; gmp->lg_ident[i].lg_ptr = cl; gmp->lg_ident[i].lg_what = ISCLASS; i++; LIST_FOREACH(ge, &cl->lg_geom, lg_geom) { gmp->lg_ident[i].lg_id = ge->lg_id; gmp->lg_ident[i].lg_ptr = ge; gmp->lg_ident[i].lg_what = ISGEOM; i++; LIST_FOREACH(pr, &ge->lg_provider, lg_provider) { gmp->lg_ident[i].lg_id = pr->lg_id; gmp->lg_ident[i].lg_ptr = pr; gmp->lg_ident[i].lg_what = ISPROVIDER; i++; } LIST_FOREACH(co, &ge->lg_consumer, lg_consumer) { gmp->lg_ident[i].lg_id = co->lg_id; gmp->lg_ident[i].lg_ptr = co; gmp->lg_ident[i].lg_what = ISCONSUMER; i++; } } } /* Substitute all identifiers */ LIST_FOREACH(cl, &gmp->lg_class, lg_class) { LIST_FOREACH(ge, &cl->lg_geom, lg_geom) { - ge->lg_class = - geom_lookupid(gmp, ge->lg_class)->lg_ptr; - LIST_FOREACH(pr, &ge->lg_provider, lg_provider) { - pr->lg_geom = - geom_lookupid(gmp, pr->lg_geom)->lg_ptr; - } + ge->lg_class = geom_lookupidptr(gmp, ge->lg_class); + LIST_FOREACH(pr, &ge->lg_provider, lg_provider) + pr->lg_geom = geom_lookupidptr(gmp, pr->lg_geom); LIST_FOREACH(co, &ge->lg_consumer, lg_consumer) { - co->lg_geom = - geom_lookupid(gmp, co->lg_geom)->lg_ptr; + co->lg_geom = geom_lookupidptr(gmp, co->lg_geom); if (co->lg_provider != NULL) { - co->lg_provider = - geom_lookupid(gmp, - co->lg_provider)->lg_ptr; - LIST_INSERT_HEAD( - &co->lg_provider->lg_consumers, - co, lg_consumers); + co->lg_provider = geom_lookupidptr(gmp, + co->lg_provider); + if (co->lg_provider != NULL) { + LIST_INSERT_HEAD( + &co->lg_provider->lg_consumers, + co, lg_consumers); + } } } } } return (0); } int geom_gettree(struct gmesh *gmp) { char *p; int error; p = geom_getxml(); if (p == NULL) return (errno); error = geom_xml2tree(gmp, p); free(p); return (error); } +int +geom_gettree_geom(struct gmesh *gmp, const char *c, const char *g, int parents) +{ + char *p; + int error; + + p = geom_getxml_geom(c, g, parents); + if (p == NULL) + return (errno); + error = geom_xml2tree(gmp, p); + free(p); + return (error); +} + static void delete_config(struct gconf *gp) { struct gconfig *cf; for (;;) { cf = LIST_FIRST(gp); if (cf == NULL) return; LIST_REMOVE(cf, lg_config); free(cf->lg_name); free(cf->lg_val); free(cf); } } void geom_deletetree(struct gmesh *gmp) { struct gclass *cl; struct ggeom *ge; struct gprovider *pr; struct gconsumer *co; free(gmp->lg_ident); gmp->lg_ident = NULL; for (;;) { cl = LIST_FIRST(&gmp->lg_class); if (cl == NULL) break; LIST_REMOVE(cl, lg_class); delete_config(&cl->lg_config); if (cl->lg_name) free(cl->lg_name); for (;;) { ge = LIST_FIRST(&cl->lg_geom); if (ge == NULL) break; LIST_REMOVE(ge, lg_geom); delete_config(&ge->lg_config); if (ge->lg_name) free(ge->lg_name); for (;;) { pr = LIST_FIRST(&ge->lg_provider); if (pr == NULL) break; LIST_REMOVE(pr, lg_provider); delete_config(&pr->lg_config); if (pr->lg_name) free(pr->lg_name); if (pr->lg_mode) free(pr->lg_mode); free(pr); } for (;;) { co = LIST_FIRST(&ge->lg_consumer); if (co == NULL) break; LIST_REMOVE(co, lg_consumer); delete_config(&co->lg_config); if (co->lg_mode) free(co->lg_mode); free(co); } free(ge); } free(cl); } } diff --git a/lib/libgeom/libgeom.h b/lib/libgeom/libgeom.h index 9be27208a98c..339a8d34729f 100644 --- a/lib/libgeom/libgeom.h +++ b/lib/libgeom/libgeom.h @@ -1,174 +1,176 @@ /*- * SPDX-License-Identifier: BSD-3-Clause-FreeBSD * * Copyright (c) 2003 Poul-Henning Kamp * 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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$ */ #ifndef _LIBGEOM_H_ #define _LIBGEOM_H_ #include #include #include #include __BEGIN_DECLS #ifndef DEBUG_LIBGEOM #define DEBUG_LIBGEOM 0 #endif void geom_stats_close(void); void geom_stats_resync(void); int geom_stats_open(void); void *geom_stats_snapshot_get(void); void geom_stats_snapshot_free(void *); void geom_stats_snapshot_timestamp(void *, struct timespec *); void geom_stats_snapshot_reset(void *); struct devstat *geom_stats_snapshot_next(void *); char *geom_getxml(void); +char *geom_getxml_geom(const char *, const char *, int); /* geom_xml2tree.c */ /* * These structs are used to build the tree based on the XML. * they're named as the kernel variant without the first '_'. */ struct gclass; struct ggeom; struct gconsumer; struct gprovider; LIST_HEAD(gconf, gconfig); struct gident { void *lg_id; void *lg_ptr; enum { ISCLASS, ISGEOM, ISPROVIDER, ISCONSUMER } lg_what; }; struct gmesh { LIST_HEAD(, gclass) lg_class; struct gident *lg_ident; }; struct gconfig { LIST_ENTRY(gconfig) lg_config; char *lg_name; char *lg_val; }; struct gclass { void *lg_id; char *lg_name; LIST_ENTRY(gclass) lg_class; LIST_HEAD(, ggeom) lg_geom; struct gconf lg_config; }; struct ggeom { void *lg_id; struct gclass *lg_class; char *lg_name; u_int lg_rank; LIST_ENTRY(ggeom) lg_geom; LIST_HEAD(, gconsumer) lg_consumer; LIST_HEAD(, gprovider) lg_provider; struct gconf lg_config; }; struct gconsumer { void *lg_id; struct ggeom *lg_geom; LIST_ENTRY(gconsumer) lg_consumer; struct gprovider *lg_provider; LIST_ENTRY(gconsumer) lg_consumers; char *lg_mode; struct gconf lg_config; }; struct gprovider { void *lg_id; char *lg_name; struct ggeom *lg_geom; LIST_ENTRY(gprovider) lg_provider; LIST_HEAD(, gconsumer) lg_consumers; char *lg_mode; off_t lg_mediasize; u_int lg_sectorsize; off_t lg_stripeoffset; off_t lg_stripesize; struct gconf lg_config; }; struct gident * geom_lookupid(struct gmesh *, const void *); int geom_xml2tree(struct gmesh *, char *); int geom_gettree(struct gmesh *); +int geom_gettree_geom(struct gmesh *, const char *, const char *, int); void geom_deletetree(struct gmesh *); /* geom_ctl.c */ struct gctl_req; #ifdef _STDIO_H_ /* limit #include pollution */ void gctl_dump(struct gctl_req *, FILE *); #endif void gctl_free(struct gctl_req *); struct gctl_req *gctl_get_handle(void); const char *gctl_issue(struct gctl_req *); void gctl_add_param(struct gctl_req *req, const char *name, int len, void *value, int flag); void gctl_ro_param(struct gctl_req *, const char *, int, const void *); void gctl_rw_param(struct gctl_req *, const char *, int, void *); /* geom_util.c */ int g_open(const char *, int); int g_close(int); off_t g_mediasize(int); ssize_t g_sectorsize(int); off_t g_stripeoffset(int); off_t g_stripesize(int); int g_flush(int); int g_delete(int, off_t, off_t); int g_get_ident(int, char *, size_t); int g_get_name(const char *, char *, size_t); int g_open_by_ident(const char *, int, char *, size_t); char *g_device_path(const char *); char *g_providername(int); __END_DECLS #endif /* _LIBGEOM_H_ */ diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c index b29d73327df8..9b43910b88f9 100644 --- a/sbin/geom/core/geom.c +++ b/sbin/geom/core/geom.c @@ -1,1404 +1,1408 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2009 Pawel Jakub Dawidek * 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 AUTHORS 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 AUTHORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc/subr.h" #ifdef STATIC_GEOM_CLASSES extern uint32_t gpart_version; extern struct g_command gpart_class_commands[]; extern uint32_t glabel_version; extern struct g_command glabel_class_commands[]; #endif static char comm[MAXPATHLEN], *class_name = NULL, *gclass_name = NULL; static uint32_t *version = NULL; static int verbose = 0; static struct g_command *class_commands = NULL; #define GEOM_CLASS_CMDS 0x01 #define GEOM_STD_CMDS 0x02 #define GEOM_CLASS_WIDTH 10 static struct g_command *find_command(const char *cmdstr, int flags); static void list_one_geom_by_provider(const char *provider_name); static int std_available(const char *name); static int std_list_available(void); static int std_load_available(void); static void std_help(struct gctl_req *req, unsigned flags); static void std_list(struct gctl_req *req, unsigned flags); static void std_status(struct gctl_req *req, unsigned flags); static void std_load(struct gctl_req *req, unsigned flags); static void std_unload(struct gctl_req *req, unsigned flags); static struct g_command std_commands[] = { { "help", 0, std_help, G_NULL_OPTS, NULL }, { "list", 0, std_list, { { 'a', "all", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-a] [name ...]" }, { "status", 0, std_status, { { 'a', "all", NULL, G_TYPE_BOOL }, { 'g', "geoms", NULL, G_TYPE_BOOL }, { 's', "script", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-ags] [name ...]" }, { "load", G_FLAG_VERBOSE | G_FLAG_LOADKLD, std_load, G_NULL_OPTS, NULL }, { "unload", G_FLAG_VERBOSE, std_unload, G_NULL_OPTS, NULL }, G_CMD_SENTINEL }; static void usage_command(struct g_command *cmd, const char *prefix) { struct g_option *opt; unsigned i; if (cmd->gc_usage != NULL) { char *pos, *ptr, *sptr; sptr = ptr = strdup(cmd->gc_usage); while ((pos = strsep(&ptr, "\n")) != NULL) { if (*pos == '\0') continue; fprintf(stderr, "%s %s %s %s\n", prefix, comm, cmd->gc_name, pos); } free(sptr); return; } fprintf(stderr, "%s %s %s", prefix, comm, cmd->gc_name); if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0) fprintf(stderr, " [-v]"); for (i = 0; ; i++) { opt = &cmd->gc_options[i]; if (opt->go_name == NULL) break; if (opt->go_val != NULL || G_OPT_TYPE(opt) == G_TYPE_BOOL) fprintf(stderr, " ["); else fprintf(stderr, " "); fprintf(stderr, "-%c", opt->go_char); if (G_OPT_TYPE(opt) != G_TYPE_BOOL) fprintf(stderr, " %s", opt->go_name); if (opt->go_val != NULL || G_OPT_TYPE(opt) == G_TYPE_BOOL) fprintf(stderr, "]"); } fprintf(stderr, "\n"); } static void usage(void) { if (class_name == NULL) { fprintf(stderr, "usage: geom [options]\n"); fprintf(stderr, " geom -p \n"); fprintf(stderr, " geom -t\n"); exit(EXIT_FAILURE); } else { struct g_command *cmd; const char *prefix; unsigned i; prefix = "usage:"; if (class_commands != NULL) { for (i = 0; ; i++) { cmd = &class_commands[i]; if (cmd->gc_name == NULL) break; usage_command(cmd, prefix); prefix = " "; } } for (i = 0; ; i++) { cmd = &std_commands[i]; if (cmd->gc_name == NULL) break; /* * If class defines command, which has the same name as * standard command, skip it, because it was already * shown on usage(). */ if (find_command(cmd->gc_name, GEOM_CLASS_CMDS) != NULL) continue; usage_command(cmd, prefix); prefix = " "; } exit(EXIT_FAILURE); } } static void load_module(void) { char name1[64], name2[64]; snprintf(name1, sizeof(name1), "g_%s", class_name); snprintf(name2, sizeof(name2), "geom_%s", class_name); if (modfind(name1) < 0) { /* Not present in kernel, try loading it. */ if (kldload(name2) < 0 || modfind(name1) < 0) { if (errno != EEXIST) { err(EXIT_FAILURE, "cannot load %s", name2); } } } } static int strlcatf(char *str, size_t size, const char *format, ...) { size_t len; va_list ap; int ret; len = strlen(str); str += len; size -= len; va_start(ap, format); ret = vsnprintf(str, size, format, ap); va_end(ap); return (ret); } /* * Find given option in options available for given command. */ static struct g_option * find_option(struct g_command *cmd, char ch) { struct g_option *opt; unsigned i; for (i = 0; ; i++) { opt = &cmd->gc_options[i]; if (opt->go_name == NULL) return (NULL); if (opt->go_char == ch) return (opt); } /* NOTREACHED */ return (NULL); } /* * Add given option to gctl_req. */ static void set_option(struct gctl_req *req, struct g_option *opt, const char *val) { const char *optname; uint64_t number; void *ptr; if (G_OPT_ISMULTI(opt)) { size_t optnamesize; if (G_OPT_NUM(opt) == UCHAR_MAX) errx(EXIT_FAILURE, "Too many -%c options.", opt->go_char); /* * Base option name length plus 3 bytes for option number * (max. 255 options) plus 1 byte for terminating '\0'. */ optnamesize = strlen(opt->go_name) + 3 + 1; ptr = malloc(optnamesize); if (ptr == NULL) errx(EXIT_FAILURE, "No memory."); snprintf(ptr, optnamesize, "%s%u", opt->go_name, G_OPT_NUM(opt)); G_OPT_NUMINC(opt); optname = ptr; } else { optname = opt->go_name; } if (G_OPT_TYPE(opt) == G_TYPE_NUMBER) { if (expand_number(val, &number) == -1) { err(EXIT_FAILURE, "Invalid value for '%c' argument", opt->go_char); } ptr = malloc(sizeof(intmax_t)); if (ptr == NULL) errx(EXIT_FAILURE, "No memory."); *(intmax_t *)ptr = number; opt->go_val = ptr; gctl_ro_param(req, optname, sizeof(intmax_t), opt->go_val); } else if (G_OPT_TYPE(opt) == G_TYPE_STRING) { gctl_ro_param(req, optname, -1, val); } else if (G_OPT_TYPE(opt) == G_TYPE_BOOL) { ptr = malloc(sizeof(int)); if (ptr == NULL) errx(EXIT_FAILURE, "No memory."); *(int *)ptr = *val - '0'; opt->go_val = ptr; gctl_ro_param(req, optname, sizeof(int), opt->go_val); } else { assert(!"Invalid type"); } if (G_OPT_ISMULTI(opt)) free(__DECONST(char *, optname)); } /* * 1. Add given argument by caller. * 2. Add default values of not given arguments. * 3. Add the rest of arguments. */ static void parse_arguments(struct g_command *cmd, struct gctl_req *req, int *argc, char ***argv) { struct g_option *opt; char opts[64]; unsigned i; int ch, vcount; *opts = '\0'; if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0) strlcat(opts, "v", sizeof(opts)); for (i = 0; ; i++) { opt = &cmd->gc_options[i]; if (opt->go_name == NULL) break; assert(G_OPT_TYPE(opt) != 0); assert((opt->go_type & ~(G_TYPE_MASK | G_TYPE_MULTI)) == 0); /* Multiple bool arguments makes no sense. */ assert(G_OPT_TYPE(opt) != G_TYPE_BOOL || (opt->go_type & G_TYPE_MULTI) == 0); strlcatf(opts, sizeof(opts), "%c", opt->go_char); if (G_OPT_TYPE(opt) != G_TYPE_BOOL) strlcat(opts, ":", sizeof(opts)); } /* * Add specified arguments. */ vcount = 0; while ((ch = getopt(*argc, *argv, opts)) != -1) { /* Standard (not passed to kernel) options. */ if (ch == 'v' && (cmd->gc_flags & G_FLAG_VERBOSE) != 0) verbose = 1; /* Options passed to kernel. */ opt = find_option(cmd, ch); if (opt == NULL) { if (ch == 'v' && (cmd->gc_flags & G_FLAG_VERBOSE) != 0){ if (++vcount < 2) continue; else warnx("Option 'v' specified twice."); } usage(); } if (!G_OPT_ISMULTI(opt) && G_OPT_ISDONE(opt)) { warnx("Option '%c' specified twice.", opt->go_char); usage(); } G_OPT_DONE(opt); if (G_OPT_TYPE(opt) == G_TYPE_BOOL) set_option(req, opt, "1"); else set_option(req, opt, optarg); } *argc -= optind; *argv += optind; /* * Add not specified arguments, but with default values. */ for (i = 0; ; i++) { opt = &cmd->gc_options[i]; if (opt->go_name == NULL) break; if (G_OPT_ISDONE(opt)) continue; if (G_OPT_TYPE(opt) == G_TYPE_BOOL) { assert(opt->go_val == NULL); set_option(req, opt, "0"); } else { if (opt->go_val == NULL) { warnx("Option '%c' not specified.", opt->go_char); usage(); } else if (opt->go_val == G_VAL_OPTIONAL) { /* add nothing. */ } else { set_option(req, opt, opt->go_val); } } } /* * Add rest of given arguments. */ gctl_ro_param(req, "nargs", sizeof(int), argc); for (i = 0; i < (unsigned)*argc; i++) { char argname[16]; snprintf(argname, sizeof(argname), "arg%u", i); gctl_ro_param(req, argname, -1, (*argv)[i]); } } /* * Find given command in commands available for given class. */ static struct g_command * find_command(const char *cmdstr, int flags) { struct g_command *cmd; unsigned i; /* * First try to find command defined by loaded library. */ if ((flags & GEOM_CLASS_CMDS) != 0 && class_commands != NULL) { for (i = 0; ; i++) { cmd = &class_commands[i]; if (cmd->gc_name == NULL) break; if (strcmp(cmd->gc_name, cmdstr) == 0) return (cmd); } } /* * Now try to find in standard commands. */ if ((flags & GEOM_STD_CMDS) != 0) { for (i = 0; ; i++) { cmd = &std_commands[i]; if (cmd->gc_name == NULL) break; if (strcmp(cmd->gc_name, cmdstr) == 0) return (cmd); } } return (NULL); } static unsigned set_flags(struct g_command *cmd) { unsigned flags = 0; if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0 && verbose) flags |= G_FLAG_VERBOSE; return (flags); } /* * Run command. */ static void run_command(int argc, char *argv[]) { struct g_command *cmd; struct gctl_req *req; const char *errstr; char buf[4096]; /* First try to find a command defined by a class. */ cmd = find_command(argv[0], GEOM_CLASS_CMDS); if (cmd == NULL) { /* Now, try to find a standard command. */ cmd = find_command(argv[0], GEOM_STD_CMDS); if (cmd == NULL) { warnx("Unknown command: %s.", argv[0]); usage(); } if (!std_available(cmd->gc_name)) { warnx("Command '%s' not available; " "try 'load' first.", argv[0]); exit(EXIT_FAILURE); } } if ((cmd->gc_flags & G_FLAG_LOADKLD) != 0) load_module(); req = gctl_get_handle(); gctl_ro_param(req, "class", -1, gclass_name); gctl_ro_param(req, "verb", -1, argv[0]); if (version != NULL) gctl_ro_param(req, "version", sizeof(*version), version); parse_arguments(cmd, req, &argc, &argv); buf[0] = '\0'; if (cmd->gc_func != NULL) { unsigned flags; flags = set_flags(cmd); cmd->gc_func(req, flags); errstr = req->error; } else { gctl_add_param(req, "output", sizeof(buf), buf, GCTL_PARAM_WR | GCTL_PARAM_ASCII); errstr = gctl_issue(req); } if (errstr != NULL && errstr[0] != '\0') { warnx("%s", errstr); if (strncmp(errstr, "warning: ", strlen("warning: ")) != 0) { gctl_free(req); exit(EXIT_FAILURE); } } if (buf[0] != '\0') printf("%s", buf); gctl_free(req); if (verbose) printf("Done.\n"); exit(EXIT_SUCCESS); } #ifndef STATIC_GEOM_CLASSES static const char * library_path(void) { const char *path; path = getenv("GEOM_LIBRARY_PATH"); if (path == NULL) path = GEOM_CLASS_DIR; return (path); } static void load_library(void) { char *curpath, path[MAXPATHLEN], *tofree, *totalpath; uint32_t *lib_version; void *dlh; int ret; ret = 0; tofree = totalpath = strdup(library_path()); if (totalpath == NULL) err(EXIT_FAILURE, "Not enough memory for library path"); if (strchr(totalpath, ':') != NULL) curpath = strsep(&totalpath, ":"); else curpath = totalpath; /* Traverse the paths to find one that contains the library we want. */ while (curpath != NULL) { snprintf(path, sizeof(path), "%s/geom_%s.so", curpath, class_name); ret = access(path, F_OK); if (ret == -1) { if (errno == ENOENT) { /* * If we cannot find library, try the next * path. */ curpath = strsep(&totalpath, ":"); continue; } err(EXIT_FAILURE, "Cannot access library"); } break; } free(tofree); /* No library was found, but standard commands can still be used */ if (ret == -1) return; dlh = dlopen(path, RTLD_NOW); if (dlh == NULL) errx(EXIT_FAILURE, "Cannot open library: %s.", dlerror()); lib_version = dlsym(dlh, "lib_version"); if (lib_version == NULL) { warnx("Cannot find symbol %s: %s.", "lib_version", dlerror()); dlclose(dlh); exit(EXIT_FAILURE); } if (*lib_version != G_LIB_VERSION) { dlclose(dlh); errx(EXIT_FAILURE, "%s and %s are not synchronized.", getprogname(), path); } version = dlsym(dlh, "version"); if (version == NULL) { warnx("Cannot find symbol %s: %s.", "version", dlerror()); dlclose(dlh); exit(EXIT_FAILURE); } class_commands = dlsym(dlh, "class_commands"); if (class_commands == NULL) { warnx("Cannot find symbol %s: %s.", "class_commands", dlerror()); dlclose(dlh); exit(EXIT_FAILURE); } } #endif /* !STATIC_GEOM_CLASSES */ /* * Class name should be all capital letters. */ static void set_class_name(void) { char *s1, *s2; s1 = class_name; for (; *s1 != '\0'; s1++) *s1 = tolower(*s1); gclass_name = malloc(strlen(class_name) + 1); if (gclass_name == NULL) errx(EXIT_FAILURE, "No memory"); s1 = gclass_name; s2 = class_name; for (; *s2 != '\0'; s2++) *s1++ = toupper(*s2); *s1 = '\0'; } static void get_class(int *argc, char ***argv) { snprintf(comm, sizeof(comm), "%s", basename((*argv)[0])); if (strcmp(comm, "geom") == 0) { if (*argc < 2) usage(); else if (*argc == 2) { if (strcmp((*argv)[1], "-h") == 0 || strcmp((*argv)[1], "help") == 0) { usage(); } } strlcatf(comm, sizeof(comm), " %s", (*argv)[1]); class_name = (*argv)[1]; *argc -= 2; *argv += 2; } else if (*comm == 'g') { class_name = comm + 1; *argc -= 1; *argv += 1; } else { errx(EXIT_FAILURE, "Invalid utility name."); } #ifndef STATIC_GEOM_CLASSES load_library(); #else if (!strcasecmp(class_name, "part")) { version = &gpart_version; class_commands = gpart_class_commands; } else if (!strcasecmp(class_name, "label")) { version = &glabel_version; class_commands = glabel_class_commands; } #endif /* !STATIC_GEOM_CLASSES */ set_class_name(); /* If we can't load or list, it's not a class. */ if (!std_load_available() && !std_list_available()) errx(EXIT_FAILURE, "Invalid class name '%s'.", class_name); if (*argc < 1) usage(); } static struct ggeom * find_geom_by_provider(struct gmesh *mesh, const char *name) { struct gclass *classp; struct ggeom *gp; struct gprovider *pp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { if (strcmp(pp->lg_name, name) == 0) return (gp); } } } return (NULL); } static int compute_tree_width_geom(struct gmesh *mesh, struct ggeom *gp, int indent) { struct gclass *classp2; struct ggeom *gp2; struct gconsumer *cp2; struct gprovider *pp; int max_width, width; max_width = width = indent + strlen(gp->lg_name); LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { LIST_FOREACH(classp2, &mesh->lg_class, lg_class) { LIST_FOREACH(gp2, &classp2->lg_geom, lg_geom) { LIST_FOREACH(cp2, &gp2->lg_consumer, lg_consumer) { if (pp != cp2->lg_provider) continue; width = compute_tree_width_geom(mesh, gp2, indent + 2); if (width > max_width) max_width = width; } } } } return (max_width); } static int compute_tree_width(struct gmesh *mesh) { struct gclass *classp; struct ggeom *gp; int max_width, width; max_width = width = 0; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (!LIST_EMPTY(&gp->lg_consumer)) continue; width = compute_tree_width_geom(mesh, gp, 0); if (width > max_width) max_width = width; } } return (max_width); } static void show_tree_geom(struct gmesh *mesh, struct ggeom *gp, int indent, int width) { struct gclass *classp2; struct ggeom *gp2; struct gconsumer *cp2; struct gprovider *pp; if (LIST_EMPTY(&gp->lg_provider)) { printf("%*s%-*.*s %-*.*s\n", indent, "", width - indent, width - indent, gp->lg_name, GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, gp->lg_class->lg_name); return; } LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { printf("%*s%-*.*s %-*.*s %s\n", indent, "", width - indent, width - indent, gp->lg_name, GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, gp->lg_class->lg_name, pp->lg_name); LIST_FOREACH(classp2, &mesh->lg_class, lg_class) { LIST_FOREACH(gp2, &classp2->lg_geom, lg_geom) { LIST_FOREACH(cp2, &gp2->lg_consumer, lg_consumer) { if (pp != cp2->lg_provider) continue; show_tree_geom(mesh, gp2, indent + 2, width); } } } } } static void show_tree(void) { struct gmesh mesh; struct gclass *classp; struct ggeom *gp; int error, width; error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); width = compute_tree_width(&mesh); printf("%-*.*s %-*.*s %s\n", width, width, "Geom", GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, "Class", "Provider"); LIST_FOREACH(classp, &mesh.lg_class, lg_class) { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (!LIST_EMPTY(&gp->lg_consumer)) continue; show_tree_geom(&mesh, gp, 0, width); } } } int main(int argc, char *argv[]) { char *provider_name; bool tflag; int ch; provider_name = NULL; tflag = false; if (strcmp(getprogname(), "geom") == 0) { while ((ch = getopt(argc, argv, "hp:t")) != -1) { switch (ch) { case 'p': provider_name = strdup(optarg); if (provider_name == NULL) err(1, "strdup"); break; case 't': tflag = true; break; case 'h': default: usage(); } } /* * Don't adjust argc and argv, it would break get_class(). */ } if (tflag && provider_name != NULL) { errx(EXIT_FAILURE, "At most one of -P and -t may be specified."); } if (provider_name != NULL) { list_one_geom_by_provider(provider_name); return (0); } if (tflag) { show_tree(); return (0); } get_class(&argc, &argv); run_command(argc, argv); /* NOTREACHED */ exit(EXIT_FAILURE); } static struct gclass * find_class(struct gmesh *mesh, const char *name) { struct gclass *classp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, name) == 0) return (classp); } return (NULL); } static struct ggeom * find_geom(struct gclass *classp, const char *name) { struct ggeom *gp; if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) name += sizeof(_PATH_DEV) - 1; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (strcmp(gp->lg_name, name) == 0) return (gp); } return (NULL); } static void list_one_provider(struct gprovider *pp, const char *prefix) { struct gconfig *conf; char buf[5]; printf("Name: %s\n", pp->lg_name); humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize, buf); printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize); if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) { printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize); printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset); } printf("%sMode: %s\n", prefix, pp->lg_mode); LIST_FOREACH(conf, &pp->lg_config, lg_config) { printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val); } } static void list_one_consumer(struct gconsumer *cp, const char *prefix) { struct gprovider *pp; struct gconfig *conf; pp = cp->lg_provider; if (pp == NULL) printf("[no provider]\n"); else { char buf[5]; printf("Name: %s\n", pp->lg_name); humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize, buf); printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize); if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) { printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize); printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset); } printf("%sMode: %s\n", prefix, cp->lg_mode); } LIST_FOREACH(conf, &cp->lg_config, lg_config) { printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val); } } static void list_one_geom(struct ggeom *gp) { struct gprovider *pp; struct gconsumer *cp; struct gconfig *conf; unsigned n; printf("Geom name: %s\n", gp->lg_name); LIST_FOREACH(conf, &gp->lg_config, lg_config) { printf("%s: %s\n", conf->lg_name, conf->lg_val); } if (!LIST_EMPTY(&gp->lg_provider)) { printf("Providers:\n"); n = 1; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { printf("%u. ", n++); list_one_provider(pp, " "); } } if (!LIST_EMPTY(&gp->lg_consumer)) { printf("Consumers:\n"); n = 1; LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) { printf("%u. ", n++); list_one_consumer(cp, " "); } } printf("\n"); } static void list_one_geom_by_provider(const char *provider_name) { struct gmesh mesh; struct ggeom *gp; int error; error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); gp = find_geom_by_provider(&mesh, provider_name); if (gp == NULL) errx(EXIT_FAILURE, "Cannot find provider '%s'.", provider_name); printf("Geom class: %s\n", gp->lg_class->lg_name); list_one_geom(gp); } static void std_help(struct gctl_req *req __unused, unsigned flags __unused) { usage(); } static int std_list_available(void) { struct gmesh mesh; struct gclass *classp; int error; - error = geom_gettree(&mesh); + error = geom_gettree_geom(&mesh, gclass_name, "", 0); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, gclass_name); geom_deletetree(&mesh); if (classp != NULL) return (1); return (0); } static void std_list(struct gctl_req *req, unsigned flags __unused) { struct gmesh mesh; struct gclass *classp; struct ggeom *gp; const char *name; int all, error, i, nargs; - error = geom_gettree(&mesh); + nargs = gctl_get_int(req, "nargs"); + if (nargs == 1) { + error = geom_gettree_geom(&mesh, gclass_name, + gctl_get_ascii(req, "arg0"), 1); + } else + error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, gclass_name); if (classp == NULL) { geom_deletetree(&mesh); errx(EXIT_FAILURE, "Class '%s' not found.", gclass_name); } - nargs = gctl_get_int(req, "nargs"); all = gctl_get_int(req, "all"); if (nargs > 0) { for (i = 0; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); gp = find_geom(classp, name); if (gp == NULL) { errx(EXIT_FAILURE, "Class '%s' does not have " "an instance named '%s'.", gclass_name, name); } list_one_geom(gp); } } else { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (LIST_EMPTY(&gp->lg_provider) && !all) continue; list_one_geom(gp); } } geom_deletetree(&mesh); } static int std_status_available(void) { /* 'status' command is available when 'list' command is. */ return (std_list_available()); } static void status_update_len(struct ggeom *gp, int *name_len, int *status_len) { struct gconfig *conf; int len; assert(gp != NULL); assert(name_len != NULL); assert(status_len != NULL); len = strlen(gp->lg_name); if (*name_len < len) *name_len = len; LIST_FOREACH(conf, &gp->lg_config, lg_config) { if (strcasecmp(conf->lg_name, "state") == 0) { len = strlen(conf->lg_val); if (*status_len < len) *status_len = len; } } } static void status_update_len_prs(struct ggeom *gp, int *name_len, int *status_len) { struct gprovider *pp; struct gconfig *conf; int len, glen; assert(gp != NULL); assert(name_len != NULL); assert(status_len != NULL); glen = 0; LIST_FOREACH(conf, &gp->lg_config, lg_config) { if (strcasecmp(conf->lg_name, "state") == 0) { glen = strlen(conf->lg_val); break; } } LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { len = strlen(pp->lg_name); if (*name_len < len) *name_len = len; len = glen; LIST_FOREACH(conf, &pp->lg_config, lg_config) { if (strcasecmp(conf->lg_name, "state") == 0) { len = strlen(conf->lg_val); break; } } if (*status_len < len) *status_len = len; } } static char * status_one_consumer(struct gconsumer *cp) { static char buf[256]; struct gprovider *pp; struct gconfig *conf; const char *state, *syncr; pp = cp->lg_provider; if (pp == NULL) return (NULL); state = NULL; syncr = NULL; LIST_FOREACH(conf, &cp->lg_config, lg_config) { if (strcasecmp(conf->lg_name, "state") == 0) state = conf->lg_val; if (strcasecmp(conf->lg_name, "synchronized") == 0) syncr = conf->lg_val; } if (state == NULL && syncr == NULL) snprintf(buf, sizeof(buf), "%s", pp->lg_name); else if (state != NULL && syncr != NULL) { snprintf(buf, sizeof(buf), "%s (%s, %s)", pp->lg_name, state, syncr); } else { snprintf(buf, sizeof(buf), "%s (%s)", pp->lg_name, state ? state : syncr); } return (buf); } static void status_one_geom(struct ggeom *gp, int script, int name_len, int status_len) { struct gconsumer *cp; struct gconfig *conf; const char *name, *status, *component; int gotone; name = gp->lg_name; status = "N/A"; LIST_FOREACH(conf, &gp->lg_config, lg_config) { if (strcasecmp(conf->lg_name, "state") == 0) { status = conf->lg_val; break; } } gotone = 0; LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) { component = status_one_consumer(cp); if (component == NULL) continue; gotone = 1; printf("%*s %*s %s\n", name_len, name, status_len, status, component); if (!script) name = status = ""; } if (!gotone) { printf("%*s %*s %s\n", name_len, name, status_len, status, "N/A"); } } static void status_one_geom_prs(struct ggeom *gp, int script, int name_len, int status_len) { struct gprovider *pp; struct gconsumer *cp; struct gconfig *conf; const char *name, *status, *component; int gotone; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { name = pp->lg_name; status = "N/A"; LIST_FOREACH(conf, &gp->lg_config, lg_config) { if (strcasecmp(conf->lg_name, "state") == 0) { status = conf->lg_val; break; } } LIST_FOREACH(conf, &pp->lg_config, lg_config) { if (strcasecmp(conf->lg_name, "state") == 0) { status = conf->lg_val; break; } } gotone = 0; LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) { component = status_one_consumer(cp); if (component == NULL) continue; gotone = 1; printf("%*s %*s %s\n", name_len, name, status_len, status, component); if (!script) name = status = ""; } if (!gotone) { printf("%*s %*s %s\n", name_len, name, status_len, status, "N/A"); } } } static void std_status(struct gctl_req *req, unsigned flags __unused) { struct gmesh mesh; struct gclass *classp; struct ggeom *gp; const char *name; int name_len, status_len; int all, error, geoms, i, n, nargs, script; error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, gclass_name); if (classp == NULL) errx(EXIT_FAILURE, "Class %s not found.", gclass_name); nargs = gctl_get_int(req, "nargs"); all = gctl_get_int(req, "all"); geoms = gctl_get_int(req, "geoms"); script = gctl_get_int(req, "script"); if (script) { name_len = 0; status_len = 0; } else { name_len = strlen("Name"); status_len = strlen("Status"); } if (nargs > 0) { for (i = 0, n = 0; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); gp = find_geom(classp, name); if (gp == NULL) errx(EXIT_FAILURE, "No such geom: %s.", name); if (geoms) { status_update_len(gp, &name_len, &status_len); } else { status_update_len_prs(gp, &name_len, &status_len); } n++; } if (n == 0) goto end; } else { n = 0; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (LIST_EMPTY(&gp->lg_provider) && !all) continue; if (geoms) { status_update_len(gp, &name_len, &status_len); } else { status_update_len_prs(gp, &name_len, &status_len); } n++; } if (n == 0) goto end; } if (!script) { printf("%*s %*s %s\n", name_len, "Name", status_len, "Status", "Components"); } if (nargs > 0) { for (i = 0; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); gp = find_geom(classp, name); if (gp == NULL) continue; if (geoms) { status_one_geom(gp, script, name_len, status_len); } else { status_one_geom_prs(gp, script, name_len, status_len); } } } else { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (LIST_EMPTY(&gp->lg_provider) && !all) continue; if (geoms) { status_one_geom(gp, script, name_len, status_len); } else { status_one_geom_prs(gp, script, name_len, status_len); } } } end: geom_deletetree(&mesh); } static int std_load_available(void) { char name[MAXPATHLEN], paths[MAXPATHLEN * 8], *p; struct stat sb; size_t len; snprintf(name, sizeof(name), "g_%s", class_name); /* * If already in kernel, "load" command is NOP. */ if (modfind(name) >= 0) return (1); bzero(paths, sizeof(paths)); len = sizeof(paths); if (sysctlbyname("kern.module_path", paths, &len, NULL, 0) < 0) err(EXIT_FAILURE, "sysctl(kern.module_path)"); for (p = strtok(paths, ";"); p != NULL; p = strtok(NULL, ";")) { snprintf(name, sizeof(name), "%s/geom_%s.ko", p, class_name); /* * If geom_.ko file exists, "load" command is available. */ if (stat(name, &sb) == 0) return (1); } return (0); } static void std_load(struct gctl_req *req __unused, unsigned flags) { /* * Do nothing special here, because of G_FLAG_LOADKLD flag, * module is already loaded. */ if ((flags & G_FLAG_VERBOSE) != 0) printf("Module available.\n"); } static int std_unload_available(void) { char name[64]; int id; snprintf(name, sizeof(name), "geom_%s", class_name); id = kldfind(name); if (id >= 0) return (1); return (0); } static void std_unload(struct gctl_req *req, unsigned flags __unused) { char name[64]; int id; snprintf(name, sizeof(name), "geom_%s", class_name); id = kldfind(name); if (id < 0) { gctl_error(req, "Could not find module: %s.", strerror(errno)); return; } if (kldunload(id) < 0) { gctl_error(req, "Could not unload module: %s.", strerror(errno)); return; } } static int std_available(const char *name) { if (strcmp(name, "help") == 0) return (1); else if (strcmp(name, "list") == 0) return (std_list_available()); else if (strcmp(name, "status") == 0) return (std_status_available()); else if (strcmp(name, "load") == 0) return (std_load_available()); else if (strcmp(name, "unload") == 0) return (std_unload_available()); else assert(!"Unknown standard command."); return (0); } diff --git a/sys/geom/geom.h b/sys/geom/geom.h index d9ba42b005de..365afcef60fe 100644 --- a/sys/geom/geom.h +++ b/sys/geom/geom.h @@ -1,443 +1,444 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2002 Poul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Poul-Henning Kamp * and NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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$ */ #ifndef _GEOM_GEOM_H_ #define _GEOM_GEOM_H_ #include #include #include #include #include #include #include struct g_class; struct g_geom; struct g_consumer; struct g_provider; struct g_stat; struct thread; struct bio; struct sbuf; struct gctl_req; struct g_configargs; struct disk_zone_args; typedef int g_config_t (struct g_configargs *ca); typedef void g_ctl_req_t (struct gctl_req *, struct g_class *cp, char const *verb); typedef int g_ctl_create_geom_t (struct gctl_req *, struct g_class *cp, struct g_provider *pp); typedef int g_ctl_destroy_geom_t (struct gctl_req *, struct g_class *cp, struct g_geom *gp); typedef int g_ctl_config_geom_t (struct gctl_req *, struct g_geom *gp, const char *verb); typedef void g_init_t (struct g_class *mp); typedef void g_fini_t (struct g_class *mp); typedef struct g_geom * g_taste_t (struct g_class *, struct g_provider *, int flags); typedef int g_ioctl_t(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td); #define G_TF_NORMAL 0 #define G_TF_INSIST 1 #define G_TF_TRANSPARENT 2 typedef int g_access_t (struct g_provider *, int, int, int); /* XXX: not sure about the thread arg */ typedef void g_orphan_t (struct g_consumer *); typedef void g_start_t (struct bio *); typedef void g_spoiled_t (struct g_consumer *); typedef void g_attrchanged_t (struct g_consumer *, const char *attr); typedef void g_provgone_t (struct g_provider *); typedef void g_dumpconf_t (struct sbuf *, const char *indent, struct g_geom *, struct g_consumer *, struct g_provider *); typedef void g_resize_t(struct g_consumer *cp); /* * The g_class structure describes a transformation class. In other words * all BSD disklabel handlers share one g_class, all MBR handlers share * one common g_class and so on. * Certain operations are instantiated on the class, most notably the * taste and config_geom functions. */ struct g_class { const char *name; u_int version; u_int spare0; g_taste_t *taste; g_config_t *config; g_ctl_req_t *ctlreq; g_init_t *init; g_fini_t *fini; g_ctl_destroy_geom_t *destroy_geom; /* * Default values for geom methods */ g_start_t *start; g_spoiled_t *spoiled; g_attrchanged_t *attrchanged; g_dumpconf_t *dumpconf; g_access_t *access; g_orphan_t *orphan; g_ioctl_t *ioctl; g_provgone_t *providergone; g_resize_t *resize; void *spare1; void *spare2; /* * The remaining elements are private */ LIST_ENTRY(g_class) class; LIST_HEAD(,g_geom) geom; }; #define G_VERSION_00 0x19950323 #define G_VERSION_01 0x20041207 /* add fflag to g_ioctl_t */ #define G_VERSION G_VERSION_01 /* * The g_geom is an instance of a g_class. */ struct g_geom { char *name; struct g_class *class; LIST_ENTRY(g_geom) geom; LIST_HEAD(,g_consumer) consumer; LIST_HEAD(,g_provider) provider; TAILQ_ENTRY(g_geom) geoms; /* XXX: better name */ int rank; g_start_t *start; g_spoiled_t *spoiled; g_attrchanged_t *attrchanged; g_dumpconf_t *dumpconf; g_access_t *access; g_orphan_t *orphan; g_ioctl_t *ioctl; g_provgone_t *providergone; g_resize_t *resize; void *spare0; void *spare1; void *softc; unsigned flags; #define G_GEOM_WITHER 0x01 #define G_GEOM_VOLATILE_BIO 0x02 #define G_GEOM_IN_ACCESS 0x04 #define G_GEOM_ACCESS_WAIT 0x08 }; /* * The g_bioq is a queue of struct bio's. * XXX: possibly collection point for statistics. * XXX: should (possibly) be collapsed with sys/bio.h::bio_queue_head. */ struct g_bioq { TAILQ_HEAD(, bio) bio_queue; struct mtx bio_queue_lock; int bio_queue_length; }; /* * A g_consumer is an attachment point for a g_provider. One g_consumer * can only be attached to one g_provider, but multiple g_consumers * can be attached to one g_provider. */ struct g_consumer { struct g_geom *geom; LIST_ENTRY(g_consumer) consumer; struct g_provider *provider; LIST_ENTRY(g_consumer) consumers; /* XXX: better name */ int acr, acw, ace; int flags; #define G_CF_SPOILED 0x1 #define G_CF_ORPHAN 0x4 #define G_CF_DIRECT_SEND 0x10 #define G_CF_DIRECT_RECEIVE 0x20 struct devstat *stat; u_int nstart, nend; /* Two fields for the implementing class to use */ void *private; u_int index; }; /* * The g_geom_alias is a list node for aliases for the provider name for device * node creation. */ struct g_geom_alias { LIST_ENTRY(g_geom_alias) ga_next; const char *ga_alias; }; /* * A g_provider is a "logical disk". */ struct g_provider { char *name; LIST_ENTRY(g_provider) provider; struct g_geom *geom; LIST_HEAD(,g_consumer) consumers; int acr, acw, ace; int error; TAILQ_ENTRY(g_provider) orphan; off_t mediasize; u_int sectorsize; off_t stripesize; off_t stripeoffset; struct devstat *stat; u_int spare1; u_int spare2; u_int flags; #define G_PF_WITHER 0x2 #define G_PF_ORPHAN 0x4 #define G_PF_ACCEPT_UNMAPPED 0x8 #define G_PF_DIRECT_SEND 0x10 #define G_PF_DIRECT_RECEIVE 0x20 LIST_HEAD(,g_geom_alias) aliases; /* Two fields for the implementing class to use */ void *private; u_int index; }; /* BIO_GETATTR("GEOM::setstate") argument values. */ #define G_STATE_FAILED 0 #define G_STATE_REBUILD 1 #define G_STATE_RESYNC 2 #define G_STATE_ACTIVE 3 /* geom_dev.c */ struct cdev; void g_dev_print(void); void g_dev_physpath_changed(void); struct g_provider *g_dev_getprovider(struct cdev *dev); /* geom_dump.c */ void (g_trace)(int level, const char *, ...) __printflike(2, 3); #define G_T_TOPOLOGY 0x01 #define G_T_BIO 0x02 #define G_T_ACCESS 0x04 extern int g_debugflags; #define G_F_FOOTSHOOTING 0x10 #define G_F_DISKIOCTL 0x40 #define G_F_CTLDUMP 0x80 #define g_trace(level, fmt, ...) do { \ if (__predict_false(g_debugflags & (level))) \ (g_trace)(level, fmt, ## __VA_ARGS__); \ } while (0) /* geom_event.c */ typedef void g_event_t(void *, int flag); struct g_event; #define EV_CANCEL 1 int g_post_event(g_event_t *func, void *arg, int flag, ...); int g_waitfor_event(g_event_t *func, void *arg, int flag, ...); void g_cancel_event(void *ref); int g_attr_changed(struct g_provider *pp, const char *attr, int flag); int g_media_changed(struct g_provider *pp, int flag); int g_media_gone(struct g_provider *pp, int flag); void g_orphan_provider(struct g_provider *pp, int error); struct g_event *g_alloc_event(int flag); void g_post_event_ep(g_event_t *func, void *arg, struct g_event *ep, ...); /* geom_subr.c */ int g_access(struct g_consumer *cp, int nread, int nwrite, int nexcl); int g_attach(struct g_consumer *cp, struct g_provider *pp); int g_compare_names(const char *namea, const char *nameb); void g_destroy_consumer(struct g_consumer *cp); void g_destroy_geom(struct g_geom *pp); void g_destroy_provider(struct g_provider *pp); void g_detach(struct g_consumer *cp); void g_error_provider(struct g_provider *pp, int error); struct g_provider *g_provider_by_name(char const *arg); int g_getattr__(const char *attr, struct g_consumer *cp, void *var, int len); #define g_getattr(a, c, v) g_getattr__((a), (c), (v), sizeof *(v)) int g_handleattr(struct bio *bp, const char *attribute, const void *val, int len); int g_handleattr_int(struct bio *bp, const char *attribute, int val); int g_handleattr_off_t(struct bio *bp, const char *attribute, off_t val); int g_handleattr_uint16_t(struct bio *bp, const char *attribute, uint16_t val); int g_handleattr_str(struct bio *bp, const char *attribute, const char *str); struct g_consumer * g_new_consumer(struct g_geom *gp); struct g_geom * g_new_geomf(struct g_class *mp, const char *fmt, ...) __printflike(2, 3); struct g_provider * g_new_providerf(struct g_geom *gp, const char *fmt, ...) __printflike(2, 3); void g_provider_add_alias(struct g_provider *pp, const char *fmt, ...) __printflike(2, 3); void g_resize_provider(struct g_provider *pp, off_t size); int g_retaste(struct g_class *mp); void g_spoil(struct g_provider *pp, struct g_consumer *cp); int g_std_access(struct g_provider *pp, int dr, int dw, int de); void g_std_done(struct bio *bp); void g_std_spoiled(struct g_consumer *cp); void g_wither_geom(struct g_geom *gp, int error); void g_wither_geom_close(struct g_geom *gp, int error); void g_wither_provider(struct g_provider *pp, int error); #if defined(DIAGNOSTIC) || defined(DDB) int g_valid_obj(void const *ptr); #endif #ifdef DIAGNOSTIC #define G_VALID_CLASS(foo) \ KASSERT(g_valid_obj(foo) == 1, ("%p is not a g_class", foo)) #define G_VALID_GEOM(foo) \ KASSERT(g_valid_obj(foo) == 2, ("%p is not a g_geom", foo)) #define G_VALID_CONSUMER(foo) \ KASSERT(g_valid_obj(foo) == 3, ("%p is not a g_consumer", foo)) #define G_VALID_PROVIDER(foo) \ KASSERT(g_valid_obj(foo) == 4, ("%p is not a g_provider", foo)) #else #define G_VALID_CLASS(foo) do { } while (0) #define G_VALID_GEOM(foo) do { } while (0) #define G_VALID_CONSUMER(foo) do { } while (0) #define G_VALID_PROVIDER(foo) do { } while (0) #endif int g_modevent(module_t, int, void *); /* geom_io.c */ struct bio * g_clone_bio(struct bio *); struct bio * g_duplicate_bio(struct bio *); void g_destroy_bio(struct bio *); void g_io_deliver(struct bio *bp, int error); int g_io_getattr(const char *attr, struct g_consumer *cp, int *len, void *ptr); int g_io_zonecmd(struct disk_zone_args *zone_args, struct g_consumer *cp); int g_io_flush(struct g_consumer *cp); int g_io_speedup(off_t shortage, u_int flags, size_t *resid, struct g_consumer *cp); void g_io_request(struct bio *bp, struct g_consumer *cp); struct bio *g_new_bio(void); struct bio *g_alloc_bio(void); void g_reset_bio(struct bio *); void * g_read_data(struct g_consumer *cp, off_t offset, off_t length, int *error); int g_write_data(struct g_consumer *cp, off_t offset, void *ptr, off_t length); int g_delete_data(struct g_consumer *cp, off_t offset, off_t length); void g_format_bio(struct sbuf *, const struct bio *bp); void g_print_bio(const char *prefix, const struct bio *bp, const char *fmtsuffix, ...) __printflike(3, 4); int g_use_g_read_data(void *, off_t, void **, int); int g_use_g_write_data(void *, off_t, void *, int); /* geom_kern.c / geom_kernsim.c */ #ifdef _KERNEL extern struct sx topology_lock; struct g_kerneldump { off_t offset; off_t length; struct dumperinfo di; }; MALLOC_DECLARE(M_GEOM); static __inline void * g_malloc(int size, int flags) { void *p; p = malloc(size, M_GEOM, flags); return (p); } static __inline void g_free(void *ptr) { #ifdef DIAGNOSTIC if (sx_xlocked(&topology_lock)) { KASSERT(g_valid_obj(ptr) == 0, ("g_free(%p) of live object, type %d", ptr, g_valid_obj(ptr))); } #endif free(ptr, M_GEOM); } #define g_topology_lock() \ do { \ sx_xlock(&topology_lock); \ } while (0) #define g_topology_try_lock() sx_try_xlock(&topology_lock) #define g_topology_unlock() \ do { \ sx_xunlock(&topology_lock); \ } while (0) #define g_topology_locked() sx_xlocked(&topology_lock) #define g_topology_assert() \ do { \ sx_assert(&topology_lock, SX_XLOCKED); \ } while (0) #define g_topology_assert_not() \ do { \ sx_assert(&topology_lock, SX_UNLOCKED); \ } while (0) #define g_topology_sleep(chan, timo) \ sx_sleep(chan, &topology_lock, 0, "gtopol", timo) #define DECLARE_GEOM_CLASS(class, name) \ static moduledata_t name##_mod = { \ #name, g_modevent, &class \ }; \ DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND); int g_is_geom_thread(struct thread *td); #ifndef _PATH_DEV #define _PATH_DEV "/dev/" #endif #endif /* _KERNEL */ /* geom_ctl.c */ int gctl_set_param(struct gctl_req *req, const char *param, void const *ptr, int len); void gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr, int len); void *gctl_get_param(struct gctl_req *req, const char *param, int *len); +void *gctl_get_param_flags(struct gctl_req *req, const char *param, int flags, int *len); char const *gctl_get_asciiparam(struct gctl_req *req, const char *param); void *gctl_get_paraml(struct gctl_req *req, const char *param, int len); void *gctl_get_paraml_opt(struct gctl_req *req, const char *param, int len); int gctl_error(struct gctl_req *req, const char *fmt, ...) __printflike(2, 3); struct g_class *gctl_get_class(struct gctl_req *req, char const *arg); struct g_geom *gctl_get_geom(struct gctl_req *req, struct g_class *mp, char const *arg); struct g_provider *gctl_get_provider(struct gctl_req *req, char const *arg); #endif /* _GEOM_GEOM_H_ */ diff --git a/sys/geom/geom_ctl.c b/sys/geom/geom_ctl.c index ba22a2c5216a..f246891d4626 100644 --- a/sys/geom/geom_ctl.c +++ b/sys/geom/geom_ctl.c @@ -1,526 +1,585 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2002 Poul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. + * Copyright (c) 2022 Alexander Motin * * This software was developed for the FreeBSD Project by Poul-Henning Kamp * and NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #define GCTL_TABLE 1 #include #include static d_ioctl_t g_ctl_ioctl; static struct cdevsw g_ctl_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_ioctl = g_ctl_ioctl, .d_name = "g_ctl", }; CTASSERT(GCTL_PARAM_RD == VM_PROT_READ); CTASSERT(GCTL_PARAM_WR == VM_PROT_WRITE); void g_ctl_init(void) { make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL, UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL); } /* * Report an error back to the user in ascii format. Return nerror * or EINVAL if nerror isn't specified. */ int gctl_error(struct gctl_req *req, const char *fmt, ...) { va_list ap; if (req == NULL) return (EINVAL); /* We only record the first error */ if (sbuf_done(req->serror)) { if (!req->nerror) req->nerror = EEXIST; return (req->nerror); } if (!req->nerror) req->nerror = EINVAL; va_start(ap, fmt); sbuf_vprintf(req->serror, fmt, ap); va_end(ap); sbuf_finish(req->serror); if (g_debugflags & G_F_CTLDUMP) printf("gctl %p error \"%s\"\n", req, sbuf_data(req->serror)); return (req->nerror); } /* * Allocate space and copyin() something. * XXX: this should really be a standard function in the kernel. */ static void * geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len) { void *ptr; ptr = g_malloc(len, M_WAITOK); req->nerror = copyin(uaddr, ptr, len); if (!req->nerror) return (ptr); g_free(ptr); return (NULL); } static void gctl_copyin(struct gctl_req *req) { struct gctl_req_arg *ap; char *p; u_int i; if (req->narg > GEOM_CTL_ARG_MAX) { gctl_error(req, "too many arguments"); req->arg = NULL; return; } ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap)); if (ap == NULL) { gctl_error(req, "bad control request"); req->arg = NULL; return; } /* Nothing have been copyin()'ed yet */ for (i = 0; i < req->narg; i++) { ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL); ap[i].flag &= ~GCTL_PARAM_CHANGED; ap[i].kvalue = NULL; } for (i = 0; i < req->narg; i++) { if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) { gctl_error(req, "wrong param name length %d: %d", i, ap[i].nlen); break; } p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen); if (p == NULL) break; if (p[ap[i].nlen - 1] != '\0') { gctl_error(req, "unterminated param name"); g_free(p); break; } ap[i].name = p; ap[i].flag |= GCTL_PARAM_NAMEKERNEL; if (ap[i].len <= 0) { gctl_error(req, "negative param length"); break; } if (ap[i].flag & GCTL_PARAM_RD) { p = geom_alloc_copyin(req, ap[i].value, ap[i].len); if (p == NULL) break; if ((ap[i].flag & GCTL_PARAM_ASCII) && p[ap[i].len - 1] != '\0') { gctl_error(req, "unterminated param value"); g_free(p); break; } } else { p = g_malloc(ap[i].len, M_WAITOK | M_ZERO); } ap[i].kvalue = p; ap[i].flag |= GCTL_PARAM_VALUEKERNEL; } req->arg = ap; return; } static void gctl_copyout(struct gctl_req *req) { int error, i; struct gctl_req_arg *ap; if (req->nerror) return; error = 0; ap = req->arg; for (i = 0; i < req->narg; i++, ap++) { if (!(ap->flag & GCTL_PARAM_CHANGED)) continue; error = copyout(ap->kvalue, ap->value, ap->len); if (!error) continue; req->nerror = error; return; } return; } static void gctl_free(struct gctl_req *req) { u_int i; sbuf_delete(req->serror); if (req->arg == NULL) return; for (i = 0; i < req->narg; i++) { if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL) g_free(req->arg[i].name); if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) && req->arg[i].len > 0) g_free(req->arg[i].kvalue); } g_free(req->arg); } static void gctl_dump(struct gctl_req *req, const char *what) { struct gctl_req_arg *ap; u_int i; int j; printf("Dump of gctl %s at %p:\n", what, req); if (req->nerror > 0) { printf(" nerror:\t%d\n", req->nerror); if (sbuf_len(req->serror) > 0) printf(" error:\t\"%s\"\n", sbuf_data(req->serror)); } if (req->arg == NULL) return; for (i = 0; i < req->narg; i++) { ap = &req->arg[i]; if (!(ap->flag & GCTL_PARAM_NAMEKERNEL)) printf(" param:\t%d@%p", ap->nlen, ap->name); else printf(" param:\t\"%s\"", ap->name); printf(" [%s%s%d] = ", ap->flag & GCTL_PARAM_RD ? "R" : "", ap->flag & GCTL_PARAM_WR ? "W" : "", ap->len); if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) { printf(" =@ %p", ap->value); } else if (ap->flag & GCTL_PARAM_ASCII) { printf("\"%s\"", (char *)ap->kvalue); } else if (ap->len > 0) { for (j = 0; j < ap->len && j < 512; j++) printf(" %02x", ((u_char *)ap->kvalue)[j]); } else { printf(" = %p", ap->kvalue); } printf("\n"); } } int gctl_set_param(struct gctl_req *req, const char *param, void const *ptr, int len) { u_int i; struct gctl_req_arg *ap; for (i = 0; i < req->narg; i++) { ap = &req->arg[i]; if (strcmp(param, ap->name)) continue; if (!(ap->flag & GCTL_PARAM_WR)) return (EPERM); ap->flag |= GCTL_PARAM_CHANGED; if (ap->len < len) { bcopy(ptr, ap->kvalue, ap->len); return (ENOSPC); } bcopy(ptr, ap->kvalue, len); return (0); } return (EINVAL); } void gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr, int len) { switch (gctl_set_param(req, param, ptr, len)) { case EPERM: gctl_error(req, "No write access %s argument", param); break; case ENOSPC: gctl_error(req, "Wrong length %s argument", param); break; case EINVAL: gctl_error(req, "Missing %s argument", param); break; } } void * -gctl_get_param(struct gctl_req *req, const char *param, int *len) +gctl_get_param_flags(struct gctl_req *req, const char *param, int flags, int *len) { u_int i; void *p; struct gctl_req_arg *ap; for (i = 0; i < req->narg; i++) { ap = &req->arg[i]; if (strcmp(param, ap->name)) continue; - if (!(ap->flag & GCTL_PARAM_RD)) + if ((ap->flag & flags) != flags) continue; p = ap->kvalue; if (len != NULL) *len = ap->len; return (p); } return (NULL); } +void * +gctl_get_param(struct gctl_req *req, const char *param, int *len) +{ + + return (gctl_get_param_flags(req, param, GCTL_PARAM_RD, len)); +} + char const * gctl_get_asciiparam(struct gctl_req *req, const char *param) { - u_int i; char const *p; - struct gctl_req_arg *ap; + int len; - for (i = 0; i < req->narg; i++) { - ap = &req->arg[i]; - if (strcmp(param, ap->name)) - continue; - if (!(ap->flag & GCTL_PARAM_RD)) - continue; - p = ap->kvalue; - if (ap->len < 1) { - gctl_error(req, "No length argument (%s)", param); - return (NULL); - } - if (p[ap->len - 1] != '\0') { - gctl_error(req, "Unterminated argument (%s)", param); - return (NULL); - } - return (p); + p = gctl_get_param_flags(req, param, GCTL_PARAM_RD, &len); + if (p == NULL) + return (NULL); + if (len < 1) { + gctl_error(req, "Argument without length (%s)", param); + return (NULL); } - return (NULL); + if (p[len - 1] != '\0') { + gctl_error(req, "Unterminated argument (%s)", param); + return (NULL); + } + return (p); } void * gctl_get_paraml_opt(struct gctl_req *req, const char *param, int len) { int i; void *p; p = gctl_get_param(req, param, &i); if (i != len) { p = NULL; gctl_error(req, "Wrong length %s argument", param); } return (p); } void * gctl_get_paraml(struct gctl_req *req, const char *param, int len) { void *p; p = gctl_get_paraml_opt(req, param, len); if (p == NULL) gctl_error(req, "Missing %s argument", param); return (p); } struct g_class * gctl_get_class(struct gctl_req *req, char const *arg) { char const *p; struct g_class *cp; p = gctl_get_asciiparam(req, arg); if (p == NULL) { gctl_error(req, "Missing %s argument", arg); return (NULL); } LIST_FOREACH(cp, &g_classes, class) { if (!strcmp(p, cp->name)) return (cp); } gctl_error(req, "Class not found: \"%s\"", p); return (NULL); } struct g_geom * gctl_get_geom(struct gctl_req *req, struct g_class *mp, char const *arg) { char const *p; struct g_geom *gp; MPASS(mp != NULL); p = gctl_get_asciiparam(req, arg); if (p == NULL) { gctl_error(req, "Missing %s argument", arg); return (NULL); } LIST_FOREACH(gp, &mp->geom, geom) if (!strcmp(p, gp->name)) return (gp); gctl_error(req, "Geom not found: \"%s\"", p); return (NULL); } struct g_provider * gctl_get_provider(struct gctl_req *req, char const *arg) { char const *p; struct g_provider *pp; p = gctl_get_asciiparam(req, arg); if (p == NULL) { gctl_error(req, "Missing '%s' argument", arg); return (NULL); } pp = g_provider_by_name(p); if (pp != NULL) return (pp); gctl_error(req, "Provider not found: \"%s\"", p); return (NULL); } +static void +g_ctl_getxml(struct gctl_req *req, struct g_class *mp) +{ + const char *name; + char *buf; + struct sbuf *sb; + int len, i = 0, n = 0, *parents; + struct g_geom *gp, **gps; + struct g_consumer *cp; + + parents = gctl_get_paraml(req, "parents", sizeof(*parents)); + if (parents == NULL) + return; + name = gctl_get_asciiparam(req, "arg0"); + n = 0; + LIST_FOREACH(gp, &mp->geom, geom) { + if (name && strcmp(gp->name, name) != 0) + continue; + n++; + if (*parents) { + LIST_FOREACH(cp, &gp->consumer, consumer) + n++; + } + } + gps = g_malloc((n + 1) * sizeof(*gps), M_WAITOK); + i = 0; + LIST_FOREACH(gp, &mp->geom, geom) { + if (name && strcmp(gp->name, name) != 0) + continue; + gps[i++] = gp; + if (*parents) { + LIST_FOREACH(cp, &gp->consumer, consumer) { + if (cp->provider != NULL) + gps[i++] = cp->provider->geom; + } + } + } + KASSERT(i == n, ("different number of geoms found (%d != %d)", + i, n)); + gps[i] = 0; + + buf = gctl_get_param_flags(req, "output", GCTL_PARAM_WR, &len); + if (buf == NULL) { + gctl_error(req, "output parameter missing"); + g_free(gps); + return; + } + sb = sbuf_new(NULL, buf, len, SBUF_FIXEDLEN | SBUF_INCLUDENUL); + g_conf_specific(sb, gps); + gctl_set_param(req, "output", buf, 0); + if (sbuf_error(sb)) + gctl_error(req, "output buffer overflow"); + sbuf_delete(sb); + g_free(gps); +} + static void g_ctl_req(void *arg, int flag __unused) { struct g_class *mp; struct gctl_req *req; char const *verb; g_topology_assert(); req = arg; mp = gctl_get_class(req, "class"); if (mp == NULL) return; - if (mp->ctlreq == NULL) { - gctl_error(req, "Class takes no requests"); - return; - } verb = gctl_get_param(req, "verb", NULL); if (verb == NULL) { gctl_error(req, "Verb missing"); return; } - mp->ctlreq(req, mp, verb); + if (strcmp(verb, "getxml") == 0) { + g_ctl_getxml(req, mp); + } else if (mp->ctlreq == NULL) { + gctl_error(req, "Class takes no requests"); + } else { + mp->ctlreq(req, mp, verb); + } g_topology_assert(); } static int g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct gctl_req *req; int nerror; req = (void *)data; req->nerror = 0; /* It is an error if we cannot return an error text */ if (req->lerror < 2) return (EINVAL); if (!useracc(req->error, req->lerror, VM_PROT_WRITE)) return (EINVAL); req->serror = sbuf_new_auto(); /* Check the version */ if (req->version != GCTL_VERSION) { gctl_error(req, "kernel and libgeom version mismatch."); req->arg = NULL; } else { /* Get things on board */ gctl_copyin(req); if (g_debugflags & G_F_CTLDUMP) gctl_dump(req, "request"); if (!req->nerror) { g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL); if (g_debugflags & G_F_CTLDUMP) gctl_dump(req, "result"); gctl_copyout(req); } } if (sbuf_done(req->serror)) { copyout(sbuf_data(req->serror), req->error, imin(req->lerror, sbuf_len(req->serror) + 1)); } nerror = req->nerror; gctl_free(req); return (nerror); } static int g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { int error; switch(cmd) { case GEOM_CTL: error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td); break; default: error = ENOIOCTL; break; } return (error); } diff --git a/sys/geom/geom_dump.c b/sys/geom/geom_dump.c index 067ab43f7d05..58077147ff8d 100644 --- a/sys/geom/geom_dump.c +++ b/sys/geom/geom_dump.c @@ -1,330 +1,339 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2002 Poul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. + * Copyright (c) 2013-2022 Alexander Motin * * This software was developed for the FreeBSD Project by Poul-Henning Kamp * and NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include static void g_confdot_consumer(struct sbuf *sb, struct g_consumer *cp) { sbuf_printf(sb, "z%p [label=\"r%dw%de%d\"];\n", cp, cp->acr, cp->acw, cp->ace); if (cp->provider) sbuf_printf(sb, "z%p -> z%p;\n", cp, cp->provider); } static void g_confdot_provider(struct sbuf *sb, struct g_provider *pp) { sbuf_printf(sb, "z%p [shape=hexagon,label=\"%s\\nr%dw%de%d\\nerr#%d\\n" "sector=%u\\nstripe=%ju\"];\n", pp, pp->name, pp->acr, pp->acw, pp->ace, pp->error, pp->sectorsize, (uintmax_t)pp->stripesize); } static void g_confdot_geom(struct sbuf *sb, struct g_geom *gp) { struct g_consumer *cp; struct g_provider *pp; sbuf_printf(sb, "z%p [shape=box,label=\"%s\\n%s\\nr#%d\"];\n", gp, gp->class->name, gp->name, gp->rank); LIST_FOREACH(cp, &gp->consumer, consumer) { g_confdot_consumer(sb, cp); sbuf_printf(sb, "z%p -> z%p;\n", gp, cp); } LIST_FOREACH(pp, &gp->provider, provider) { g_confdot_provider(sb, pp); sbuf_printf(sb, "z%p -> z%p;\n", pp, gp); } } static void g_confdot_class(struct sbuf *sb, struct g_class *mp) { struct g_geom *gp; LIST_FOREACH(gp, &mp->geom, geom) g_confdot_geom(sb, gp); } void g_confdot(void *p, int flag ) { struct g_class *mp; struct sbuf *sb; KASSERT(flag != EV_CANCEL, ("g_confdot was cancelled")); sb = p; g_topology_assert(); sbuf_cat(sb, "digraph geom {\n"); LIST_FOREACH(mp, &g_classes, class) g_confdot_class(sb, mp); sbuf_cat(sb, "}\n"); sbuf_finish(sb); } static void g_conftxt_geom(struct sbuf *sb, struct g_geom *gp, int level) { struct g_provider *pp; struct g_consumer *cp; if (gp->flags & G_GEOM_WITHER) return; LIST_FOREACH(pp, &gp->provider, provider) { sbuf_printf(sb, "%d %s %s %ju %u", level, gp->class->name, pp->name, (uintmax_t)pp->mediasize, pp->sectorsize); if (gp->dumpconf != NULL) gp->dumpconf(sb, NULL, gp, NULL, pp); sbuf_cat(sb, "\n"); LIST_FOREACH(cp, &pp->consumers, consumers) g_conftxt_geom(sb, cp->geom, level + 1); } } static void g_conftxt_class(struct sbuf *sb, struct g_class *mp) { struct g_geom *gp; LIST_FOREACH(gp, &mp->geom, geom) g_conftxt_geom(sb, gp, 0); } void g_conftxt(void *p, int flag) { struct g_class *mp; struct sbuf *sb; KASSERT(flag != EV_CANCEL, ("g_conftxt was cancelled")); sb = p; g_topology_assert(); LIST_FOREACH(mp, &g_classes, class) { if (!strcmp(mp->name, G_DISK_CLASS_NAME) || !strcmp(mp->name, "MD")) g_conftxt_class(sb, mp); } sbuf_finish(sb); } void g_conf_cat_escaped(struct sbuf *sb, const char *buf) { const u_char *c; for (c = buf; *c != '\0'; c++) { if (*c == '&' || *c == '<' || *c == '>' || *c == '\'' || *c == '"' || *c > 0x7e) sbuf_printf(sb, "&#x%X;", *c); else if (*c == '\t' || *c == '\n' || *c == '\r' || *c > 0x1f) sbuf_putc(sb, *c); else sbuf_putc(sb, '?'); } } void g_conf_printf_escaped(struct sbuf *sb, const char *fmt, ...) { struct sbuf *s; va_list ap; s = sbuf_new_auto(); va_start(ap, fmt); sbuf_vprintf(s, fmt, ap); va_end(ap); sbuf_finish(s); g_conf_cat_escaped(sb, sbuf_data(s)); sbuf_delete(s); } static void g_conf_consumer(struct sbuf *sb, struct g_consumer *cp) { sbuf_printf(sb, "\t\n", cp); sbuf_printf(sb, "\t \n", cp->geom); if (cp->provider != NULL) sbuf_printf(sb, "\t \n", cp->provider); sbuf_printf(sb, "\t r%dw%de%d\n", cp->acr, cp->acw, cp->ace); if (cp->geom->flags & G_GEOM_WITHER) ; else if (cp->geom->dumpconf != NULL) { sbuf_cat(sb, "\t \n"); cp->geom->dumpconf(sb, "\t ", cp->geom, cp, NULL); sbuf_cat(sb, "\t \n"); } sbuf_cat(sb, "\t\n"); } static void g_conf_provider(struct sbuf *sb, struct g_provider *pp) { struct g_geom_alias *gap; sbuf_printf(sb, "\t\n", pp); sbuf_printf(sb, "\t \n", pp->geom); sbuf_printf(sb, "\t r%dw%de%d\n", pp->acr, pp->acw, pp->ace); sbuf_cat(sb, "\t "); g_conf_cat_escaped(sb, pp->name); sbuf_cat(sb, "\n"); LIST_FOREACH(gap, &pp->aliases, ga_next) { sbuf_cat(sb, "\t "); g_conf_cat_escaped(sb, gap->ga_alias); sbuf_cat(sb, "\n"); } sbuf_printf(sb, "\t %jd\n", (intmax_t)pp->mediasize); sbuf_printf(sb, "\t %u\n", pp->sectorsize); sbuf_printf(sb, "\t %ju\n", (uintmax_t)pp->stripesize); sbuf_printf(sb, "\t %ju\n", (uintmax_t)pp->stripeoffset); if (pp->flags & G_PF_WITHER) sbuf_cat(sb, "\t \n"); else if (pp->geom->flags & G_GEOM_WITHER) ; else if (pp->geom->dumpconf != NULL) { sbuf_cat(sb, "\t \n"); pp->geom->dumpconf(sb, "\t ", pp->geom, NULL, pp); sbuf_cat(sb, "\t \n"); } sbuf_cat(sb, "\t\n"); } static void -g_conf_geom(struct sbuf *sb, struct g_geom *gp, struct g_provider *pp, struct g_consumer *cp) +g_conf_geom(struct sbuf *sb, struct g_geom *gp) { - struct g_consumer *cp2; - struct g_provider *pp2; + struct g_consumer *cp; + struct g_provider *pp; sbuf_printf(sb, " \n", gp); sbuf_printf(sb, " \n", gp->class); sbuf_cat(sb, " "); g_conf_cat_escaped(sb, gp->name); sbuf_cat(sb, "\n"); sbuf_printf(sb, " %d\n", gp->rank); if (gp->flags & G_GEOM_WITHER) sbuf_cat(sb, " \n"); else if (gp->dumpconf != NULL) { sbuf_cat(sb, " \n"); gp->dumpconf(sb, "\t", gp, NULL, NULL); sbuf_cat(sb, " \n"); } - LIST_FOREACH(cp2, &gp->consumer, consumer) { - if (cp != NULL && cp != cp2) - continue; - g_conf_consumer(sb, cp2); - } + LIST_FOREACH(cp, &gp->consumer, consumer) + g_conf_consumer(sb, cp); + LIST_FOREACH(pp, &gp->provider, provider) + g_conf_provider(sb, pp); + sbuf_cat(sb, " \n"); +} - LIST_FOREACH(pp2, &gp->provider, provider) { - if (pp != NULL && pp != pp2) - continue; - g_conf_provider(sb, pp2); +static bool +g_conf_matchgp(struct g_geom *gp, struct g_geom **gps) +{ + + if (gps == NULL) + return (true); + for (; *gps != NULL; gps++) { + if (*gps == gp) + return (true); } - sbuf_cat(sb, " \n"); + return (false); } static void -g_conf_class(struct sbuf *sb, struct g_class *mp, struct g_geom *gp, struct g_provider *pp, struct g_consumer *cp) +g_conf_class(struct sbuf *sb, struct g_class *mp, struct g_geom **gps) { - struct g_geom *gp2; + struct g_geom *gp; sbuf_printf(sb, " \n", mp); sbuf_cat(sb, " "); g_conf_cat_escaped(sb, mp->name); sbuf_cat(sb, "\n"); - LIST_FOREACH(gp2, &mp->geom, geom) { - if (gp != NULL && gp != gp2) + LIST_FOREACH(gp, &mp->geom, geom) { + if (!g_conf_matchgp(gp, gps)) continue; - g_conf_geom(sb, gp2, pp, cp); + g_conf_geom(sb, gp); + if (sbuf_error(sb)) + break; } sbuf_cat(sb, " \n"); } void -g_conf_specific(struct sbuf *sb, struct g_class *mp, struct g_geom *gp, struct g_provider *pp, struct g_consumer *cp) +g_conf_specific(struct sbuf *sb, struct g_geom **gps) { struct g_class *mp2; g_topology_assert(); sbuf_cat(sb, "\n"); LIST_FOREACH(mp2, &g_classes, class) { - if (mp != NULL && mp != mp2) - continue; - g_conf_class(sb, mp2, gp, pp, cp); + g_conf_class(sb, mp2, gps); + if (sbuf_error(sb)) + break; } sbuf_cat(sb, "\n"); sbuf_finish(sb); } void g_confxml(void *p, int flag) { KASSERT(flag != EV_CANCEL, ("g_confxml was cancelled")); g_topology_assert(); - g_conf_specific(p, NULL, NULL, NULL, NULL); + g_conf_specific(p, NULL); } void (g_trace)(int level, const char *fmt, ...) { va_list ap; if (!(g_debugflags & level)) return; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); } diff --git a/sys/geom/geom_int.h b/sys/geom/geom_int.h index 67c46d715885..121793b18007 100644 --- a/sys/geom/geom_int.h +++ b/sys/geom/geom_int.h @@ -1,78 +1,78 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2002 Poul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Poul-Henning Kamp * and NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * 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. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * 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$ */ LIST_HEAD(class_list_head, g_class); TAILQ_HEAD(g_tailq_head, g_geom); extern int g_collectstats; #define G_STATS_PROVIDERS 1 /* Collect I/O stats for providers */ #define G_STATS_CONSUMERS 2 /* Collect I/O stats for consumers */ /* geom_dump.c */ void g_confxml(void *, int flag); -void g_conf_specific(struct sbuf *sb, struct g_class *mp, struct g_geom *gp, struct g_provider *pp, struct g_consumer *cp); +void g_conf_specific(struct sbuf *sb, struct g_geom **gps); void g_conf_cat_escaped(struct sbuf *sb, const char *buf); void g_conf_printf_escaped(struct sbuf *sb, const char *fmt, ...); void g_confdot(void *, int flag); void g_conftxt(void *, int flag); /* geom_event.c */ void g_event_init(void); void g_run_events(void); void g_do_wither(void); /* geom_subr.c */ extern struct class_list_head g_classes; extern char *g_wait_event, *g_wait_sim, *g_wait_up, *g_wait_down; void g_wither_washer(void); /* geom_io.c */ void g_io_init(void); void g_io_schedule_down(struct thread *tp); void g_io_schedule_up(struct thread *tp); /* geom_kern.c / geom_kernsim.c */ void g_init(void); extern struct thread *g_up_td; extern struct thread *g_down_td; extern int g_shutdown; extern int g_notaste; /* geom_ctl.c */ void g_ctl_init(void);