Index: stable/11/sbin/geom/class/cache/geom_cache.c =================================================================== --- stable/11/sbin/geom/class/cache/geom_cache.c (revision 330725) +++ stable/11/sbin/geom/class/cache/geom_cache.c (revision 330726) @@ -1,241 +1,242 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Ruslan Ermilov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "core/geom.h" #include "misc/subr.h" uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_CACHE_VERSION; #define GCACHE_BLOCKSIZE "65536" #define GCACHE_SIZE "100" static void cache_main(struct gctl_req *req, unsigned flags); static void cache_clear(struct gctl_req *req); static void cache_dump(struct gctl_req *req); static void cache_label(struct gctl_req *req); struct g_command class_commands[] = { { "clear", G_FLAG_VERBOSE, cache_main, G_NULL_OPTS, "[-v] prov ..." }, { "configure", G_FLAG_VERBOSE, NULL, { { 'b', "blocksize", "0", G_TYPE_NUMBER }, { 's', "size", "0", G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-v] [-b blocksize] [-s size] name" }, { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, { { 'b', "blocksize", GCACHE_BLOCKSIZE, G_TYPE_NUMBER }, { 's', "size", GCACHE_SIZE, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-v] [-b blocksize] [-s size] name prov" }, { "destroy", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-fv] name ..." }, { "dump", 0, cache_main, G_NULL_OPTS, "prov ..." }, { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, cache_main, { { 'b', "blocksize", GCACHE_BLOCKSIZE, G_TYPE_NUMBER }, { 's', "size", GCACHE_SIZE, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-v] [-b blocksize] [-s size] name prov" }, { "reset", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "[-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 cache_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) cache_label(req); else if (strcmp(name, "clear") == 0) cache_clear(req); else if (strcmp(name, "dump") == 0) cache_dump(req); else gctl_error(req, "Unknown command: %s.", name); } static void cache_label(struct gctl_req *req) { struct g_cache_metadata md; u_char sector[512]; const char *name; int error, nargs; intmax_t val; + bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); if (nargs != 2) { gctl_error(req, "Invalid number of arguments."); return; } strlcpy(md.md_magic, G_CACHE_MAGIC, sizeof(md.md_magic)); md.md_version = G_CACHE_VERSION; name = gctl_get_ascii(req, "arg0"); strlcpy(md.md_name, name, sizeof(md.md_name)); val = gctl_get_intmax(req, "blocksize"); md.md_bsize = val; val = gctl_get_intmax(req, "size"); md.md_size = val; name = gctl_get_ascii(req, "arg1"); md.md_provsize = g_get_mediasize(name); if (md.md_provsize == 0) { fprintf(stderr, "Can't get mediasize of %s: %s.\n", name, strerror(errno)); gctl_error(req, "Not fully done."); return; } cache_metadata_encode(&md, sector); error = g_metadata_store(name, sector, sizeof(sector)); if (error != 0) { fprintf(stderr, "Can't store metadata on %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); return; } if (verbose) printf("Metadata value stored on %s.\n", name); } static void cache_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_CACHE_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 cache_metadata_dump(const struct g_cache_metadata *md) { printf(" Magic string: %s\n", md->md_magic); printf(" Metadata version: %u\n", (u_int)md->md_version); printf(" Device name: %s\n", md->md_name); printf(" Block size: %u\n", (u_int)md->md_bsize); printf(" Cache size: %u\n", (u_int)md->md_size); printf(" Provider size: %ju\n", (uintmax_t)md->md_provsize); } static void cache_dump(struct gctl_req *req) { struct g_cache_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_CACHE_MAGIC); if (error != 0) { fprintf(stderr, "Can't read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } cache_metadata_decode((u_char *)&tmpmd, &md); printf("Metadata on %s:\n", name); cache_metadata_dump(&md); printf("\n"); } } Index: stable/11/sbin/geom/class/concat/geom_concat.c =================================================================== --- stable/11/sbin/geom/class/concat/geom_concat.c (revision 330725) +++ stable/11/sbin/geom/class/concat/geom_concat.c (revision 330726) @@ -1,249 +1,250 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2005 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 "core/geom.h" #include "misc/subr.h" uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_CONCAT_VERSION; static void concat_main(struct gctl_req *req, unsigned flags); static void concat_clear(struct gctl_req *req); static void concat_dump(struct gctl_req *req); static void concat_label(struct gctl_req *req); struct g_command class_commands[] = { { "clear", G_FLAG_VERBOSE, concat_main, G_NULL_OPTS, "[-v] prov ..." }, { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, 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, concat_main, G_NULL_OPTS, "prov ..." }, { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, concat_main, { { 'h', "hardcode", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-hv] name prov ..." }, { "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 concat_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) concat_label(req); else if (strcmp(name, "clear") == 0) concat_clear(req); else if (strcmp(name, "dump") == 0) concat_dump(req); else gctl_error(req, "Unknown command: %s.", name); } static void concat_label(struct gctl_req *req) { struct g_concat_metadata md; u_char sector[512]; const char *name; int error, i, hardcode, nargs; + bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); if (nargs < 2) { gctl_error(req, "Too few arguments."); return; } hardcode = gctl_get_int(req, "hardcode"); /* * Clear last sector first to spoil all components if device exists. */ for (i = 1; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); error = g_metadata_clear(name, NULL); if (error != 0) { gctl_error(req, "Can't store metadata on %s: %s.", name, strerror(error)); return; } } strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); md.md_version = G_CONCAT_VERSION; name = gctl_get_ascii(req, "arg0"); strlcpy(md.md_name, name, sizeof(md.md_name)); md.md_id = arc4random(); md.md_all = nargs - 1; /* * Ok, store metadata. */ for (i = 1; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); md.md_no = i - 1; if (!hardcode) bzero(md.md_provider, sizeof(md.md_provider)); else { if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) name += sizeof(_PATH_DEV) - 1; strlcpy(md.md_provider, name, sizeof(md.md_provider)); } md.md_provsize = g_get_mediasize(name); if (md.md_provsize == 0) { fprintf(stderr, "Can't get mediasize of %s: %s.\n", name, strerror(errno)); gctl_error(req, "Not fully done."); continue; } concat_metadata_encode(&md, sector); error = g_metadata_store(name, sector, sizeof(sector)); if (error != 0) { fprintf(stderr, "Can't store metadata on %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (verbose) printf("Metadata value stored on %s.\n", name); } } static void concat_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_CONCAT_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 concat_metadata_dump(const struct g_concat_metadata *md) { printf(" Magic string: %s\n", md->md_magic); printf(" Metadata version: %u\n", (u_int)md->md_version); printf(" Device name: %s\n", md->md_name); printf(" Device ID: %u\n", (u_int)md->md_id); printf(" Disk number: %u\n", (u_int)md->md_no); printf("Total number of disks: %u\n", (u_int)md->md_all); printf(" Hardcoded provider: %s\n", md->md_provider); } static void concat_dump(struct gctl_req *req) { struct g_concat_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_CONCAT_MAGIC); if (error != 0) { fprintf(stderr, "Can't read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } concat_metadata_decode((u_char *)&tmpmd, &md); printf("Metadata on %s:\n", name); concat_metadata_dump(&md); printf("\n"); } } Index: stable/11/sbin/geom/class/journal/geom_journal.c =================================================================== --- stable/11/sbin/geom/class/journal/geom_journal.c (revision 330725) +++ stable/11/sbin/geom/class/journal/geom_journal.c (revision 330726) @@ -1,350 +1,351 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005-2006 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 "geom_journal.h" uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_JOURNAL_VERSION; static void journal_main(struct gctl_req *req, unsigned flags); static void journal_clear(struct gctl_req *req); static void journal_dump(struct gctl_req *req); static void journal_label(struct gctl_req *req); struct g_command class_commands[] = { { "clear", G_FLAG_VERBOSE, journal_main, G_NULL_OPTS, "[-v] prov ..." }, { "dump", 0, journal_main, G_NULL_OPTS, "prov ..." }, { "label", G_FLAG_VERBOSE, journal_main, { { 'c', "checksum", NULL, G_TYPE_BOOL }, { 'f', "force", NULL, G_TYPE_BOOL }, { 'h', "hardcode", NULL, G_TYPE_BOOL }, { 's', "jsize", "-1", G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-cfhv] [-s jsize] dataprov [jprov]" }, { "stop", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-fv] name ..." }, { "sync", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "[-v]" }, G_CMD_SENTINEL }; static int verbose = 0; static void journal_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) journal_label(req); else if (strcmp(name, "clear") == 0) journal_clear(req); else if (strcmp(name, "dump") == 0) journal_dump(req); else gctl_error(req, "Unknown command: %s.", name); } static int g_journal_fs_exists(const char *prov) { if (g_journal_ufs_exists(prov)) return (1); #if 0 if (g_journal_otherfs_exists(prov)) return (1); #endif return (0); } static int g_journal_fs_using_last_sector(const char *prov) { if (g_journal_ufs_using_last_sector(prov)) return (1); #if 0 if (g_journal_otherfs_using_last_sector(prov)) return (1); #endif return (0); } static void journal_label(struct gctl_req *req) { struct g_journal_metadata md; const char *data, *journal, *str; u_char sector[512]; intmax_t jsize, msize, ssize; int error, force, i, nargs, checksum, hardcode; + bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); str = NULL; /* gcc */ strlcpy(md.md_magic, G_JOURNAL_MAGIC, sizeof(md.md_magic)); md.md_version = G_JOURNAL_VERSION; md.md_id = arc4random(); md.md_joffset = 0; md.md_jid = 0; md.md_flags = GJ_FLAG_CLEAN; checksum = gctl_get_int(req, "checksum"); if (checksum) md.md_flags |= GJ_FLAG_CHECKSUM; force = gctl_get_int(req, "force"); hardcode = gctl_get_int(req, "hardcode"); if (nargs != 1 && nargs != 2) { gctl_error(req, "Invalid number of arguments."); return; } /* Verify the given providers. */ for (i = 0; i < nargs; i++) { str = gctl_get_ascii(req, "arg%d", i); if (g_get_mediasize(str) == 0) { gctl_error(req, "Invalid provider %s.", str); return; } } data = gctl_get_ascii(req, "arg0"); jsize = gctl_get_intmax(req, "jsize"); journal = NULL; switch (nargs) { case 1: if (!force && g_journal_fs_exists(data)) { gctl_error(req, "File system exists on %s and this " "operation would destroy it.\nUse -f if you " "really want to do it.", data); return; } journal = data; msize = g_get_mediasize(data); ssize = g_get_sectorsize(data); if (jsize == -1) { /* * No journal size specified. 1GB should be safe * default. */ jsize = 1073741824ULL; } else { if (jsize < 104857600) { gctl_error(req, "Journal too small."); return; } if ((jsize % ssize) != 0) { gctl_error(req, "Invalid journal size."); return; } } if (jsize + ssize >= msize) { gctl_error(req, "Provider too small for journalling. " "You can try smaller jsize (default is %jd).", jsize); return; } md.md_jstart = msize - ssize - jsize; md.md_jend = msize - ssize; break; case 2: if (!force && g_journal_fs_using_last_sector(data)) { gctl_error(req, "File system on %s is using the last " "sector and this operation is going to overwrite " "it. Use -f if you really want to do it.", data); return; } journal = gctl_get_ascii(req, "arg1"); if (jsize != -1) { gctl_error(req, "jsize argument is valid only for " "all-in-one configuration."); return; } msize = g_get_mediasize(journal); ssize = g_get_sectorsize(journal); md.md_jstart = 0; md.md_jend = msize - ssize; break; } if (g_get_sectorsize(data) != g_get_sectorsize(journal)) { gctl_error(req, "Not equal sector sizes."); return; } /* * Clear last sector first, to spoil all components if device exists. */ for (i = 0; i < nargs; i++) { str = gctl_get_ascii(req, "arg%d", i); error = g_metadata_clear(str, NULL); if (error != 0) { gctl_error(req, "Cannot clear metadata on %s: %s.", str, strerror(error)); return; } } /* * Ok, store metadata. */ for (i = 0; i < nargs; i++) { switch (i) { case 0: str = data; md.md_type = GJ_TYPE_DATA; if (nargs == 1) md.md_type |= GJ_TYPE_JOURNAL; break; case 1: str = journal; md.md_type = GJ_TYPE_JOURNAL; break; } 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)); } journal_metadata_encode(&md, sector); error = g_metadata_store(str, sector, sizeof(sector)); if (error != 0) { fprintf(stderr, "Cannot 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 journal_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_JOURNAL_MAGIC); if (error != 0) { fprintf(stderr, "Cannot 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 journal_dump(struct gctl_req *req) { struct g_journal_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_JOURNAL_MAGIC); if (error != 0) { fprintf(stderr, "Cannot read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (journal_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); journal_metadata_dump(&md); printf("\n"); } } Index: stable/11/sbin/geom/class/label/geom_label.c =================================================================== --- stable/11/sbin/geom/class/label/geom_label.c (revision 330725) +++ stable/11/sbin/geom/class/label/geom_label.c (revision 330726) @@ -1,258 +1,260 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2005 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 "core/geom.h" #include "misc/subr.h" #ifdef STATIC_GEOM_CLASSES #define PUBSYM(x) glabel_##x #else #define PUBSYM(x) x #endif uint32_t PUBSYM(lib_version) = G_LIB_VERSION; uint32_t PUBSYM(version) = G_LABEL_VERSION; static void label_main(struct gctl_req *req, unsigned flags); static void label_clear(struct gctl_req *req); static void label_dump(struct gctl_req *req); static void label_label(struct gctl_req *req); static void label_refresh(struct gctl_req *req); struct g_command PUBSYM(class_commands)[] = { { "clear", G_FLAG_VERBOSE, label_main, G_NULL_OPTS, "[-v] dev ..." }, { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, G_NULL_OPTS, "[-v] name dev" }, { "destroy", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-fv] name ..." }, { "dump", 0, label_main, G_NULL_OPTS, "dev ..." }, { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, label_main, G_NULL_OPTS, "[-v] name dev" }, { "refresh", 0, label_main, G_NULL_OPTS, "dev ..." }, { "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 label_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) label_label(req); else if (strcmp(name, "clear") == 0) label_clear(req); else if (strcmp(name, "dump") == 0) label_dump(req); else if (strcmp(name, "refresh") == 0) label_refresh(req); else gctl_error(req, "Unknown command: %s.", name); } static void label_label(struct gctl_req *req) { struct g_label_metadata md; const char *name, *label; u_char sector[512]; int error, nargs; + bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); if (nargs != 2) { gctl_error(req, "Invalid number of arguments."); return; } /* * Clear last sector first to spoil all components if device exists. */ name = gctl_get_ascii(req, "arg1"); error = g_metadata_clear(name, NULL); if (error != 0) { gctl_error(req, "Can't store metadata on %s: %s.", name, strerror(error)); return; } strlcpy(md.md_magic, G_LABEL_MAGIC, sizeof(md.md_magic)); md.md_version = G_LABEL_VERSION; label = gctl_get_ascii(req, "arg0"); + bzero(md.md_label, sizeof(md.md_label)); strlcpy(md.md_label, label, sizeof(md.md_label)); md.md_provsize = g_get_mediasize(name); if (md.md_provsize == 0) { gctl_error(req, "Can't get mediasize of %s: %s.", name, strerror(errno)); return; } /* * Ok, store metadata. */ label_metadata_encode(&md, sector); error = g_metadata_store(name, sector, sizeof(sector)); if (error != 0) { fprintf(stderr, "Can't store metadata on %s: %s.\n", name, strerror(error)); gctl_error(req, "Not done."); } if (verbose) printf("Metadata value stored on %s.\n", name); } static void label_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_LABEL_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 label_metadata_dump(const struct g_label_metadata *md) { printf(" Magic string: %s\n", md->md_magic); printf("Metadata version: %u\n", (u_int)md->md_version); printf(" Label: %s\n", md->md_label); } static void label_dump(struct gctl_req *req) { struct g_label_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_LABEL_MAGIC); if (error != 0) { fprintf(stderr, "Can't read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } label_metadata_decode((u_char *)&tmpmd, &md); printf("Metadata on %s:\n", name); label_metadata_dump(&md); printf("\n"); } } static void label_refresh(struct gctl_req *req) { const char *name; int i, nargs, fd; 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); fd = g_open(name, 1); if (fd == -1) { printf("Can't refresh metadata from %s: %s.\n", name, strerror(errno)); } else { printf("Metadata from %s refreshed.\n", name); (void)g_close(fd); } } } Index: stable/11/sbin/geom/class/mirror/geom_mirror.c =================================================================== --- stable/11/sbin/geom/class/mirror/geom_mirror.c (revision 330725) +++ stable/11/sbin/geom/class/mirror/geom_mirror.c (revision 330726) @@ -1,499 +1,500 @@ /*- * 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; 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"); name = gctl_get_ascii(req, "class"); if (name == NULL) abort(); 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); if (gp == NULL) errx(EXIT_FAILURE, "No such geom: %s.", name); pp = LIST_FIRST(&gp->lg_provider); if (pp == NULL) errx(EXIT_FAILURE, "Provider of geom %s not found.", name); 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); } Index: stable/11/sbin/geom/class/raid3/geom_raid3.c =================================================================== --- stable/11/sbin/geom/class/raid3/geom_raid3.c (revision 330725) +++ stable/11/sbin/geom/class/raid3/geom_raid3.c (revision 330726) @@ -1,337 +1,338 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2005 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 uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_RAID3_VERSION; static void raid3_main(struct gctl_req *req, unsigned f); static void raid3_clear(struct gctl_req *req); static void raid3_dump(struct gctl_req *req); static void raid3_label(struct gctl_req *req); struct g_command class_commands[] = { { "clear", G_FLAG_VERBOSE, raid3_main, G_NULL_OPTS, "[-v] prov ..." }, { "configure", G_FLAG_VERBOSE, NULL, { { 'a', "autosync", NULL, G_TYPE_BOOL }, { '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 }, { 'r', "round_robin", NULL, G_TYPE_BOOL }, { 'R', "noround_robin", NULL, G_TYPE_BOOL }, { 'w', "verify", NULL, G_TYPE_BOOL }, { 'W', "noverify", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-adfFhnrRvwW] name" }, { "dump", 0, raid3_main, G_NULL_OPTS, "prov ..." }, { "insert", G_FLAG_VERBOSE, NULL, { { 'h', "hardcode", NULL, G_TYPE_BOOL }, { 'n', "number", G_VAL_OPTIONAL, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-hv] <-n number> name prov" }, { "label", G_FLAG_VERBOSE, raid3_main, { { 'h', "hardcode", NULL, G_TYPE_BOOL }, { 'F', "nofailsync", NULL, G_TYPE_BOOL }, { 'n', "noautosync", NULL, G_TYPE_BOOL }, { 'r', "round_robin", NULL, G_TYPE_BOOL }, { 's', "sectorsize", "0", G_TYPE_NUMBER }, { 'w', "verify", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-hFnrvw] [-s blocksize] name prov prov prov ..." }, { "rebuild", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "[-v] name prov" }, { "remove", G_FLAG_VERBOSE, NULL, { { 'n', "number", NULL, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-v] <-n number> 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 raid3_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) raid3_label(req); else if (strcmp(name, "clear") == 0) raid3_clear(req); else if (strcmp(name, "dump") == 0) raid3_dump(req); else gctl_error(req, "Unknown command: %s.", name); } static void raid3_label(struct gctl_req *req) { struct g_raid3_metadata md; u_char sector[512]; const char *str; unsigned sectorsize, ssize; off_t mediasize, msize; int hardcode, round_robin, verify; int error, i, nargs; + bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); if (nargs < 4) { gctl_error(req, "Too few arguments."); return; } if (bitcount32(nargs - 2) != 1) { gctl_error(req, "Invalid number of components."); return; } strlcpy(md.md_magic, G_RAID3_MAGIC, sizeof(md.md_magic)); md.md_version = G_RAID3_VERSION; str = gctl_get_ascii(req, "arg0"); strlcpy(md.md_name, str, sizeof(md.md_name)); md.md_id = 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; if (gctl_get_int(req, "noautosync")) md.md_mflags |= G_RAID3_DEVICE_FLAG_NOAUTOSYNC; if (gctl_get_int(req, "nofailsync")) md.md_mflags |= G_RAID3_DEVICE_FLAG_NOFAILSYNC; round_robin = gctl_get_int(req, "round_robin"); if (round_robin) md.md_mflags |= G_RAID3_DEVICE_FLAG_ROUND_ROBIN; verify = gctl_get_int(req, "verify"); if (verify) md.md_mflags |= G_RAID3_DEVICE_FLAG_VERIFY; if (round_robin && verify) { gctl_error(req, "Both '%c' and '%c' options given.", 'r', 'w'); return; } 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 = gctl_get_intmax(req, "sectorsize"); for (i = 1; i < nargs; i++) { 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 * (nargs - 2); md.md_sectorsize = sectorsize * (nargs - 2); md.md_mediasize -= (md.md_mediasize % md.md_sectorsize); if (md.md_sectorsize > MAXPHYS) { gctl_error(req, "The blocksize is too big."); return; } /* * 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); msize = g_get_mediasize(str); ssize = g_get_sectorsize(str); if (mediasize < msize - ssize) { fprintf(stderr, "warning: %s: only %jd bytes from %jd bytes used.\n", str, (intmax_t)mediasize, (intmax_t)(msize - ssize)); } md.md_no = i - 1; md.md_provsize = msize; 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)); } if (verify && md.md_no == md.md_all - 1) { /* * In "verify" mode, force synchronization of parity * component on start. */ md.md_syncid = 0; } raid3_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 raid3_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_RAID3_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 raid3_dump(struct gctl_req *req) { struct g_raid3_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_RAID3_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 (raid3_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); raid3_metadata_dump(&md); printf("\n"); } } Index: stable/11/sbin/geom/class/shsec/geom_shsec.c =================================================================== --- stable/11/sbin/geom/class/shsec/geom_shsec.c (revision 330725) +++ stable/11/sbin/geom/class/shsec/geom_shsec.c (revision 330726) @@ -1,261 +1,262 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2005 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 "core/geom.h" #include "misc/subr.h" uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_SHSEC_VERSION; static void shsec_main(struct gctl_req *req, unsigned flags); static void shsec_clear(struct gctl_req *req); static void shsec_dump(struct gctl_req *req); static void shsec_label(struct gctl_req *req); struct g_command class_commands[] = { { "clear", G_FLAG_VERBOSE, shsec_main, G_NULL_OPTS, "[-v] prov ..." }, { "dump", 0, shsec_main, G_NULL_OPTS, "prov ..." }, { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, shsec_main, { { 'h', "hardcode", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-hv] name prov prov ..." }, { "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 shsec_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) shsec_label(req); else if (strcmp(name, "clear") == 0) shsec_clear(req); else if (strcmp(name, "dump") == 0) shsec_dump(req); else gctl_error(req, "Unknown command: %s.", name); } static void shsec_label(struct gctl_req *req) { struct g_shsec_metadata md; off_t compsize, msize; u_char sector[512]; unsigned ssize, secsize; const char *name; int error, i, nargs, hardcode; + bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); if (nargs <= 2) { gctl_error(req, "Too few arguments."); return; } hardcode = gctl_get_int(req, "hardcode"); /* * Clear last sector first to spoil all components if device exists. */ compsize = 0; secsize = 0; for (i = 1; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); msize = g_get_mediasize(name); ssize = g_get_sectorsize(name); if (msize == 0 || ssize == 0) { gctl_error(req, "Can't get informations about %s: %s.", name, strerror(errno)); return; } msize -= ssize; if (compsize == 0 || (compsize > 0 && msize < compsize)) compsize = msize; if (secsize == 0) secsize = ssize; else secsize = g_lcm(secsize, ssize); error = g_metadata_clear(name, NULL); if (error != 0) { gctl_error(req, "Can't store metadata on %s: %s.", name, strerror(error)); return; } } strlcpy(md.md_magic, G_SHSEC_MAGIC, sizeof(md.md_magic)); md.md_version = G_SHSEC_VERSION; name = gctl_get_ascii(req, "arg0"); strlcpy(md.md_name, name, sizeof(md.md_name)); md.md_id = arc4random(); md.md_all = nargs - 1; /* * Ok, store metadata. */ for (i = 1; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); msize = g_get_mediasize(name); ssize = g_get_sectorsize(name); if (compsize < msize - ssize) { fprintf(stderr, "warning: %s: only %jd bytes from %jd bytes used.\n", name, (intmax_t)compsize, (intmax_t)(msize - ssize)); } md.md_no = i - 1; md.md_provsize = msize; if (!hardcode) bzero(md.md_provider, sizeof(md.md_provider)); else { if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) name += sizeof(_PATH_DEV) - 1; strlcpy(md.md_provider, name, sizeof(md.md_provider)); } shsec_metadata_encode(&md, sector); error = g_metadata_store(name, sector, sizeof(sector)); if (error != 0) { fprintf(stderr, "Can't store metadata on %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (verbose) printf("Metadata value stored on %s.\n", name); } } static void shsec_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_SHSEC_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 shsec_metadata_dump(const struct g_shsec_metadata *md) { printf(" Magic string: %s\n", md->md_magic); printf(" Metadata version: %u\n", (u_int)md->md_version); printf(" Device name: %s\n", md->md_name); printf(" Device ID: %u\n", (u_int)md->md_id); printf(" Disk number: %u\n", (u_int)md->md_no); printf("Total number of disks: %u\n", (u_int)md->md_all); printf(" Hardcoded provider: %s\n", md->md_provider); } static void shsec_dump(struct gctl_req *req) { struct g_shsec_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_SHSEC_MAGIC); if (error != 0) { fprintf(stderr, "Can't read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } shsec_metadata_decode((u_char *)&tmpmd, &md); printf("Metadata on %s:\n", name); shsec_metadata_dump(&md); printf("\n"); } } Index: stable/11/sbin/geom/class/stripe/geom_stripe.c =================================================================== --- stable/11/sbin/geom/class/stripe/geom_stripe.c (revision 330725) +++ stable/11/sbin/geom/class/stripe/geom_stripe.c (revision 330726) @@ -1,287 +1,288 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2005 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 "core/geom.h" #include "misc/subr.h" uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_STRIPE_VERSION; #define GSTRIPE_STRIPESIZE "65536" static void stripe_main(struct gctl_req *req, unsigned flags); static void stripe_clear(struct gctl_req *req); static void stripe_dump(struct gctl_req *req); static void stripe_label(struct gctl_req *req); struct g_command class_commands[] = { { "clear", G_FLAG_VERBOSE, stripe_main, G_NULL_OPTS, "[-v] prov ..." }, { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, { { 's', "stripesize", GSTRIPE_STRIPESIZE, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-v] [-s stripesize] name prov prov ..." }, { "destroy", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, "[-fv] name ..." }, { "dump", 0, stripe_main, G_NULL_OPTS, "prov ..." }, { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, stripe_main, { { 'h', "hardcode", NULL, G_TYPE_BOOL }, { 's', "stripesize", GSTRIPE_STRIPESIZE, G_TYPE_NUMBER }, G_OPT_SENTINEL }, "[-hv] [-s stripesize] name prov prov ..." }, { "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 stripe_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) stripe_label(req); else if (strcmp(name, "clear") == 0) stripe_clear(req); else if (strcmp(name, "dump") == 0) stripe_dump(req); else gctl_error(req, "Unknown command: %s.", name); } static void stripe_label(struct gctl_req *req) { struct g_stripe_metadata md; intmax_t stripesize; off_t compsize, msize; u_char sector[512]; unsigned ssize, secsize; const char *name; int error, i, nargs, hardcode; + bzero(sector, sizeof(sector)); nargs = gctl_get_int(req, "nargs"); if (nargs < 3) { gctl_error(req, "Too few arguments."); return; } hardcode = gctl_get_int(req, "hardcode"); /* * Clear last sector first to spoil all components if device exists. */ compsize = 0; secsize = 0; for (i = 1; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); msize = g_get_mediasize(name); ssize = g_get_sectorsize(name); if (msize == 0 || ssize == 0) { gctl_error(req, "Can't get informations about %s: %s.", name, strerror(errno)); return; } msize -= ssize; if (compsize == 0 || (compsize > 0 && msize < compsize)) compsize = msize; if (secsize == 0) secsize = ssize; else secsize = g_lcm(secsize, ssize); error = g_metadata_clear(name, NULL); if (error != 0) { gctl_error(req, "Can't store metadata on %s: %s.", name, strerror(error)); return; } } strlcpy(md.md_magic, G_STRIPE_MAGIC, sizeof(md.md_magic)); md.md_version = G_STRIPE_VERSION; name = gctl_get_ascii(req, "arg0"); strlcpy(md.md_name, name, sizeof(md.md_name)); md.md_id = arc4random(); md.md_all = nargs - 1; stripesize = gctl_get_intmax(req, "stripesize"); if ((stripesize % secsize) != 0) { gctl_error(req, "Stripesize should be multiple of %u.", secsize); return; } md.md_stripesize = stripesize; /* * Ok, store metadata. */ for (i = 1; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); msize = g_get_mediasize(name); ssize = g_get_sectorsize(name); if (compsize < msize - ssize) { fprintf(stderr, "warning: %s: only %jd bytes from %jd bytes used.\n", name, (intmax_t)compsize, (intmax_t)(msize - ssize)); } md.md_no = i - 1; md.md_provsize = msize; if (!hardcode) bzero(md.md_provider, sizeof(md.md_provider)); else { if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) name += sizeof(_PATH_DEV) - 1; strlcpy(md.md_provider, name, sizeof(md.md_provider)); } stripe_metadata_encode(&md, sector); error = g_metadata_store(name, sector, sizeof(sector)); if (error != 0) { fprintf(stderr, "Can't store metadata on %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } if (verbose) printf("Metadata value stored on %s.\n", name); } } static void stripe_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_STRIPE_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 stripe_metadata_dump(const struct g_stripe_metadata *md) { printf(" Magic string: %s\n", md->md_magic); printf(" Metadata version: %u\n", (u_int)md->md_version); printf(" Device name: %s\n", md->md_name); printf(" Device ID: %u\n", (u_int)md->md_id); printf(" Disk number: %u\n", (u_int)md->md_no); printf("Total number of disks: %u\n", (u_int)md->md_all); printf(" Stripe size: %u\n", (u_int)md->md_stripesize); printf(" Hardcoded provider: %s\n", md->md_provider); } static void stripe_dump(struct gctl_req *req) { struct g_stripe_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_STRIPE_MAGIC); if (error != 0) { fprintf(stderr, "Can't read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done."); continue; } stripe_metadata_decode((u_char *)&tmpmd, &md); printf("Metadata on %s:\n", name); stripe_metadata_dump(&md); printf("\n"); } } Index: stable/11/sbin/geom/class/virstor/geom_virstor.c =================================================================== --- stable/11/sbin/geom/class/virstor/geom_virstor.c (revision 330725) +++ stable/11/sbin/geom/class/virstor/geom_virstor.c (revision 330726) @@ -1,585 +1,586 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 Ivan Voras * * 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 uint32_t lib_version = G_LIB_VERSION; uint32_t version = G_VIRSTOR_VERSION; #define GVIRSTOR_CHUNK_SIZE "4M" #define GVIRSTOR_VIR_SIZE "2T" #if G_LIB_VERSION == 1 /* Support RELENG_6 */ #define G_TYPE_BOOL G_TYPE_NONE #endif /* * virstor_main gets called by the geom(8) utility */ static void virstor_main(struct gctl_req *req, unsigned flags); struct g_command class_commands[] = { { "clear", G_FLAG_VERBOSE, virstor_main, G_NULL_OPTS, "[-v] prov ..." }, { "dump", 0, virstor_main, G_NULL_OPTS, "prov ..." }, { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, virstor_main, { { 'h', "hardcode", NULL, G_TYPE_BOOL}, { 'm', "chunk_size", GVIRSTOR_CHUNK_SIZE, G_TYPE_NUMBER}, { 's', "vir_size", GVIRSTOR_VIR_SIZE, G_TYPE_NUMBER}, G_OPT_SENTINEL }, "[-h] [-v] [-m chunk_size] [-s vir_size] name provider0 [provider1 ...]" }, { "destroy", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL}, G_OPT_SENTINEL }, "[-fv] name ..." }, { "stop", G_FLAG_VERBOSE, NULL, { { 'f', "force", NULL, G_TYPE_BOOL}, G_OPT_SENTINEL }, "[-fv] name ... (alias for \"destroy\")" }, { "add", G_FLAG_VERBOSE, NULL, { { 'h', "hardcode", NULL, G_TYPE_BOOL}, G_OPT_SENTINEL }, "[-vh] name prov [prov ...]" }, { "remove", G_FLAG_VERBOSE, NULL, G_NULL_OPTS, "[-v] name ..." }, G_CMD_SENTINEL }; static int verbose = 0; /* Helper functions' declarations */ static void virstor_clear(struct gctl_req *req); static void virstor_dump(struct gctl_req *req); static void virstor_label(struct gctl_req *req); /* Dispatcher function (no real work done here, only verbose flag recorder) */ static void virstor_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) virstor_label(req); else if (strcmp(name, "clear") == 0) virstor_clear(req); else if (strcmp(name, "dump") == 0) virstor_dump(req); else gctl_error(req, "%s: Unknown command: %s.", __func__, name); /* No CTASSERT in userland CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS); */ } static void pathgen(const char *name, char *path, size_t size) { if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0) snprintf(path, size, "%s%s", _PATH_DEV, name); else strlcpy(path, name, size); } static int my_g_metadata_store(const char *name, u_char *md, size_t size) { char path[MAXPATHLEN]; unsigned sectorsize; off_t mediasize; u_char *sector; int error, fd; pathgen(name, path, sizeof(path)); sector = NULL; error = 0; fd = open(path, O_RDWR); if (fd == -1) return (errno); mediasize = g_get_mediasize(name); if (mediasize == 0) { error = errno; goto out; } sectorsize = g_get_sectorsize(name); if (sectorsize == 0) { error = errno; goto out; } assert(sectorsize >= size); sector = malloc(sectorsize); if (sector == NULL) { error = ENOMEM; goto out; } bcopy(md, sector, size); + bzero(sector + size, sectorsize - size); if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != (ssize_t)sectorsize) { error = errno; goto out; } out: if (sector != NULL) free(sector); close(fd); return (error); } /* * Labels a new geom Meaning: parses and checks the parameters, calculates & * writes metadata to the relevant providers so when the next round of * "tasting" comes (which will be just after the provider(s) are closed) geom * can be instantiated with the tasted metadata. */ static void virstor_label(struct gctl_req *req) { struct g_virstor_metadata md; off_t msize; unsigned char *sect; unsigned int i; size_t ssize, secsize; const char *name; char param[32]; int hardcode, nargs, error; struct virstor_map_entry *map; size_t total_chunks; /* We'll run out of memory if this needs to be bigger. */ unsigned int map_chunks; /* Chunks needed by the map (map size). */ size_t map_size; /* In bytes. */ ssize_t written; int fd; nargs = gctl_get_int(req, "nargs"); if (nargs < 2) { gctl_error(req, "Too few arguments (%d): expecting: name " "provider0 [provider1 ...]", nargs); return; } hardcode = gctl_get_int(req, "hardcode"); /* * Initialize constant parts of metadata: magic signature, version, * name. */ bzero(&md, sizeof(md)); strlcpy(md.md_magic, G_VIRSTOR_MAGIC, sizeof(md.md_magic)); md.md_version = G_VIRSTOR_VERSION; name = gctl_get_ascii(req, "arg0"); if (name == NULL) { gctl_error(req, "No 'arg%u' argument.", 0); return; } strlcpy(md.md_name, name, sizeof(md.md_name)); md.md_virsize = (off_t)gctl_get_intmax(req, "vir_size"); md.md_chunk_size = gctl_get_intmax(req, "chunk_size"); md.md_count = nargs - 1; if (md.md_virsize == 0 || md.md_chunk_size == 0) { gctl_error(req, "Virtual size and chunk size must be non-zero"); return; } if (md.md_chunk_size % MAXPHYS != 0) { /* XXX: This is not strictly needed, but it's convenient to * impose some limitations on it, so why not MAXPHYS. */ size_t new_size = rounddown(md.md_chunk_size, MAXPHYS); if (new_size < md.md_chunk_size) new_size += MAXPHYS; fprintf(stderr, "Resizing chunk size to be a multiple of " "MAXPHYS (%d kB).\n", MAXPHYS / 1024); fprintf(stderr, "New chunk size: %zu kB\n", new_size / 1024); md.md_chunk_size = new_size; } if (md.md_virsize % md.md_chunk_size != 0) { off_t chunk_count = md.md_virsize / md.md_chunk_size; md.md_virsize = chunk_count * md.md_chunk_size; fprintf(stderr, "Resizing virtual size to be a multiple of " "chunk size.\n"); fprintf(stderr, "New virtual size: %zu MB\n", (size_t)(md.md_virsize/(1024 * 1024))); } msize = secsize = 0; for (i = 1; i < (unsigned)nargs; i++) { snprintf(param, sizeof(param), "arg%u", i); name = gctl_get_ascii(req, "%s", param); ssize = g_get_sectorsize(name); if (ssize == 0) fprintf(stderr, "%s for %s\n", strerror(errno), name); msize += g_get_mediasize(name); if (secsize == 0) secsize = ssize; else if (secsize != ssize) { gctl_error(req, "Devices need to have same sector size " "(%u on %s needs to be %u).", (u_int)ssize, name, (u_int)secsize); return; } } if (secsize == 0) { gctl_error(req, "Device not specified"); return; } if (md.md_chunk_size % secsize != 0) { fprintf(stderr, "Error: chunk size is not a multiple of sector " "size."); gctl_error(req, "Chunk size (in bytes) must be multiple of %u.", (unsigned int)secsize); return; } total_chunks = md.md_virsize / md.md_chunk_size; map_size = total_chunks * sizeof(*map); assert(md.md_virsize % md.md_chunk_size == 0); ssize = map_size % secsize; if (ssize != 0) { size_t add_chunks = (secsize - ssize) / sizeof(*map); total_chunks += add_chunks; md.md_virsize = (off_t)total_chunks * (off_t)md.md_chunk_size; map_size = total_chunks * sizeof(*map); fprintf(stderr, "Resizing virtual size to fit virstor " "structures.\n"); fprintf(stderr, "New virtual size: %ju MB (%zu new chunks)\n", (uintmax_t)(md.md_virsize / (1024 * 1024)), add_chunks); } if (verbose) printf("Total virtual chunks: %zu (%zu MB each), %ju MB total " "virtual size.\n", total_chunks, (size_t)(md.md_chunk_size / (1024 * 1024)), md.md_virsize/(1024 * 1024)); if ((off_t)md.md_virsize < msize) fprintf(stderr, "WARNING: Virtual storage size < Physical " "available storage (%ju < %ju)\n", md.md_virsize, msize); /* Clear last sector first to spoil all components if device exists. */ if (verbose) printf("Clearing metadata on"); for (i = 1; i < (unsigned)nargs; i++) { snprintf(param, sizeof(param), "arg%u", i); name = gctl_get_ascii(req, "%s", param); if (verbose) printf(" %s", name); msize = g_get_mediasize(name); ssize = g_get_sectorsize(name); if (msize == 0 || ssize == 0) { gctl_error(req, "Can't retrieve information about " "%s: %s.", name, strerror(errno)); return; } if (msize < (off_t) MAX(md.md_chunk_size*4, map_size)) gctl_error(req, "Device %s is too small", name); error = g_metadata_clear(name, NULL); if (error != 0) { gctl_error(req, "Can't clear metadata on %s: %s.", name, strerror(error)); return; } } /* Write allocation table to the first provider - this needs to be done * before metadata is written because when kernel tastes it it's too * late */ name = gctl_get_ascii(req, "arg1"); /* device with metadata */ if (verbose) printf(".\nWriting allocation table to %s...", name); /* How many chunks does the map occupy? */ map_chunks = map_size/md.md_chunk_size; if (map_size % md.md_chunk_size != 0) map_chunks++; if (verbose) { printf(" (%zu MB, %d chunks) ", map_size/(1024*1024), map_chunks); fflush(stdout); } if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) fd = open(name, O_RDWR); else { sprintf(param, "%s%s", _PATH_DEV, name); fd = open(param, O_RDWR); } if (fd < 0) gctl_error(req, "Cannot open provider %s to write map", name); /* Do it with calloc because there might be a need to set up chunk flags * in the future */ map = calloc(total_chunks, sizeof(*map)); if (map == NULL) { gctl_error(req, "Out of memory (need %zu bytes for allocation map)", map_size); } written = pwrite(fd, map, map_size, 0); free(map); if ((size_t)written != map_size) { if (verbose) { fprintf(stderr, "\nTried to write %zu, written %zd (%s)\n", map_size, written, strerror(errno)); } gctl_error(req, "Error writing out allocation map!"); return; } close (fd); if (verbose) printf("\nStoring metadata on "); /* * ID is randomly generated, unique for a geom. This is used to * recognize all providers belonging to one geom. */ md.md_id = arc4random(); /* Ok, store metadata. */ for (i = 1; i < (unsigned)nargs; i++) { snprintf(param, sizeof(param), "arg%u", i); name = gctl_get_ascii(req, "%s", param); msize = g_get_mediasize(name); ssize = g_get_sectorsize(name); if (verbose) printf("%s ", name); /* this provider's position/type in geom */ md.no = i - 1; /* this provider's size */ md.provsize = msize; /* chunk allocation info */ md.chunk_count = md.provsize / md.md_chunk_size; if (verbose) printf("(%u chunks) ", md.chunk_count); /* Check to make sure last sector is unused */ if ((off_t)(md.chunk_count * md.md_chunk_size) > (off_t)(msize-ssize)) md.chunk_count--; md.chunk_next = 0; if (i != 1) { md.chunk_reserved = 0; md.flags = 0; } else { md.chunk_reserved = map_chunks * 2; md.flags = VIRSTOR_PROVIDER_ALLOCATED | VIRSTOR_PROVIDER_CURRENT; md.chunk_next = md.chunk_reserved; if (verbose) printf("(%u reserved) ", md.chunk_reserved); } if (!hardcode) bzero(md.provider, sizeof(md.provider)); else { /* convert "/dev/something" to "something" */ if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) { strlcpy(md.provider, name + sizeof(_PATH_DEV) - 1, sizeof(md.provider)); } else strlcpy(md.provider, name, sizeof(md.provider)); } sect = malloc(ssize); if (sect == NULL) err(1, "Cannot allocate sector of %zu bytes", ssize); bzero(sect, ssize); virstor_metadata_encode(&md, sect); error = my_g_metadata_store(name, sect, ssize); free(sect); if (error != 0) { if (verbose) printf("\n"); fprintf(stderr, "Can't store metadata on %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done (error storing metadata)."); return; } } #if 0 if (verbose) printf("\n"); #endif } /* Clears metadata on given provider(s) IF it's owned by us */ static void virstor_clear(struct gctl_req *req) { const char *name; char param[32]; unsigned i; int nargs, error; int fd; nargs = gctl_get_int(req, "nargs"); if (nargs < 1) { gctl_error(req, "Too few arguments."); return; } for (i = 0; i < (unsigned)nargs; i++) { snprintf(param, sizeof(param), "arg%u", i); name = gctl_get_ascii(req, "%s", param); error = g_metadata_clear(name, G_VIRSTOR_MAGIC); if (error != 0) { fprintf(stderr, "Can't clear metadata on %s: %s " "(do I own it?)\n", name, strerror(error)); gctl_error(req, "Not fully done (can't clear metadata)."); continue; } if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) fd = open(name, O_RDWR); else { sprintf(param, "%s%s", _PATH_DEV, name); fd = open(param, O_RDWR); } if (fd < 0) { gctl_error(req, "Cannot clear header sector for %s", name); continue; } if (verbose) printf("Metadata cleared on %s.\n", name); } } /* Print some metadata information */ static void virstor_metadata_dump(const struct g_virstor_metadata *md) { printf(" Magic string: %s\n", md->md_magic); printf(" Metadata version: %u\n", (u_int) md->md_version); printf(" Device name: %s\n", md->md_name); printf(" Device ID: %u\n", (u_int) md->md_id); printf(" Provider index: %u\n", (u_int) md->no); printf(" Active providers: %u\n", (u_int) md->md_count); printf(" Hardcoded provider: %s\n", md->provider[0] != '\0' ? md->provider : "(not hardcoded)"); printf(" Virtual size: %u MB\n", (unsigned int)(md->md_virsize/(1024 * 1024))); printf(" Chunk size: %u kB\n", md->md_chunk_size / 1024); printf(" Chunks on provider: %u\n", md->chunk_count); printf(" Chunks free: %u\n", md->chunk_count - md->chunk_next); printf(" Reserved chunks: %u\n", md->chunk_reserved); } /* Called by geom(8) via gvirstor_main() to dump metadata information */ static void virstor_dump(struct gctl_req *req) { struct g_virstor_metadata md; u_char tmpmd[512]; /* temporary buffer */ const char *name; char param[16]; int nargs, error, i; assert(sizeof(tmpmd) >= sizeof(md)); nargs = gctl_get_int(req, "nargs"); if (nargs < 1) { gctl_error(req, "Too few arguments."); return; } for (i = 0; i < nargs; i++) { snprintf(param, sizeof(param), "arg%u", i); name = gctl_get_ascii(req, "%s", param); error = g_metadata_read(name, (u_char *) & tmpmd, sizeof(tmpmd), G_VIRSTOR_MAGIC); if (error != 0) { fprintf(stderr, "Can't read metadata from %s: %s.\n", name, strerror(error)); gctl_error(req, "Not fully done (error reading metadata)."); continue; } virstor_metadata_decode((u_char *) & tmpmd, &md); printf("Metadata on %s:\n", name); virstor_metadata_dump(&md); printf("\n"); } } Index: stable/11/sbin/geom/misc/subr.c =================================================================== --- stable/11/sbin/geom/misc/subr.c (revision 330725) +++ stable/11/sbin/geom/misc/subr.c (revision 330726) @@ -1,538 +1,546 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004-2010 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 "misc/subr.h" struct std_metadata { char md_magic[16]; uint32_t md_version; }; static void std_metadata_decode(const unsigned char *data, struct std_metadata *md) { bcopy(data, md->md_magic, sizeof(md->md_magic)); md->md_version = le32dec(data + 16); } /* * Greatest Common Divisor. */ static unsigned int gcd(unsigned int a, unsigned int b) { unsigned int c; while (b != 0) { c = a; a = b; b = (c % b); } return (a); } /* * Least Common Multiple. */ unsigned int g_lcm(unsigned int a, unsigned int b) { return ((a * b) / gcd(a, b)); } uint32_t bitcount32(uint32_t x) { x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1); x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2); x = (x & 0x0f0f0f0f) + ((x & 0xf0f0f0f0) >> 4); x = (x & 0x00ff00ff) + ((x & 0xff00ff00) >> 8); x = (x & 0x0000ffff) + ((x & 0xffff0000) >> 16); return (x); } /* * The size of a sector is context specific (i.e. determined by the * media). But when users enter a value with a SI unit, they really * mean the byte-size or byte-offset and not the size or offset in * sectors. We should map the byte-oriented value into a sector-oriented * value when we already know the sector size in bytes. At this time * we can use g_parse_lba() function. It converts user specified * value into sectors with following conditions: * o Sectors size taken as argument from caller. * o When no SI unit is specified the value is in sectors. * o With an SI unit the value is in bytes. * o The 'b' suffix forces byte interpretation and the 's' * suffix forces sector interpretation. * * Thus: * o 2 and 2s mean 2 sectors, and 2b means 2 bytes. * o 4k and 4kb mean 4096 bytes, and 4ks means 4096 sectors. * */ int g_parse_lba(const char *lbastr, unsigned int sectorsize, off_t *sectors) { off_t number, mult, unit; char *s; assert(lbastr != NULL); assert(sectorsize > 0); assert(sectors != NULL); number = (off_t)strtoimax(lbastr, &s, 0); if (s == lbastr || number < 0) return (EINVAL); mult = 1; unit = sectorsize; if (*s == '\0') goto done; switch (*s) { case 'e': case 'E': mult *= 1024; /* FALLTHROUGH */ case 'p': case 'P': mult *= 1024; /* FALLTHROUGH */ case 't': case 'T': mult *= 1024; /* FALLTHROUGH */ case 'g': case 'G': mult *= 1024; /* FALLTHROUGH */ case 'm': case 'M': mult *= 1024; /* FALLTHROUGH */ case 'k': case 'K': mult *= 1024; break; default: goto sfx; } unit = 1; /* bytes */ s++; if (*s == '\0') goto done; sfx: switch (*s) { case 's': case 'S': unit = sectorsize; /* sector */ break; case 'b': case 'B': unit = 1; /* bytes */ break; default: return (EINVAL); } s++; if (*s != '\0') return (EINVAL); done: if ((OFF_MAX / unit) < mult || (OFF_MAX / mult / unit) < number) return (ERANGE); number *= mult * unit; if (number % sectorsize) return (EINVAL); number /= sectorsize; *sectors = number; return (0); } off_t g_get_mediasize(const char *name) { off_t mediasize; int fd; fd = g_open(name, 0); if (fd == -1) return (0); mediasize = g_mediasize(fd); if (mediasize == -1) mediasize = 0; (void)g_close(fd); return (mediasize); } unsigned int g_get_sectorsize(const char *name) { ssize_t sectorsize; int fd; fd = g_open(name, 0); if (fd == -1) return (0); sectorsize = g_sectorsize(fd); if (sectorsize == -1) sectorsize = 0; (void)g_close(fd); return ((unsigned int)sectorsize); } int g_metadata_read(const char *name, unsigned char *md, size_t size, const char *magic) { struct std_metadata stdmd; unsigned char *sector; ssize_t sectorsize; off_t mediasize; int error, fd; sector = NULL; error = 0; fd = g_open(name, 0); if (fd == -1) return (errno); mediasize = g_mediasize(fd); if (mediasize == -1) { error = errno; goto out; } sectorsize = g_sectorsize(fd); if (sectorsize == -1) { error = errno; goto out; } assert(sectorsize >= (ssize_t)size); sector = malloc(sectorsize); if (sector == NULL) { error = ENOMEM; goto out; } if (pread(fd, sector, sectorsize, mediasize - sectorsize) != sectorsize) { error = errno; goto out; } if (magic != NULL) { std_metadata_decode(sector, &stdmd); if (strcmp(stdmd.md_magic, magic) != 0) { error = EINVAL; goto out; } } bcopy(sector, md, size); out: if (sector != NULL) free(sector); g_close(fd); return (error); } +/* + * Actually write the GEOM label to the provider + * + * @param name GEOM provider's name (ie "ada0") + * @param md Pointer to the label data to write + * @param size Size of the data pointed to by md + */ int g_metadata_store(const char *name, const unsigned char *md, size_t size) { unsigned char *sector; ssize_t sectorsize; off_t mediasize; int error, fd; sector = NULL; error = 0; fd = g_open(name, 1); if (fd == -1) return (errno); mediasize = g_mediasize(fd); if (mediasize == -1) { error = errno; goto out; } sectorsize = g_sectorsize(fd); if (sectorsize == -1) { error = errno; goto out; } assert(sectorsize >= (ssize_t)size); sector = malloc(sectorsize); if (sector == NULL) { error = ENOMEM; goto out; } bcopy(md, sector, size); + bzero(sector + size, sectorsize - size); if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != sectorsize) { error = errno; goto out; } (void)g_flush(fd); out: if (sector != NULL) free(sector); (void)g_close(fd); return (error); } int g_metadata_clear(const char *name, const char *magic) { struct std_metadata md; unsigned char *sector; ssize_t sectorsize; off_t mediasize; int error, fd; sector = NULL; error = 0; fd = g_open(name, 1); if (fd == -1) return (errno); mediasize = g_mediasize(fd); if (mediasize == 0) { error = errno; goto out; } sectorsize = g_sectorsize(fd); if (sectorsize <= 0) { error = errno; goto out; } sector = malloc(sectorsize); if (sector == NULL) { error = ENOMEM; goto out; } if (magic != NULL) { if (pread(fd, sector, sectorsize, mediasize - sectorsize) != sectorsize) { error = errno; goto out; } std_metadata_decode(sector, &md); if (strcmp(md.md_magic, magic) != 0) { error = EINVAL; goto out; } } bzero(sector, sectorsize); if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != sectorsize) { error = errno; goto out; } (void)g_flush(fd); out: free(sector); g_close(fd); return (error); } /* * Set an error message, if one does not already exist. */ void gctl_error(struct gctl_req *req, const char *error, ...) { va_list ap; if (req != NULL && req->error != NULL) return; va_start(ap, error); if (req != NULL) { vasprintf(&req->error, error, ap); } else { vfprintf(stderr, error, ap); fprintf(stderr, "\n"); } va_end(ap); } static void * gctl_get_param(struct gctl_req *req, size_t len, const char *pfmt, va_list ap) { struct gctl_req_arg *argp; char param[256]; unsigned int i; void *p; vsnprintf(param, sizeof(param), pfmt, ap); for (i = 0; i < req->narg; i++) { argp = &req->arg[i]; if (strcmp(param, argp->name)) continue; if (!(argp->flag & GCTL_PARAM_RD)) continue; p = argp->value; if (len == 0) { /* We are looking for a string. */ if (argp->len < 1) { fprintf(stderr, "No length argument (%s).\n", param); abort(); } if (((char *)p)[argp->len - 1] != '\0') { fprintf(stderr, "Unterminated argument (%s).\n", param); abort(); } } else if ((int)len != argp->len) { fprintf(stderr, "Wrong length %s argument.\n", param); abort(); } return (p); } fprintf(stderr, "No such argument (%s).\n", param); abort(); } int gctl_get_int(struct gctl_req *req, const char *pfmt, ...) { int *p; va_list ap; va_start(ap, pfmt); p = gctl_get_param(req, sizeof(int), pfmt, ap); va_end(ap); return (*p); } intmax_t gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...) { intmax_t *p; va_list ap; va_start(ap, pfmt); p = gctl_get_param(req, sizeof(intmax_t), pfmt, ap); va_end(ap); return (*p); } const char * gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...) { const char *p; va_list ap; va_start(ap, pfmt); p = gctl_get_param(req, 0, pfmt, ap); va_end(ap); return (p); } int gctl_change_param(struct gctl_req *req, const char *name, int len, const void *value) { struct gctl_req_arg *ap; unsigned int i; if (req == NULL || req->error != NULL) return (EDOOFUS); for (i = 0; i < req->narg; i++) { ap = &req->arg[i]; if (strcmp(ap->name, name) != 0) continue; ap->value = __DECONST(void *, value); if (len >= 0) { ap->flag &= ~GCTL_PARAM_ASCII; ap->len = len; } else if (len < 0) { ap->flag |= GCTL_PARAM_ASCII; ap->len = strlen(value) + 1; } return (0); } return (ENOENT); } int gctl_delete_param(struct gctl_req *req, const char *name) { struct gctl_req_arg *ap; unsigned int i; if (req == NULL || req->error != NULL) return (EDOOFUS); i = 0; while (i < req->narg) { ap = &req->arg[i]; if (strcmp(ap->name, name) == 0) break; i++; } if (i == req->narg) return (ENOENT); free(ap->name); req->narg--; while (i < req->narg) { req->arg[i] = req->arg[i + 1]; i++; } return (0); } int gctl_has_param(struct gctl_req *req, const char *name) { struct gctl_req_arg *ap; unsigned int i; if (req == NULL || req->error != NULL) return (0); for (i = 0; i < req->narg; i++) { ap = &req->arg[i]; if (strcmp(ap->name, name) == 0) return (1); } return (0); } Index: stable/11/sys/geom/eli/g_eli_integrity.c =================================================================== --- stable/11/sys/geom/eli/g_eli_integrity.c (revision 330725) +++ stable/11/sys/geom/eli/g_eli_integrity.c (revision 330726) @@ -1,525 +1,533 @@ /*- * Copyright (c) 2005-2011 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 /* * The data layout description when integrity verification is configured. * * One of the most important assumption here is that authenticated data and its * HMAC has to be stored in the same place (namely in the same sector) to make * it work reliable. * The problem is that file systems work only with sectors that are multiple of * 512 bytes and a power of two number. * My idea to implement it is as follows. * Let's store HMAC in sector. This is a must. This leaves us 480 bytes for * data. We can't use that directly (ie. we can't create provider with 480 bytes * sector size). We need another sector from where we take only 32 bytes of data * and we store HMAC of this data as well. This takes two sectors from the * original provider at the input and leaves us one sector of authenticated data * at the output. Not very efficient, but you got the idea. * Now, let's assume, we want to create provider with 4096 bytes sector. * To output 4096 bytes of authenticated data we need 8x480 plus 1x256, so we * need nine 512-bytes sectors at the input to get one 4096-bytes sector at the * output. That's better. With 4096 bytes sector we can use 89% of size of the * original provider. I find it as an acceptable cost. * The reliability comes from the fact, that every HMAC stored inside the sector * is calculated only for the data in the same sector, so its impossible to * write new data and leave old HMAC or vice versa. * * And here is the picture: * * da0: +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+-----+ * |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |480b| |32b |256b | * |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data| |HMAC|Data | * +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+ +----+-----+ * |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |512 bytes| |288 bytes | * +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ |224 unused| * +----------+ * da0.eli: +----+----+----+----+----+----+----+----+----+ * |480b|480b|480b|480b|480b|480b|480b|480b|256b| * +----+----+----+----+----+----+----+----+----+ * | 4096 bytes | * +--------------------------------------------+ * * PS. You can use any sector size with geli(8). My example is using 4kB, * because it's most efficient. For 8kB sectors you need 2 extra sectors, * so the cost is the same as for 4kB sectors. */ /* * Code paths: * BIO_READ: * g_eli_start -> g_eli_auth_read -> g_io_request -> g_eli_read_done -> g_eli_auth_run -> g_eli_auth_read_done -> g_io_deliver * BIO_WRITE: * g_eli_start -> g_eli_auth_run -> g_eli_auth_write_done -> g_io_request -> g_eli_write_done -> g_io_deliver */ MALLOC_DECLARE(M_ELI); /* * Here we generate key for HMAC. Every sector has its own HMAC key, so it is * not possible to copy sectors. * We cannot depend on fact, that every sector has its own IV, because different * IV doesn't change HMAC, when we use encrypt-then-authenticate method. */ static void g_eli_auth_keygen(struct g_eli_softc *sc, off_t offset, u_char *key) { SHA256_CTX ctx; /* Copy precalculated SHA256 context. */ bcopy(&sc->sc_akeyctx, &ctx, sizeof(ctx)); SHA256_Update(&ctx, (uint8_t *)&offset, sizeof(offset)); SHA256_Final(key, &ctx); } /* * The function is called after we read and decrypt data. * * g_eli_start -> g_eli_auth_read -> g_io_request -> g_eli_read_done -> g_eli_auth_run -> G_ELI_AUTH_READ_DONE -> g_io_deliver */ static int g_eli_auth_read_done(struct cryptop *crp) { struct g_eli_softc *sc; struct bio *bp; if (crp->crp_etype == EAGAIN) { if (g_eli_crypto_rerun(crp) == 0) return (0); } bp = (struct bio *)crp->crp_opaque; bp->bio_inbed++; if (crp->crp_etype == 0) { bp->bio_completed += crp->crp_olen; G_ELI_DEBUG(3, "Crypto READ request done (%d/%d) (add=%jd completed=%jd).", bp->bio_inbed, bp->bio_children, (intmax_t)crp->crp_olen, (intmax_t)bp->bio_completed); } else { G_ELI_DEBUG(1, "Crypto READ request failed (%d/%d) error=%d.", bp->bio_inbed, bp->bio_children, crp->crp_etype); if (bp->bio_error == 0) bp->bio_error = crp->crp_etype; } sc = bp->bio_to->geom->softc; g_eli_key_drop(sc, crp->crp_desc->crd_next->crd_key); /* * Do we have all sectors already? */ if (bp->bio_inbed < bp->bio_children) return (0); if (bp->bio_error == 0) { u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize; u_char *srcdata, *dstdata, *auth; off_t coroff, corsize; /* * Verify data integrity based on calculated and read HMACs. */ /* Sectorsize of decrypted provider eg. 4096. */ decr_secsize = bp->bio_to->sectorsize; /* The real sectorsize of encrypted provider, eg. 512. */ encr_secsize = LIST_FIRST(&sc->sc_geom->consumer)->provider->sectorsize; /* Number of data bytes in one encrypted sector, eg. 480. */ data_secsize = sc->sc_data_per_sector; /* Number of sectors from decrypted provider, eg. 2. */ nsec = bp->bio_length / decr_secsize; /* Number of sectors from encrypted provider, eg. 18. */ nsec = (nsec * sc->sc_bytes_per_sector) / encr_secsize; /* Last sector number in every big sector, eg. 9. */ lsec = sc->sc_bytes_per_sector / encr_secsize; srcdata = bp->bio_driver2; dstdata = bp->bio_data; auth = srcdata + encr_secsize * nsec; coroff = -1; corsize = 0; for (i = 1; i <= nsec; i++) { data_secsize = sc->sc_data_per_sector; if ((i % lsec) == 0) data_secsize = decr_secsize % data_secsize; if (bcmp(srcdata, auth, sc->sc_alen) != 0) { /* * Curruption detected, remember the offset if * this is the first corrupted sector and * increase size. */ if (bp->bio_error == 0) bp->bio_error = -1; if (coroff == -1) { coroff = bp->bio_offset + (dstdata - (u_char *)bp->bio_data); } corsize += data_secsize; } else { /* * No curruption, good. * Report previous corruption if there was one. */ if (coroff != -1) { G_ELI_DEBUG(0, "%s: Failed to authenticate %jd " "bytes of data at offset %jd.", sc->sc_name, (intmax_t)corsize, (intmax_t)coroff); coroff = -1; corsize = 0; } bcopy(srcdata + sc->sc_alen, dstdata, data_secsize); } srcdata += encr_secsize; dstdata += data_secsize; auth += sc->sc_alen; } /* Report previous corruption if there was one. */ if (coroff != -1) { G_ELI_DEBUG(0, "%s: Failed to authenticate %jd " "bytes of data at offset %jd.", sc->sc_name, (intmax_t)corsize, (intmax_t)coroff); } } free(bp->bio_driver2, M_ELI); bp->bio_driver2 = NULL; if (bp->bio_error != 0) { if (bp->bio_error == -1) bp->bio_error = EINVAL; else { G_ELI_LOGREQ(0, bp, "Crypto READ request failed (error=%d).", bp->bio_error); } bp->bio_completed = 0; } /* * Read is finished, send it up. */ g_io_deliver(bp, bp->bio_error); atomic_subtract_int(&sc->sc_inflight, 1); return (0); } /* * The function is called after data encryption. * * g_eli_start -> g_eli_auth_run -> G_ELI_AUTH_WRITE_DONE -> g_io_request -> g_eli_write_done -> g_io_deliver */ static int g_eli_auth_write_done(struct cryptop *crp) { struct g_eli_softc *sc; struct g_consumer *cp; struct bio *bp, *cbp, *cbp2; u_int nsec; if (crp->crp_etype == EAGAIN) { if (g_eli_crypto_rerun(crp) == 0) return (0); } bp = (struct bio *)crp->crp_opaque; bp->bio_inbed++; if (crp->crp_etype == 0) { G_ELI_DEBUG(3, "Crypto WRITE request done (%d/%d).", bp->bio_inbed, bp->bio_children); } else { G_ELI_DEBUG(1, "Crypto WRITE request failed (%d/%d) error=%d.", bp->bio_inbed, bp->bio_children, crp->crp_etype); if (bp->bio_error == 0) bp->bio_error = crp->crp_etype; } sc = bp->bio_to->geom->softc; g_eli_key_drop(sc, crp->crp_desc->crd_key); /* * All sectors are already encrypted? */ if (bp->bio_inbed < bp->bio_children) return (0); if (bp->bio_error != 0) { G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).", bp->bio_error); free(bp->bio_driver2, M_ELI); bp->bio_driver2 = NULL; cbp = bp->bio_driver1; bp->bio_driver1 = NULL; g_destroy_bio(cbp); g_io_deliver(bp, bp->bio_error); atomic_subtract_int(&sc->sc_inflight, 1); return (0); } cp = LIST_FIRST(&sc->sc_geom->consumer); cbp = bp->bio_driver1; bp->bio_driver1 = NULL; cbp->bio_to = cp->provider; cbp->bio_done = g_eli_write_done; /* Number of sectors from decrypted provider, eg. 1. */ nsec = bp->bio_length / bp->bio_to->sectorsize; /* Number of sectors from encrypted provider, eg. 9. */ nsec = (nsec * sc->sc_bytes_per_sector) / cp->provider->sectorsize; cbp->bio_length = cp->provider->sectorsize * nsec; cbp->bio_offset = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; cbp->bio_data = bp->bio_driver2; /* * We write more than what is requested, so we have to be ready to write * more than MAXPHYS. */ cbp2 = NULL; if (cbp->bio_length > MAXPHYS) { cbp2 = g_duplicate_bio(bp); cbp2->bio_length = cbp->bio_length - MAXPHYS; cbp2->bio_data = cbp->bio_data + MAXPHYS; cbp2->bio_offset = cbp->bio_offset + MAXPHYS; cbp2->bio_to = cp->provider; cbp2->bio_done = g_eli_write_done; cbp->bio_length = MAXPHYS; } /* * Send encrypted data to the provider. */ G_ELI_LOGREQ(2, cbp, "Sending request."); bp->bio_inbed = 0; bp->bio_children = (cbp2 != NULL ? 2 : 1); g_io_request(cbp, cp); if (cbp2 != NULL) { G_ELI_LOGREQ(2, cbp2, "Sending request."); g_io_request(cbp2, cp); } return (0); } void g_eli_auth_read(struct g_eli_softc *sc, struct bio *bp) { struct g_consumer *cp; struct bio *cbp, *cbp2; size_t size; off_t nsec; bp->bio_pflags = 0; cp = LIST_FIRST(&sc->sc_geom->consumer); cbp = bp->bio_driver1; bp->bio_driver1 = NULL; cbp->bio_to = cp->provider; cbp->bio_done = g_eli_read_done; /* Number of sectors from decrypted provider, eg. 1. */ nsec = bp->bio_length / bp->bio_to->sectorsize; /* Number of sectors from encrypted provider, eg. 9. */ nsec = (nsec * sc->sc_bytes_per_sector) / cp->provider->sectorsize; cbp->bio_length = cp->provider->sectorsize * nsec; size = cbp->bio_length; size += sc->sc_alen * nsec; size += sizeof(struct cryptop) * nsec; size += sizeof(struct cryptodesc) * nsec * 2; size += G_ELI_AUTH_SECKEYLEN * nsec; cbp->bio_offset = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; bp->bio_driver2 = malloc(size, M_ELI, M_WAITOK); cbp->bio_data = bp->bio_driver2; /* * We read more than what is requested, so we have to be ready to read * more than MAXPHYS. */ cbp2 = NULL; if (cbp->bio_length > MAXPHYS) { cbp2 = g_duplicate_bio(bp); cbp2->bio_length = cbp->bio_length - MAXPHYS; cbp2->bio_data = cbp->bio_data + MAXPHYS; cbp2->bio_offset = cbp->bio_offset + MAXPHYS; cbp2->bio_to = cp->provider; cbp2->bio_done = g_eli_read_done; cbp->bio_length = MAXPHYS; } /* * Read encrypted data from provider. */ G_ELI_LOGREQ(2, cbp, "Sending request."); g_io_request(cbp, cp); if (cbp2 != NULL) { G_ELI_LOGREQ(2, cbp2, "Sending request."); g_io_request(cbp2, cp); } } /* * This is the main function responsible for cryptography (ie. communication * with crypto(9) subsystem). * * BIO_READ: * g_eli_start -> g_eli_auth_read -> g_io_request -> g_eli_read_done -> G_ELI_AUTH_RUN -> g_eli_auth_read_done -> g_io_deliver * BIO_WRITE: * g_eli_start -> G_ELI_AUTH_RUN -> g_eli_auth_write_done -> g_io_request -> g_eli_write_done -> g_io_deliver */ void g_eli_auth_run(struct g_eli_worker *wr, struct bio *bp) { struct g_eli_softc *sc; struct cryptop *crp; struct cryptodesc *crde, *crda; u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize; off_t dstoff; u_char *p, *data, *auth, *authkey, *plaindata; int error; G_ELI_LOGREQ(3, bp, "%s", __func__); bp->bio_pflags = wr->w_number; sc = wr->w_softc; /* Sectorsize of decrypted provider eg. 4096. */ decr_secsize = bp->bio_to->sectorsize; /* The real sectorsize of encrypted provider, eg. 512. */ encr_secsize = LIST_FIRST(&sc->sc_geom->consumer)->provider->sectorsize; /* Number of data bytes in one encrypted sector, eg. 480. */ data_secsize = sc->sc_data_per_sector; /* Number of sectors from decrypted provider, eg. 2. */ nsec = bp->bio_length / decr_secsize; /* Number of sectors from encrypted provider, eg. 18. */ nsec = (nsec * sc->sc_bytes_per_sector) / encr_secsize; /* Last sector number in every big sector, eg. 9. */ lsec = sc->sc_bytes_per_sector / encr_secsize; /* Destination offset, used for IV generation. */ dstoff = (bp->bio_offset / bp->bio_to->sectorsize) * sc->sc_bytes_per_sector; auth = NULL; /* Silence compiler warning. */ plaindata = bp->bio_data; if (bp->bio_cmd == BIO_READ) { data = bp->bio_driver2; auth = data + encr_secsize * nsec; p = auth + sc->sc_alen * nsec; } else { size_t size; size = encr_secsize * nsec; size += sizeof(*crp) * nsec; size += sizeof(*crde) * nsec; size += sizeof(*crda) * nsec; size += G_ELI_AUTH_SECKEYLEN * nsec; data = malloc(size, M_ELI, M_WAITOK); bp->bio_driver2 = data; p = data + encr_secsize * nsec; } bp->bio_inbed = 0; bp->bio_children = nsec; for (i = 1; i <= nsec; i++, dstoff += encr_secsize) { crp = (struct cryptop *)p; p += sizeof(*crp); crde = (struct cryptodesc *)p; p += sizeof(*crde); crda = (struct cryptodesc *)p; p += sizeof(*crda); authkey = (u_char *)p; p += G_ELI_AUTH_SECKEYLEN; data_secsize = sc->sc_data_per_sector; - if ((i % lsec) == 0) + if ((i % lsec) == 0) { data_secsize = decr_secsize % data_secsize; + /* + * Last encrypted sector of each decrypted sector is + * only partially filled. + */ + if (bp->bio_cmd == BIO_WRITE) + memset(data + sc->sc_alen + data_secsize, 0, + encr_secsize - sc->sc_alen - data_secsize); + } if (bp->bio_cmd == BIO_READ) { /* Remember read HMAC. */ bcopy(data, auth, sc->sc_alen); auth += sc->sc_alen; /* TODO: bzero(9) can be commented out later. */ bzero(data, sc->sc_alen); } else { bcopy(plaindata, data + sc->sc_alen, data_secsize); plaindata += data_secsize; } crp->crp_sid = wr->w_sid; crp->crp_ilen = sc->sc_alen + data_secsize; crp->crp_olen = data_secsize; crp->crp_opaque = (void *)bp; crp->crp_buf = (void *)data; data += encr_secsize; crp->crp_flags = CRYPTO_F_CBIFSYNC; if (g_eli_batch) crp->crp_flags |= CRYPTO_F_BATCH; if (bp->bio_cmd == BIO_WRITE) { crp->crp_callback = g_eli_auth_write_done; crp->crp_desc = crde; crde->crd_next = crda; crda->crd_next = NULL; } else { crp->crp_callback = g_eli_auth_read_done; crp->crp_desc = crda; crda->crd_next = crde; crde->crd_next = NULL; } crde->crd_skip = sc->sc_alen; crde->crd_len = data_secsize; crde->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; if ((sc->sc_flags & G_ELI_FLAG_FIRST_KEY) == 0) crde->crd_flags |= CRD_F_KEY_EXPLICIT; if (bp->bio_cmd == BIO_WRITE) crde->crd_flags |= CRD_F_ENCRYPT; crde->crd_alg = sc->sc_ealgo; crde->crd_key = g_eli_key_hold(sc, dstoff, encr_secsize); crde->crd_klen = sc->sc_ekeylen; if (sc->sc_ealgo == CRYPTO_AES_XTS) crde->crd_klen <<= 1; g_eli_crypto_ivgen(sc, dstoff, crde->crd_iv, sizeof(crde->crd_iv)); crda->crd_skip = sc->sc_alen; crda->crd_len = data_secsize; crda->crd_inject = 0; crda->crd_flags = CRD_F_KEY_EXPLICIT; crda->crd_alg = sc->sc_aalgo; g_eli_auth_keygen(sc, dstoff, authkey); crda->crd_key = authkey; crda->crd_klen = G_ELI_AUTH_SECKEYLEN * 8; crp->crp_etype = 0; error = crypto_dispatch(crp); KASSERT(error == 0, ("crypto_dispatch() failed (error=%d)", error)); } } Index: stable/11/sys/geom/virstor/g_virstor.c =================================================================== --- stable/11/sys/geom/virstor/g_virstor.c (revision 330725) +++ stable/11/sys/geom/virstor/g_virstor.c (revision 330726) @@ -1,1890 +1,1891 @@ /*- * Copyright (c) 2006-2007 Ivan Voras * 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. */ /* Implementation notes: * - "Components" are wrappers around providers that make up the * virtual storage (i.e. a virstor has "physical" components) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include FEATURE(g_virstor, "GEOM virtual storage support"); /* Declare malloc(9) label */ static MALLOC_DEFINE(M_GVIRSTOR, "gvirstor", "GEOM_VIRSTOR Data"); /* GEOM class methods */ static g_init_t g_virstor_init; static g_fini_t g_virstor_fini; static g_taste_t g_virstor_taste; static g_ctl_req_t g_virstor_config; static g_ctl_destroy_geom_t g_virstor_destroy_geom; /* Declare & initialize class structure ("geom class") */ struct g_class g_virstor_class = { .name = G_VIRSTOR_CLASS_NAME, .version = G_VERSION, .init = g_virstor_init, .fini = g_virstor_fini, .taste = g_virstor_taste, .ctlreq = g_virstor_config, .destroy_geom = g_virstor_destroy_geom /* The .dumpconf and the rest are only usable for a geom instance, so * they will be set when such instance is created. */ }; /* Declare sysctl's and loader tunables */ SYSCTL_DECL(_kern_geom); static SYSCTL_NODE(_kern_geom, OID_AUTO, virstor, CTLFLAG_RW, 0, "GEOM_GVIRSTOR information"); static u_int g_virstor_debug = 2; /* XXX: lower to 2 when released to public */ SYSCTL_UINT(_kern_geom_virstor, OID_AUTO, debug, CTLFLAG_RWTUN, &g_virstor_debug, 0, "Debug level (2=production, 5=normal, 15=excessive)"); static u_int g_virstor_chunk_watermark = 100; SYSCTL_UINT(_kern_geom_virstor, OID_AUTO, chunk_watermark, CTLFLAG_RWTUN, &g_virstor_chunk_watermark, 0, "Minimum number of free chunks before issuing administrative warning"); static u_int g_virstor_component_watermark = 1; SYSCTL_UINT(_kern_geom_virstor, OID_AUTO, component_watermark, CTLFLAG_RWTUN, &g_virstor_component_watermark, 0, "Minimum number of free components before issuing administrative warning"); static int read_metadata(struct g_consumer *, struct g_virstor_metadata *); static void write_metadata(struct g_consumer *, struct g_virstor_metadata *); static int clear_metadata(struct g_virstor_component *); static int add_provider_to_geom(struct g_virstor_softc *, struct g_provider *, struct g_virstor_metadata *); static struct g_geom *create_virstor_geom(struct g_class *, struct g_virstor_metadata *); static void virstor_check_and_run(struct g_virstor_softc *); static u_int virstor_valid_components(struct g_virstor_softc *); static int virstor_geom_destroy(struct g_virstor_softc *, boolean_t, boolean_t); static void remove_component(struct g_virstor_softc *, struct g_virstor_component *, boolean_t); static void bioq_dismantle(struct bio_queue_head *); static int allocate_chunk(struct g_virstor_softc *, struct g_virstor_component **, u_int *, u_int *); static void delay_destroy_consumer(void *, int); static void dump_component(struct g_virstor_component *comp); #if 0 static void dump_me(struct virstor_map_entry *me, unsigned int nr); #endif static void virstor_ctl_stop(struct gctl_req *, struct g_class *); static void virstor_ctl_add(struct gctl_req *, struct g_class *); static void virstor_ctl_remove(struct gctl_req *, struct g_class *); static struct g_virstor_softc * virstor_find_geom(const struct g_class *, const char *); static void update_metadata(struct g_virstor_softc *); static void fill_metadata(struct g_virstor_softc *, struct g_virstor_metadata *, u_int, u_int); static void g_virstor_orphan(struct g_consumer *); static int g_virstor_access(struct g_provider *, int, int, int); static void g_virstor_start(struct bio *); static void g_virstor_dumpconf(struct sbuf *, const char *, struct g_geom *, struct g_consumer *, struct g_provider *); static void g_virstor_done(struct bio *); static void invalid_call(void); /* * Initialise GEOM class (per-class callback) */ static void g_virstor_init(struct g_class *mp __unused) { /* Catch map struct size mismatch at compile time; Map entries must * fit into MAXPHYS exactly, with no wasted space. */ CTASSERT(VIRSTOR_MAP_BLOCK_ENTRIES*VIRSTOR_MAP_ENTRY_SIZE == MAXPHYS); /* Init UMA zones, TAILQ's, other global vars */ } /* * Finalise GEOM class (per-class callback) */ static void g_virstor_fini(struct g_class *mp __unused) { /* Deinit UMA zones & global vars */ } /* * Config (per-class callback) */ static void g_virstor_config(struct gctl_req *req, struct g_class *cp, char const *verb) { uint32_t *version; g_topology_assert(); version = gctl_get_paraml(req, "version", sizeof(*version)); if (version == NULL) { gctl_error(req, "Failed to get 'version' argument"); return; } if (*version != G_VIRSTOR_VERSION) { gctl_error(req, "Userland and kernel versions out of sync"); return; } g_topology_unlock(); if (strcmp(verb, "add") == 0) virstor_ctl_add(req, cp); else if (strcmp(verb, "stop") == 0 || strcmp(verb, "destroy") == 0) virstor_ctl_stop(req, cp); else if (strcmp(verb, "remove") == 0) virstor_ctl_remove(req, cp); else gctl_error(req, "unknown verb: '%s'", verb); g_topology_lock(); } /* * "stop" verb from userland */ static void virstor_ctl_stop(struct gctl_req *req, struct g_class *cp) { int *force, *nargs; int i; nargs = gctl_get_paraml(req, "nargs", sizeof *nargs); if (nargs == NULL) { gctl_error(req, "Error fetching argument '%s'", "nargs"); return; } if (*nargs < 1) { gctl_error(req, "Invalid number of arguments"); return; } force = gctl_get_paraml(req, "force", sizeof *force); if (force == NULL) { gctl_error(req, "Error fetching argument '%s'", "force"); return; } g_topology_lock(); for (i = 0; i < *nargs; i++) { char param[8]; const char *name; struct g_virstor_softc *sc; int error; sprintf(param, "arg%d", i); name = gctl_get_asciiparam(req, param); if (name == NULL) { gctl_error(req, "No 'arg%d' argument", i); g_topology_unlock(); return; } sc = virstor_find_geom(cp, name); if (sc == NULL) { gctl_error(req, "Don't know anything about '%s'", name); g_topology_unlock(); return; } LOG_MSG(LVL_INFO, "Stopping %s by the userland command", sc->geom->name); update_metadata(sc); if ((error = virstor_geom_destroy(sc, TRUE, TRUE)) != 0) { LOG_MSG(LVL_ERROR, "Cannot destroy %s: %d", sc->geom->name, error); } } g_topology_unlock(); } /* * "add" verb from userland - add new component(s) to the structure. * This will be done all at once in here, without going through the * .taste function for new components. */ static void virstor_ctl_add(struct gctl_req *req, struct g_class *cp) { /* Note: while this is going on, I/O is being done on * the g_up and g_down threads. The idea is to make changes * to softc members in a way that can atomically activate * them all at once. */ struct g_virstor_softc *sc; int *hardcode, *nargs; const char *geom_name; /* geom to add a component to */ struct g_consumer *fcp; struct g_virstor_bio_q *bq; u_int added; int error; int i; nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); if (nargs == NULL) { gctl_error(req, "Error fetching argument '%s'", "nargs"); return; } if (*nargs < 2) { gctl_error(req, "Invalid number of arguments"); return; } hardcode = gctl_get_paraml(req, "hardcode", sizeof(*hardcode)); if (hardcode == NULL) { gctl_error(req, "Error fetching argument '%s'", "hardcode"); return; } /* Find "our" geom */ geom_name = gctl_get_asciiparam(req, "arg0"); if (geom_name == NULL) { gctl_error(req, "Error fetching argument '%s'", "geom_name (arg0)"); return; } sc = virstor_find_geom(cp, geom_name); if (sc == NULL) { gctl_error(req, "Don't know anything about '%s'", geom_name); return; } if (virstor_valid_components(sc) != sc->n_components) { LOG_MSG(LVL_ERROR, "Cannot add components to incomplete " "virstor %s", sc->geom->name); gctl_error(req, "Virstor %s is incomplete", sc->geom->name); return; } fcp = sc->components[0].gcons; added = 0; g_topology_lock(); for (i = 1; i < *nargs; i++) { struct g_virstor_metadata md; char aname[8]; const char *prov_name; struct g_provider *pp; struct g_consumer *cp; u_int nc; u_int j; snprintf(aname, sizeof aname, "arg%d", i); prov_name = gctl_get_asciiparam(req, aname); if (prov_name == NULL) { gctl_error(req, "Error fetching argument '%s'", aname); g_topology_unlock(); return; } if (strncmp(prov_name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) prov_name += sizeof(_PATH_DEV) - 1; pp = g_provider_by_name(prov_name); if (pp == NULL) { /* This is the most common error so be verbose about it */ if (added != 0) { gctl_error(req, "Invalid provider: '%s' (added" " %u components)", prov_name, added); update_metadata(sc); } else { gctl_error(req, "Invalid provider: '%s'", prov_name); } g_topology_unlock(); return; } cp = g_new_consumer(sc->geom); if (cp == NULL) { gctl_error(req, "Cannot create consumer"); g_topology_unlock(); return; } error = g_attach(cp, pp); if (error != 0) { gctl_error(req, "Cannot attach a consumer to %s", pp->name); g_destroy_consumer(cp); g_topology_unlock(); return; } if (fcp->acr != 0 || fcp->acw != 0 || fcp->ace != 0) { error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); if (error != 0) { gctl_error(req, "Access request failed for %s", pp->name); g_destroy_consumer(cp); g_topology_unlock(); return; } } if (fcp->provider->sectorsize != pp->sectorsize) { gctl_error(req, "Sector size doesn't fit for %s", pp->name); g_destroy_consumer(cp); g_topology_unlock(); return; } for (j = 0; j < sc->n_components; j++) { if (strcmp(sc->components[j].gcons->provider->name, pp->name) == 0) { gctl_error(req, "Component %s already in %s", pp->name, sc->geom->name); g_destroy_consumer(cp); g_topology_unlock(); return; } } sc->components = realloc(sc->components, sizeof(*sc->components) * (sc->n_components + 1), M_GVIRSTOR, M_WAITOK); nc = sc->n_components; sc->components[nc].gcons = cp; sc->components[nc].sc = sc; sc->components[nc].index = nc; sc->components[nc].chunk_count = cp->provider->mediasize / sc->chunk_size; sc->components[nc].chunk_next = 0; sc->components[nc].chunk_reserved = 0; if (sc->components[nc].chunk_count < 4) { gctl_error(req, "Provider too small: %s", cp->provider->name); g_destroy_consumer(cp); g_topology_unlock(); return; } fill_metadata(sc, &md, nc, *hardcode); write_metadata(cp, &md); /* The new component becomes visible when n_components is * incremented */ sc->n_components++; added++; } /* This call to update_metadata() is critical. In case there's a * power failure in the middle of it and some components are updated * while others are not, there will be trouble on next .taste() iff * a non-updated component is detected first */ update_metadata(sc); g_topology_unlock(); LOG_MSG(LVL_INFO, "Added %d component(s) to %s", added, sc->geom->name); /* Fire off BIOs previously queued because there wasn't any * physical space left. If the BIOs still can't be satisfied * they will again be added to the end of the queue (during * which the mutex will be recursed) */ bq = malloc(sizeof(*bq), M_GVIRSTOR, M_WAITOK); bq->bio = NULL; mtx_lock(&sc->delayed_bio_q_mtx); /* First, insert a sentinel to the queue end, so we don't * end up in an infinite loop if there's still no free * space available. */ STAILQ_INSERT_TAIL(&sc->delayed_bio_q, bq, linkage); while (!STAILQ_EMPTY(&sc->delayed_bio_q)) { bq = STAILQ_FIRST(&sc->delayed_bio_q); if (bq->bio != NULL) { g_virstor_start(bq->bio); STAILQ_REMOVE_HEAD(&sc->delayed_bio_q, linkage); free(bq, M_GVIRSTOR); } else { STAILQ_REMOVE_HEAD(&sc->delayed_bio_q, linkage); free(bq, M_GVIRSTOR); break; } } mtx_unlock(&sc->delayed_bio_q_mtx); } /* * Find a geom handled by the class */ static struct g_virstor_softc * virstor_find_geom(const struct g_class *cp, const char *name) { struct g_geom *gp; LIST_FOREACH(gp, &cp->geom, geom) { if (strcmp(name, gp->name) == 0) return (gp->softc); } return (NULL); } /* * Update metadata on all components to reflect the current state * of these fields: * - chunk_next * - flags * - md_count * Expects things to be set up so write_metadata() can work, i.e. * the topology lock must be held. */ static void update_metadata(struct g_virstor_softc *sc) { struct g_virstor_metadata md; u_int n; if (virstor_valid_components(sc) != sc->n_components) return; /* Incomplete device */ LOG_MSG(LVL_DEBUG, "Updating metadata on components for %s", sc->geom->name); /* Update metadata on components */ g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, sc->geom->class->name, sc->geom->name); g_topology_assert(); for (n = 0; n < sc->n_components; n++) { read_metadata(sc->components[n].gcons, &md); md.chunk_next = sc->components[n].chunk_next; md.flags = sc->components[n].flags; md.md_count = sc->n_components; write_metadata(sc->components[n].gcons, &md); } } /* * Fills metadata (struct md) from information stored in softc and the nc'th * component of virstor */ static void fill_metadata(struct g_virstor_softc *sc, struct g_virstor_metadata *md, u_int nc, u_int hardcode) { struct g_virstor_component *c; bzero(md, sizeof *md); c = &sc->components[nc]; strncpy(md->md_magic, G_VIRSTOR_MAGIC, sizeof md->md_magic); md->md_version = G_VIRSTOR_VERSION; strncpy(md->md_name, sc->geom->name, sizeof md->md_name); md->md_id = sc->id; md->md_virsize = sc->virsize; md->md_chunk_size = sc->chunk_size; md->md_count = sc->n_components; if (hardcode) { strncpy(md->provider, c->gcons->provider->name, sizeof md->provider); } md->no = nc; md->provsize = c->gcons->provider->mediasize; md->chunk_count = c->chunk_count; md->chunk_next = c->chunk_next; md->chunk_reserved = c->chunk_reserved; md->flags = c->flags; } /* * Remove a component from virstor device. * Can only be done if the component is unallocated. */ static void virstor_ctl_remove(struct gctl_req *req, struct g_class *cp) { /* As this is executed in parallel to I/O, operations on virstor * structures must be as atomic as possible. */ struct g_virstor_softc *sc; int *nargs; const char *geom_name; u_int removed; int i; nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); if (nargs == NULL) { gctl_error(req, "Error fetching argument '%s'", "nargs"); return; } if (*nargs < 2) { gctl_error(req, "Invalid number of arguments"); return; } /* Find "our" geom */ geom_name = gctl_get_asciiparam(req, "arg0"); if (geom_name == NULL) { gctl_error(req, "Error fetching argument '%s'", "geom_name (arg0)"); return; } sc = virstor_find_geom(cp, geom_name); if (sc == NULL) { gctl_error(req, "Don't know anything about '%s'", geom_name); return; } if (virstor_valid_components(sc) != sc->n_components) { LOG_MSG(LVL_ERROR, "Cannot remove components from incomplete " "virstor %s", sc->geom->name); gctl_error(req, "Virstor %s is incomplete", sc->geom->name); return; } removed = 0; for (i = 1; i < *nargs; i++) { char param[8]; const char *prov_name; int j, found; struct g_virstor_component *newcomp, *compbak; sprintf(param, "arg%d", i); prov_name = gctl_get_asciiparam(req, param); if (prov_name == NULL) { gctl_error(req, "Error fetching argument '%s'", param); return; } if (strncmp(prov_name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) prov_name += sizeof(_PATH_DEV) - 1; found = -1; for (j = 0; j < sc->n_components; j++) { if (strcmp(sc->components[j].gcons->provider->name, prov_name) == 0) { found = j; break; } } if (found == -1) { LOG_MSG(LVL_ERROR, "No %s component in %s", prov_name, sc->geom->name); continue; } compbak = sc->components; newcomp = malloc(sc->n_components * sizeof(*sc->components), M_GVIRSTOR, M_WAITOK | M_ZERO); bcopy(sc->components, newcomp, found * sizeof(*sc->components)); bcopy(&sc->components[found + 1], newcomp + found, found * sizeof(*sc->components)); if ((sc->components[j].flags & VIRSTOR_PROVIDER_ALLOCATED) != 0) { LOG_MSG(LVL_ERROR, "Allocated provider %s cannot be " "removed from %s", prov_name, sc->geom->name); free(newcomp, M_GVIRSTOR); /* We'll consider this non-fatal error */ continue; } /* Renumerate unallocated components */ for (j = 0; j < sc->n_components-1; j++) { if ((sc->components[j].flags & VIRSTOR_PROVIDER_ALLOCATED) == 0) { sc->components[j].index = j; } } /* This is the critical section. If a component allocation * event happens while both variables are not yet set, * there will be trouble. Something will panic on encountering * NULL sc->components[x].gcomp member. * Luckily, component allocation happens very rarely and * removing components is an abnormal action in any case. */ sc->components = newcomp; sc->n_components--; /* End critical section */ g_topology_lock(); if (clear_metadata(&compbak[found]) != 0) { LOG_MSG(LVL_WARNING, "Trouble ahead: cannot clear " "metadata on %s", prov_name); } g_detach(compbak[found].gcons); g_destroy_consumer(compbak[found].gcons); g_topology_unlock(); free(compbak, M_GVIRSTOR); removed++; } /* This call to update_metadata() is critical. In case there's a * power failure in the middle of it and some components are updated * while others are not, there will be trouble on next .taste() iff * a non-updated component is detected first */ g_topology_lock(); update_metadata(sc); g_topology_unlock(); LOG_MSG(LVL_INFO, "Removed %d component(s) from %s", removed, sc->geom->name); } /* * Clear metadata sector on component */ static int clear_metadata(struct g_virstor_component *comp) { char *buf; int error; LOG_MSG(LVL_INFO, "Clearing metadata on %s", comp->gcons->provider->name); g_topology_assert(); error = g_access(comp->gcons, 0, 1, 0); if (error != 0) return (error); buf = malloc(comp->gcons->provider->sectorsize, M_GVIRSTOR, M_WAITOK | M_ZERO); error = g_write_data(comp->gcons, comp->gcons->provider->mediasize - comp->gcons->provider->sectorsize, buf, comp->gcons->provider->sectorsize); free(buf, M_GVIRSTOR); g_access(comp->gcons, 0, -1, 0); return (error); } /* * Destroy geom forcibly. */ static int g_virstor_destroy_geom(struct gctl_req *req __unused, struct g_class *mp, struct g_geom *gp) { struct g_virstor_softc *sc; int exitval; sc = gp->softc; KASSERT(sc != NULL, ("%s: NULL sc", __func__)); exitval = 0; LOG_MSG(LVL_DEBUG, "%s called for %s, sc=%p", __func__, gp->name, gp->softc); if (sc != NULL) { #ifdef INVARIANTS char *buf; int error; off_t off; int isclean, count; int n; LOG_MSG(LVL_INFO, "INVARIANTS detected"); LOG_MSG(LVL_INFO, "Verifying allocation " "table for %s", sc->geom->name); count = 0; for (n = 0; n < sc->chunk_count; n++) { if (sc->map[n].flags || VIRSTOR_MAP_ALLOCATED != 0) count++; } LOG_MSG(LVL_INFO, "Device %s has %d allocated chunks", sc->geom->name, count); n = off = count = 0; isclean = 1; if (virstor_valid_components(sc) != sc->n_components) { /* This is a incomplete virstor device (not all * components have been found) */ LOG_MSG(LVL_ERROR, "Device %s is incomplete", sc->geom->name); goto bailout; } error = g_access(sc->components[0].gcons, 1, 0, 0); KASSERT(error == 0, ("%s: g_access failed (%d)", __func__, error)); /* Compare the whole on-disk allocation table with what's * currently in memory */ while (n < sc->chunk_count) { buf = g_read_data(sc->components[0].gcons, off, sc->sectorsize, &error); KASSERT(buf != NULL, ("g_read_data returned NULL (%d) " "for read at %jd", error, off)); if (bcmp(buf, &sc->map[n], sc->sectorsize) != 0) { LOG_MSG(LVL_ERROR, "ERROR in allocation table, " "entry %d, offset %jd", n, off); isclean = 0; count++; } n += sc->me_per_sector; off += sc->sectorsize; g_free(buf); } error = g_access(sc->components[0].gcons, -1, 0, 0); KASSERT(error == 0, ("%s: g_access failed (%d) on exit", __func__, error)); if (isclean != 1) { LOG_MSG(LVL_ERROR, "ALLOCATION TABLE CORRUPTED FOR %s " "(%d sectors don't match, max %zu allocations)", sc->geom->name, count, count * sc->me_per_sector); } else { LOG_MSG(LVL_INFO, "Allocation table ok for %s", sc->geom->name); } bailout: #endif update_metadata(sc); virstor_geom_destroy(sc, FALSE, FALSE); exitval = EAGAIN; } else exitval = 0; return (exitval); } /* * Taste event (per-class callback) * Examines a provider and creates geom instances if needed */ static struct g_geom * g_virstor_taste(struct g_class *mp, struct g_provider *pp, int flags) { struct g_virstor_metadata md; struct g_geom *gp; struct g_consumer *cp; struct g_virstor_softc *sc; int error; g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); g_topology_assert(); LOG_MSG(LVL_DEBUG, "Tasting %s", pp->name); /* We need a dummy geom to attach a consumer to the given provider */ gp = g_new_geomf(mp, "virstor:taste.helper"); gp->start = (void *)invalid_call; /* XXX: hacked up so the */ gp->access = (void *)invalid_call; /* compiler doesn't complain. */ gp->orphan = (void *)invalid_call; /* I really want these to fail. */ cp = g_new_consumer(gp); g_attach(cp, pp); error = read_metadata(cp, &md); g_detach(cp); g_destroy_consumer(cp); g_destroy_geom(gp); if (error != 0) return (NULL); if (strcmp(md.md_magic, G_VIRSTOR_MAGIC) != 0) return (NULL); if (md.md_version != G_VIRSTOR_VERSION) { LOG_MSG(LVL_ERROR, "Kernel module version invalid " "to handle %s (%s) : %d should be %d", md.md_name, pp->name, md.md_version, G_VIRSTOR_VERSION); return (NULL); } if (md.provsize != pp->mediasize) return (NULL); /* If the provider name is hardcoded, use the offered provider only * if it's been offered with its proper name (the one used in * the label command). */ if (md.provider[0] != '\0' && !g_compare_names(md.provider, pp->name)) return (NULL); /* Iterate all geoms this class already knows about to see if a new * geom instance of this class needs to be created (in case the provider * is first from a (possibly) multi-consumer geom) or it just needs * to be added to an existing instance. */ sc = NULL; gp = NULL; LIST_FOREACH(gp, &mp->geom, geom) { sc = gp->softc; if (sc == NULL) continue; if (strcmp(md.md_name, sc->geom->name) != 0) continue; if (md.md_id != sc->id) continue; break; } if (gp != NULL) { /* We found an existing geom instance; add to it */ LOG_MSG(LVL_INFO, "Adding %s to %s", pp->name, md.md_name); error = add_provider_to_geom(sc, pp, &md); if (error != 0) { LOG_MSG(LVL_ERROR, "Error adding %s to %s (error %d)", pp->name, md.md_name, error); return (NULL); } } else { /* New geom instance needs to be created */ gp = create_virstor_geom(mp, &md); if (gp == NULL) { LOG_MSG(LVL_ERROR, "Error creating new instance of " "class %s: %s", mp->name, md.md_name); LOG_MSG(LVL_DEBUG, "Error creating %s at %s", md.md_name, pp->name); return (NULL); } sc = gp->softc; LOG_MSG(LVL_INFO, "Adding %s to %s (first found)", pp->name, md.md_name); error = add_provider_to_geom(sc, pp, &md); if (error != 0) { LOG_MSG(LVL_ERROR, "Error adding %s to %s (error %d)", pp->name, md.md_name, error); virstor_geom_destroy(sc, TRUE, FALSE); return (NULL); } } return (gp); } /* * Destroyes consumer passed to it in arguments. Used as a callback * on g_event queue. */ static void delay_destroy_consumer(void *arg, int flags __unused) { struct g_consumer *c = arg; KASSERT(c != NULL, ("%s: invalid consumer", __func__)); LOG_MSG(LVL_DEBUG, "Consumer %s destroyed with delay", c->provider->name); g_detach(c); g_destroy_consumer(c); } /* * Remove a component (consumer) from geom instance; If it's the first * component being removed, orphan the provider to announce geom's being * dismantled */ static void remove_component(struct g_virstor_softc *sc, struct g_virstor_component *comp, boolean_t delay) { struct g_consumer *c; KASSERT(comp->gcons != NULL, ("Component with no consumer in %s", sc->geom->name)); c = comp->gcons; comp->gcons = NULL; KASSERT(c->provider != NULL, ("%s: no provider", __func__)); LOG_MSG(LVL_DEBUG, "Component %s removed from %s", c->provider->name, sc->geom->name); if (sc->provider != NULL) { LOG_MSG(LVL_INFO, "Removing provider %s", sc->provider->name); g_wither_provider(sc->provider, ENXIO); sc->provider = NULL; } if (c->acr > 0 || c->acw > 0 || c->ace > 0) g_access(c, -c->acr, -c->acw, -c->ace); if (delay) { /* Destroy consumer after it's tasted */ g_post_event(delay_destroy_consumer, c, M_WAITOK, NULL); } else { g_detach(c); g_destroy_consumer(c); } } /* * Destroy geom - called internally * See g_virstor_destroy_geom for the other one */ static int virstor_geom_destroy(struct g_virstor_softc *sc, boolean_t force, boolean_t delay) { struct g_provider *pp; struct g_geom *gp; u_int n; g_topology_assert(); if (sc == NULL) return (ENXIO); pp = sc->provider; if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { LOG_MSG(force ? LVL_WARNING : LVL_ERROR, "Device %s is still open.", pp->name); if (!force) return (EBUSY); } for (n = 0; n < sc->n_components; n++) { if (sc->components[n].gcons != NULL) remove_component(sc, &sc->components[n], delay); } gp = sc->geom; gp->softc = NULL; KASSERT(sc->provider == NULL, ("Provider still exists for %s", gp->name)); /* XXX: This might or might not work, since we're called with * the topology lock held. Also, it might panic the kernel if * the error'd BIO is in softupdates code. */ mtx_lock(&sc->delayed_bio_q_mtx); while (!STAILQ_EMPTY(&sc->delayed_bio_q)) { struct g_virstor_bio_q *bq; bq = STAILQ_FIRST(&sc->delayed_bio_q); bq->bio->bio_error = ENOSPC; g_io_deliver(bq->bio, EIO); STAILQ_REMOVE_HEAD(&sc->delayed_bio_q, linkage); free(bq, M_GVIRSTOR); } mtx_unlock(&sc->delayed_bio_q_mtx); mtx_destroy(&sc->delayed_bio_q_mtx); free(sc->map, M_GVIRSTOR); free(sc->components, M_GVIRSTOR); bzero(sc, sizeof *sc); free(sc, M_GVIRSTOR); pp = LIST_FIRST(&gp->provider); /* We only offer one provider */ if (pp == NULL || (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)) LOG_MSG(LVL_DEBUG, "Device %s destroyed", gp->name); g_wither_geom(gp, ENXIO); return (0); } /* * Utility function: read metadata & decode. Wants topology lock to be * held. */ static int read_metadata(struct g_consumer *cp, struct g_virstor_metadata *md) { struct g_provider *pp; char *buf; int error; g_topology_assert(); error = g_access(cp, 1, 0, 0); if (error != 0) return (error); pp = cp->provider; g_topology_unlock(); buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, &error); g_topology_lock(); g_access(cp, -1, 0, 0); if (buf == NULL) return (error); virstor_metadata_decode(buf, md); g_free(buf); return (0); } /** * Utility function: encode & write metadata. Assumes topology lock is * held. * * There is no useful way of recovering from errors in this function, * not involving panicking the kernel. If the metadata cannot be written * the most we can do is notify the operator and hope he spots it and * replaces the broken drive. */ static void write_metadata(struct g_consumer *cp, struct g_virstor_metadata *md) { struct g_provider *pp; char *buf; int error; KASSERT(cp != NULL && md != NULL && cp->provider != NULL, ("Something's fishy in %s", __func__)); LOG_MSG(LVL_DEBUG, "Writing metadata on %s", cp->provider->name); g_topology_assert(); error = g_access(cp, 0, 1, 0); if (error != 0) { LOG_MSG(LVL_ERROR, "g_access(0,1,0) failed for %s: %d", cp->provider->name, error); return; } pp = cp->provider; buf = malloc(pp->sectorsize, M_GVIRSTOR, M_WAITOK); + bzero(buf, pp->sectorsize); virstor_metadata_encode(md, buf); g_topology_unlock(); error = g_write_data(cp, pp->mediasize - pp->sectorsize, buf, pp->sectorsize); g_topology_lock(); g_access(cp, 0, -1, 0); free(buf, M_GVIRSTOR); if (error != 0) LOG_MSG(LVL_ERROR, "Error %d writing metadata to %s", error, cp->provider->name); } /* * Creates a new instance of this GEOM class, initialise softc */ static struct g_geom * create_virstor_geom(struct g_class *mp, struct g_virstor_metadata *md) { struct g_geom *gp; struct g_virstor_softc *sc; LOG_MSG(LVL_DEBUG, "Creating geom instance for %s (id=%u)", md->md_name, md->md_id); if (md->md_count < 1 || md->md_chunk_size < 1 || md->md_virsize < md->md_chunk_size) { /* This is bogus configuration, and probably means data is * somehow corrupted. Panic, maybe? */ LOG_MSG(LVL_ERROR, "Nonsensical metadata information for %s", md->md_name); return (NULL); } /* Check if it's already created */ LIST_FOREACH(gp, &mp->geom, geom) { sc = gp->softc; if (sc != NULL && strcmp(sc->geom->name, md->md_name) == 0) { LOG_MSG(LVL_WARNING, "Geom %s already exists", md->md_name); if (sc->id != md->md_id) { LOG_MSG(LVL_ERROR, "Some stale or invalid components " "exist for virstor device named %s. " "You will need to all stale " "components and maybe reconfigure " "the virstor device. Tune " "kern.geom.virstor.debug sysctl up " "for more information.", sc->geom->name); } return (NULL); } } gp = g_new_geomf(mp, "%s", md->md_name); gp->softc = NULL; /* to circumevent races that test softc */ gp->start = g_virstor_start; gp->spoiled = g_virstor_orphan; gp->orphan = g_virstor_orphan; gp->access = g_virstor_access; gp->dumpconf = g_virstor_dumpconf; sc = malloc(sizeof(*sc), M_GVIRSTOR, M_WAITOK | M_ZERO); sc->id = md->md_id; sc->n_components = md->md_count; sc->components = malloc(sizeof(struct g_virstor_component) * md->md_count, M_GVIRSTOR, M_WAITOK | M_ZERO); sc->chunk_size = md->md_chunk_size; sc->virsize = md->md_virsize; STAILQ_INIT(&sc->delayed_bio_q); mtx_init(&sc->delayed_bio_q_mtx, "gvirstor_delayed_bio_q_mtx", "gvirstor", MTX_DEF | MTX_RECURSE); sc->geom = gp; sc->provider = NULL; /* virstor_check_and_run will create it */ gp->softc = sc; LOG_MSG(LVL_ANNOUNCE, "Device %s created", sc->geom->name); return (gp); } /* * Add provider to a GEOM class instance */ static int add_provider_to_geom(struct g_virstor_softc *sc, struct g_provider *pp, struct g_virstor_metadata *md) { struct g_virstor_component *component; struct g_consumer *cp, *fcp; struct g_geom *gp; int error; if (md->no >= sc->n_components) return (EINVAL); /* "Current" compontent */ component = &(sc->components[md->no]); if (component->gcons != NULL) return (EEXIST); gp = sc->geom; fcp = LIST_FIRST(&gp->consumer); cp = g_new_consumer(gp); error = g_attach(cp, pp); if (error != 0) { g_destroy_consumer(cp); return (error); } if (fcp != NULL) { if (fcp->provider->sectorsize != pp->sectorsize) { /* TODO: this can be made to work */ LOG_MSG(LVL_ERROR, "Provider %s of %s has invalid " "sector size (%d)", pp->name, sc->geom->name, pp->sectorsize); return (EINVAL); } if (fcp->acr > 0 || fcp->acw || fcp->ace > 0) { /* Replicate access permissions from first "live" consumer * to the new one */ error = g_access(cp, fcp->acr, fcp->acw, fcp->ace); if (error != 0) { g_detach(cp); g_destroy_consumer(cp); return (error); } } } /* Bring up a new component */ cp->private = component; component->gcons = cp; component->sc = sc; component->index = md->no; component->chunk_count = md->chunk_count; component->chunk_next = md->chunk_next; component->chunk_reserved = md->chunk_reserved; component->flags = md->flags; LOG_MSG(LVL_DEBUG, "%s attached to %s", pp->name, sc->geom->name); virstor_check_and_run(sc); return (0); } /* * Check if everything's ready to create the geom provider & device entry, * create and start provider. * Called ultimately by .taste, from g_event thread */ static void virstor_check_and_run(struct g_virstor_softc *sc) { off_t off; size_t n, count; int index; int error; if (virstor_valid_components(sc) != sc->n_components) return; if (virstor_valid_components(sc) == 0) { /* This is actually a candidate for panic() */ LOG_MSG(LVL_ERROR, "No valid components for %s?", sc->provider->name); return; } sc->sectorsize = sc->components[0].gcons->provider->sectorsize; /* Initialise allocation map from the first consumer */ sc->chunk_count = sc->virsize / sc->chunk_size; if (sc->chunk_count * (off_t)sc->chunk_size != sc->virsize) { LOG_MSG(LVL_WARNING, "Device %s truncated to %ju bytes", sc->provider->name, sc->chunk_count * (off_t)sc->chunk_size); } sc->map_size = sc->chunk_count * sizeof *(sc->map); /* The following allocation is in order of 4MB - 8MB */ sc->map = malloc(sc->map_size, M_GVIRSTOR, M_WAITOK); KASSERT(sc->map != NULL, ("%s: Memory allocation error (%zu bytes) for %s", __func__, sc->map_size, sc->provider->name)); sc->map_sectors = sc->map_size / sc->sectorsize; count = 0; for (n = 0; n < sc->n_components; n++) count += sc->components[n].chunk_count; LOG_MSG(LVL_INFO, "Device %s has %zu physical chunks and %zu virtual " "(%zu KB chunks)", sc->geom->name, count, sc->chunk_count, sc->chunk_size / 1024); error = g_access(sc->components[0].gcons, 1, 0, 0); if (error != 0) { LOG_MSG(LVL_ERROR, "Cannot acquire read access for %s to " "read allocation map for %s", sc->components[0].gcons->provider->name, sc->geom->name); return; } /* Read in the allocation map */ LOG_MSG(LVL_DEBUG, "Reading map for %s from %s", sc->geom->name, sc->components[0].gcons->provider->name); off = count = n = 0; while (count < sc->map_size) { struct g_virstor_map_entry *mapbuf; size_t bs; bs = MIN(MAXPHYS, sc->map_size - count); if (bs % sc->sectorsize != 0) { /* Check for alignment errors */ bs = rounddown(bs, sc->sectorsize); if (bs == 0) break; LOG_MSG(LVL_ERROR, "Trouble: map is not sector-aligned " "for %s on %s", sc->geom->name, sc->components[0].gcons->provider->name); } mapbuf = g_read_data(sc->components[0].gcons, off, bs, &error); if (mapbuf == NULL) { free(sc->map, M_GVIRSTOR); LOG_MSG(LVL_ERROR, "Error reading allocation map " "for %s from %s (offset %ju) (error %d)", sc->geom->name, sc->components[0].gcons->provider->name, off, error); return; } bcopy(mapbuf, &sc->map[n], bs); off += bs; count += bs; n += bs / sizeof *(sc->map); g_free(mapbuf); } g_access(sc->components[0].gcons, -1, 0, 0); LOG_MSG(LVL_DEBUG, "Read map for %s", sc->geom->name); /* find first component with allocatable chunks */ index = -1; for (n = 0; n < sc->n_components; n++) { if (sc->components[n].chunk_next < sc->components[n].chunk_count) { index = n; break; } } if (index == -1) /* not found? set it to the last component and handle it * later */ index = sc->n_components - 1; if (index >= sc->n_components - g_virstor_component_watermark - 1) { LOG_MSG(LVL_WARNING, "Device %s running out of components " "(%d/%u: %s)", sc->geom->name, index+1, sc->n_components, sc->components[index].gcons->provider->name); } sc->curr_component = index; if (sc->components[index].chunk_next >= sc->components[index].chunk_count - g_virstor_chunk_watermark) { LOG_MSG(LVL_WARNING, "Component %s of %s is running out of free space " "(%u chunks left)", sc->components[index].gcons->provider->name, sc->geom->name, sc->components[index].chunk_count - sc->components[index].chunk_next); } sc->me_per_sector = sc->sectorsize / sizeof *(sc->map); if (sc->sectorsize % sizeof *(sc->map) != 0) { LOG_MSG(LVL_ERROR, "%s: Map entries don't fit exactly in a sector (%s)", __func__, sc->geom->name); return; } /* Recalculate allocated chunks in components & at the same time * verify map data is sane. We could trust metadata on this, but * we want to make sure. */ for (n = 0; n < sc->n_components; n++) sc->components[n].chunk_next = sc->components[n].chunk_reserved; for (n = 0; n < sc->chunk_count; n++) { if (sc->map[n].provider_no >= sc->n_components || sc->map[n].provider_chunk >= sc->components[sc->map[n].provider_no].chunk_count) { LOG_MSG(LVL_ERROR, "%s: Invalid entry %u in map for %s", __func__, (u_int)n, sc->geom->name); LOG_MSG(LVL_ERROR, "%s: provider_no: %u, n_components: %u" " provider_chunk: %u, chunk_count: %u", __func__, sc->map[n].provider_no, sc->n_components, sc->map[n].provider_chunk, sc->components[sc->map[n].provider_no].chunk_count); return; } if (sc->map[n].flags & VIRSTOR_MAP_ALLOCATED) sc->components[sc->map[n].provider_no].chunk_next++; } sc->provider = g_new_providerf(sc->geom, "virstor/%s", sc->geom->name); sc->provider->sectorsize = sc->sectorsize; sc->provider->mediasize = sc->virsize; g_error_provider(sc->provider, 0); LOG_MSG(LVL_INFO, "%s activated", sc->provider->name); LOG_MSG(LVL_DEBUG, "%s starting with current component %u, starting " "chunk %u", sc->provider->name, sc->curr_component, sc->components[sc->curr_component].chunk_next); } /* * Returns count of active providers in this geom instance */ static u_int virstor_valid_components(struct g_virstor_softc *sc) { unsigned int nc, i; nc = 0; KASSERT(sc != NULL, ("%s: softc is NULL", __func__)); KASSERT(sc->components != NULL, ("%s: sc->components is NULL", __func__)); for (i = 0; i < sc->n_components; i++) if (sc->components[i].gcons != NULL) nc++; return (nc); } /* * Called when the consumer gets orphaned (?) */ static void g_virstor_orphan(struct g_consumer *cp) { struct g_virstor_softc *sc; struct g_virstor_component *comp; struct g_geom *gp; g_topology_assert(); gp = cp->geom; sc = gp->softc; if (sc == NULL) return; comp = cp->private; KASSERT(comp != NULL, ("%s: No component in private part of consumer", __func__)); remove_component(sc, comp, FALSE); if (virstor_valid_components(sc) == 0) virstor_geom_destroy(sc, TRUE, FALSE); } /* * Called to notify geom when it's been opened, and for what intent */ static int g_virstor_access(struct g_provider *pp, int dr, int dw, int de) { struct g_consumer *c; struct g_virstor_softc *sc; struct g_geom *gp; int error; KASSERT(pp != NULL, ("%s: NULL provider", __func__)); gp = pp->geom; KASSERT(gp != NULL, ("%s: NULL geom", __func__)); sc = gp->softc; if (sc == NULL) { /* It seems that .access can be called with negative dr,dw,dx * in this case but I want to check for myself */ LOG_MSG(LVL_WARNING, "access(%d, %d, %d) for %s", dr, dw, de, pp->name); /* This should only happen when geom is withered so * allow only negative requests */ KASSERT(dr <= 0 && dw <= 0 && de <= 0, ("%s: Positive access for %s", __func__, pp->name)); if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0) LOG_MSG(LVL_DEBUG, "Device %s definitely destroyed", pp->name); return (0); } /* Grab an exclusive bit to propagate on our consumers on first open */ if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0) de++; /* ... drop it on close */ if (pp->acr + dr == 0 && pp->acw + dw == 0 && pp->ace + de == 0) { de--; update_metadata(sc); /* Writes statistical information */ } error = ENXIO; LIST_FOREACH(c, &gp->consumer, consumer) { KASSERT(c != NULL, ("%s: consumer is NULL", __func__)); error = g_access(c, dr, dw, de); if (error != 0) { struct g_consumer *c2; /* Backout earlier changes */ LIST_FOREACH(c2, &gp->consumer, consumer) { if (c2 == c) /* all eariler components fixed */ return (error); g_access(c2, -dr, -dw, -de); } } } return (error); } /* * Generate XML dump of current state */ static void g_virstor_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp, struct g_provider *pp) { struct g_virstor_softc *sc; g_topology_assert(); sc = gp->softc; if (sc == NULL || pp != NULL) return; if (cp != NULL) { /* For each component */ struct g_virstor_component *comp; comp = cp->private; if (comp == NULL) return; sbuf_printf(sb, "%s%u\n", indent, comp->index); sbuf_printf(sb, "%s%u\n", indent, comp->chunk_count); sbuf_printf(sb, "%s%u\n", indent, comp->chunk_next); sbuf_printf(sb, "%s%u\n", indent, comp->chunk_reserved); sbuf_printf(sb, "%s%u%%\n", indent, comp->chunk_next > 0 ? 100 - ((comp->chunk_next + comp->chunk_reserved) * 100) / comp->chunk_count : 100); } else { /* For the whole thing */ u_int count, used, i; off_t size; count = used = size = 0; for (i = 0; i < sc->n_components; i++) { if (sc->components[i].gcons != NULL) { count += sc->components[i].chunk_count; used += sc->components[i].chunk_next + sc->components[i].chunk_reserved; size += sc->components[i].gcons-> provider->mediasize; } } sbuf_printf(sb, "%s" "Components=%u, Online=%u\n", indent, sc->n_components, virstor_valid_components(sc)); sbuf_printf(sb, "%s%u%% physical free\n", indent, 100-(used * 100) / count); sbuf_printf(sb, "%s%zu\n", indent, sc->chunk_size); sbuf_printf(sb, "%s%u%%\n", indent, used > 0 ? 100 - (used * 100) / count : 100); sbuf_printf(sb, "%s%u\n", indent, count); sbuf_printf(sb, "%s%zu\n", indent, sc->chunk_count); sbuf_printf(sb, "%s%zu%%\n", indent, (count * 100) / sc->chunk_count); sbuf_printf(sb, "%s%jd\n", indent, size); sbuf_printf(sb, "%s%jd\n", indent, sc->virsize); } } /* * GEOM .done handler * Can't use standard handler because one requested IO may * fork into additional data IOs */ static void g_virstor_done(struct bio *b) { struct g_virstor_softc *sc; struct bio *parent_b; parent_b = b->bio_parent; sc = parent_b->bio_to->geom->softc; if (b->bio_error != 0) { LOG_MSG(LVL_ERROR, "Error %d for offset=%ju, length=%ju, %s", b->bio_error, b->bio_offset, b->bio_length, b->bio_to->name); if (parent_b->bio_error == 0) parent_b->bio_error = b->bio_error; } parent_b->bio_inbed++; parent_b->bio_completed += b->bio_completed; if (parent_b->bio_children == parent_b->bio_inbed) { parent_b->bio_completed = parent_b->bio_length; g_io_deliver(parent_b, parent_b->bio_error); } g_destroy_bio(b); } /* * I/O starts here * Called in g_down thread */ static void g_virstor_start(struct bio *b) { struct g_virstor_softc *sc; struct g_virstor_component *comp; struct bio *cb; struct g_provider *pp; char *addr; off_t offset, length; struct bio_queue_head bq; size_t chunk_size; /* cached for convenience */ u_int count; pp = b->bio_to; sc = pp->geom->softc; KASSERT(sc != NULL, ("%s: no softc (error=%d, device=%s)", __func__, b->bio_to->error, b->bio_to->name)); LOG_REQ(LVL_MOREDEBUG, b, "%s", __func__); switch (b->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: break; default: g_io_deliver(b, EOPNOTSUPP); return; } LOG_MSG(LVL_DEBUG2, "BIO arrived, size=%ju", b->bio_length); bioq_init(&bq); chunk_size = sc->chunk_size; addr = b->bio_data; offset = b->bio_offset; /* virtual offset and length */ length = b->bio_length; while (length > 0) { size_t chunk_index, in_chunk_offset, in_chunk_length; struct virstor_map_entry *me; chunk_index = offset / chunk_size; /* round downwards */ in_chunk_offset = offset % chunk_size; in_chunk_length = min(length, chunk_size - in_chunk_offset); LOG_MSG(LVL_DEBUG, "Mapped %s(%ju, %ju) to (%zu,%zu,%zu)", b->bio_cmd == BIO_READ ? "R" : "W", offset, length, chunk_index, in_chunk_offset, in_chunk_length); me = &sc->map[chunk_index]; if (b->bio_cmd == BIO_READ || b->bio_cmd == BIO_DELETE) { if ((me->flags & VIRSTOR_MAP_ALLOCATED) == 0) { /* Reads from unallocated chunks return zeroed * buffers */ if (b->bio_cmd == BIO_READ) bzero(addr, in_chunk_length); } else { comp = &sc->components[me->provider_no]; cb = g_clone_bio(b); if (cb == NULL) { bioq_dismantle(&bq); if (b->bio_error == 0) b->bio_error = ENOMEM; g_io_deliver(b, b->bio_error); return; } cb->bio_to = comp->gcons->provider; cb->bio_done = g_virstor_done; cb->bio_offset = (off_t)me->provider_chunk * (off_t)chunk_size + in_chunk_offset; cb->bio_length = in_chunk_length; cb->bio_data = addr; cb->bio_caller1 = comp; bioq_disksort(&bq, cb); } } else { /* handle BIO_WRITE */ KASSERT(b->bio_cmd == BIO_WRITE, ("%s: Unknown command %d", __func__, b->bio_cmd)); if ((me->flags & VIRSTOR_MAP_ALLOCATED) == 0) { /* We have a virtual chunk, represented by * the "me" entry, but it's not yet allocated * (tied to) a physical chunk. So do it now. */ struct virstor_map_entry *data_me; u_int phys_chunk, comp_no; off_t s_offset; int error; error = allocate_chunk(sc, &comp, &comp_no, &phys_chunk); if (error != 0) { /* We cannot allocate a physical chunk * to satisfy this request, so we'll * delay it to when we can... * XXX: this will prevent the fs from * being umounted! */ struct g_virstor_bio_q *biq; biq = malloc(sizeof *biq, M_GVIRSTOR, M_NOWAIT); if (biq == NULL) { bioq_dismantle(&bq); if (b->bio_error == 0) b->bio_error = ENOMEM; g_io_deliver(b, b->bio_error); return; } biq->bio = b; mtx_lock(&sc->delayed_bio_q_mtx); STAILQ_INSERT_TAIL(&sc->delayed_bio_q, biq, linkage); mtx_unlock(&sc->delayed_bio_q_mtx); LOG_MSG(LVL_WARNING, "Delaying BIO " "(size=%ju) until free physical " "space can be found on %s", b->bio_length, sc->provider->name); return; } LOG_MSG(LVL_DEBUG, "Allocated chunk %u on %s " "for %s", phys_chunk, comp->gcons->provider->name, sc->provider->name); me->provider_no = comp_no; me->provider_chunk = phys_chunk; me->flags |= VIRSTOR_MAP_ALLOCATED; cb = g_clone_bio(b); if (cb == NULL) { me->flags &= ~VIRSTOR_MAP_ALLOCATED; me->provider_no = 0; me->provider_chunk = 0; bioq_dismantle(&bq); if (b->bio_error == 0) b->bio_error = ENOMEM; g_io_deliver(b, b->bio_error); return; } /* The allocation table is stored continuously * at the start of the drive. We need to * calculate the offset of the sector that holds * this map entry both on the drive and in the * map array. * sc_offset will end up pointing to the drive * sector. */ s_offset = chunk_index * sizeof *me; s_offset = rounddown(s_offset, sc->sectorsize); /* data_me points to map entry sector * in memory (analogous to offset) */ data_me = &sc->map[rounddown(chunk_index, sc->me_per_sector)]; /* Commit sector with map entry to storage */ cb->bio_to = sc->components[0].gcons->provider; cb->bio_done = g_virstor_done; cb->bio_offset = s_offset; cb->bio_data = (char *)data_me; cb->bio_length = sc->sectorsize; cb->bio_caller1 = &sc->components[0]; bioq_disksort(&bq, cb); } comp = &sc->components[me->provider_no]; cb = g_clone_bio(b); if (cb == NULL) { bioq_dismantle(&bq); if (b->bio_error == 0) b->bio_error = ENOMEM; g_io_deliver(b, b->bio_error); return; } /* Finally, handle the data */ cb->bio_to = comp->gcons->provider; cb->bio_done = g_virstor_done; cb->bio_offset = (off_t)me->provider_chunk*(off_t)chunk_size + in_chunk_offset; cb->bio_length = in_chunk_length; cb->bio_data = addr; cb->bio_caller1 = comp; bioq_disksort(&bq, cb); } addr += in_chunk_length; length -= in_chunk_length; offset += in_chunk_length; } /* Fire off bio's here */ count = 0; for (cb = bioq_first(&bq); cb != NULL; cb = bioq_first(&bq)) { bioq_remove(&bq, cb); LOG_REQ(LVL_MOREDEBUG, cb, "Firing request"); comp = cb->bio_caller1; cb->bio_caller1 = NULL; LOG_MSG(LVL_DEBUG, " firing bio, offset=%ju, length=%ju", cb->bio_offset, cb->bio_length); g_io_request(cb, comp->gcons); count++; } if (count == 0) { /* We handled everything locally */ b->bio_completed = b->bio_length; g_io_deliver(b, 0); } } /* * Allocate a chunk from a physical provider. Returns physical component, * chunk index relative to the component and the component's index. */ static int allocate_chunk(struct g_virstor_softc *sc, struct g_virstor_component **comp, u_int *comp_no_p, u_int *chunk) { u_int comp_no; KASSERT(sc->curr_component < sc->n_components, ("%s: Invalid curr_component: %u", __func__, sc->curr_component)); comp_no = sc->curr_component; *comp = &sc->components[comp_no]; dump_component(*comp); if ((*comp)->chunk_next >= (*comp)->chunk_count) { /* This component is full. Allocate next component */ if (comp_no >= sc->n_components-1) { LOG_MSG(LVL_ERROR, "All physical space allocated for %s", sc->geom->name); return (-1); } (*comp)->flags &= ~VIRSTOR_PROVIDER_CURRENT; sc->curr_component = ++comp_no; *comp = &sc->components[comp_no]; if (comp_no >= sc->n_components - g_virstor_component_watermark-1) LOG_MSG(LVL_WARNING, "Device %s running out of components " "(switching to %u/%u: %s)", sc->geom->name, comp_no+1, sc->n_components, (*comp)->gcons->provider->name); /* Take care not to overwrite reserved chunks */ if ( (*comp)->chunk_reserved > 0 && (*comp)->chunk_next < (*comp)->chunk_reserved) (*comp)->chunk_next = (*comp)->chunk_reserved; (*comp)->flags |= VIRSTOR_PROVIDER_ALLOCATED | VIRSTOR_PROVIDER_CURRENT; dump_component(*comp); *comp_no_p = comp_no; *chunk = (*comp)->chunk_next++; } else { *comp_no_p = comp_no; *chunk = (*comp)->chunk_next++; } return (0); } /* Dump a component */ static void dump_component(struct g_virstor_component *comp) { if (g_virstor_debug < LVL_DEBUG2) return; printf("Component %d: %s\n", comp->index, comp->gcons->provider->name); printf(" chunk_count: %u\n", comp->chunk_count); printf(" chunk_next: %u\n", comp->chunk_next); printf(" flags: %u\n", comp->flags); } #if 0 /* Dump a map entry */ static void dump_me(struct virstor_map_entry *me, unsigned int nr) { if (g_virstor_debug < LVL_DEBUG) return; printf("VIRT. CHUNK #%d: ", nr); if ((me->flags & VIRSTOR_MAP_ALLOCATED) == 0) printf("(unallocated)\n"); else printf("allocated at provider %u, provider_chunk %u\n", me->provider_no, me->provider_chunk); } #endif /* * Dismantle bio_queue and destroy its components */ static void bioq_dismantle(struct bio_queue_head *bq) { struct bio *b; for (b = bioq_first(bq); b != NULL; b = bioq_first(bq)) { bioq_remove(bq, b); g_destroy_bio(b); } } /* * The function that shouldn't be called. * When this is called, the stack is already garbled because of * argument mismatch. There's nothing to do now but panic, which is * accidentally the whole purpose of this function. * Motivation: to guard from accidentally calling geom methods when * they shouldn't be called. (see g_..._taste) */ static void invalid_call(void) { panic("invalid_call() has just been called. Something's fishy here."); } DECLARE_GEOM_CLASS(g_virstor_class, g_virstor); /* Let there be light */ Index: stable/11 =================================================================== --- stable/11 (revision 330725) +++ stable/11 (revision 330726) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r323314,323338,328849