diff --git a/sbin/gconcat/Makefile b/sbin/gconcat/Makefile new file mode 100644 index 000000000000..d2d5d7d5a075 --- /dev/null +++ b/sbin/gconcat/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +PROG= gconcat +#MAN= gconcat.8 +NOMAN= not_yet +DPADD= ${LIBGEOM} +LDADD= -lgeom +WARNS?= 6 +CFLAGS+=-I${.CURDIR}/../../sys + +.include diff --git a/sbin/gconcat/gconcat.c b/sbin/gconcat/gconcat.c new file mode 100644 index 000000000000..0b1644bbfe2c --- /dev/null +++ b/sbin/gconcat/gconcat.c @@ -0,0 +1,525 @@ +/*- + * Copyright (c) 2004 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +enum { + UNSET, + CREATE, + DESTROY, + LABEL, + CLEAR, + LIST +} action = UNSET; + +static const char *comm; +static int force = 0; +static int verbose = 0; + + +static void +usage(void) +{ + fprintf(stderr, + "usage: %s create [-v] name dev1 dev2 [dev3 [...]]\n" + " %s destroy [-fv] name\n" + " %s label [-v] name dev1 dev2 [dev3 [...]]\n" + " %s clear [-v] dev1 dev2 [dev3 [...]]\n" + " %s list\n", + comm, comm, comm, comm, comm); + exit(EXIT_FAILURE); +} + +static void +load_module(void) +{ + + if (modfind("g_concat") < 0) { + /* Not present in kernel, try loading it. */ + if (kldload("geom_concat") < 0 || modfind("g_concat") < 0) { + if (errno != EEXIST) { + errx(EXIT_FAILURE, + "geom_concat module not available!"); + } + } + } +} + +/* + * Remove ".concat" suffix if exists. + */ +static void +cut_suffix(char *name) +{ + + for (; *name != '\0'; name++) { + if (strcmp(name, ".concat") == 0) { + *name = '\0'; + return; + } + } +} + +/* + * Add ".concat" suffix if not exists. + */ +static char * +add_suffix(char *name) +{ + char *newname, *s; + size_t size; + + for (s = name; *s != '\0'; s++) { + if (strcmp(s, ".concat") == 0) { + newname = strdup(name); + if (newname == NULL) + errx(EXIT_FAILURE, "no memory"); + return (newname); + } + } + /* 8 == strlen(".concat") + 1 */ + size = strlen(name) + 8; + newname = malloc(size); + if (newname == NULL) + errx(EXIT_FAILURE, "no memory"); + snprintf(newname, size, "%s.concat", name); + + return (newname); +} + +static struct gctl_req * +init_gctl(const char *name, const char *verb) +{ + struct gctl_req *grq; + + grq = gctl_get_handle(); + gctl_ro_param(grq, "verb", -1, verb); + gctl_ro_param(grq, "class", -1, G_CONCAT_CLASS_NAME); + if (name != NULL) + gctl_ro_param(grq, "geom", -1, name); + + return (grq); +} + +static void +concat_create(int argc, char *argv[]) +{ + struct g_concat_metadata md; + struct gctl_req *grq; + const char *errstr; + char buf[256], *s; + unsigned i; + + if (argc < 2) + usage(); + + cut_suffix(argv[0]); + strlcpy(md.md_name, argv[0], sizeof(md.md_name)); + md.md_id = arc4random(); + + argc--; + argv++; + + load_module(); + + if (verbose) + printf("Creating device %s:", md.md_name); + + md.md_all = argc; + grq = init_gctl(NULL, "create device"); + gctl_ro_param(grq, "metadata", sizeof(md), &md); + for (i = 0; i < (unsigned)argc; i++) { + snprintf(buf, sizeof(buf), "disk%u", i); + s = argv[i]; + if (strncmp(s, _PATH_DEV, strlen(_PATH_DEV)) == 0) + s += strlen(_PATH_DEV); + if (verbose) + printf(" %s", s); + gctl_ro_param(grq, buf, -1, s); + } + if (verbose) + printf(".\n"); + errstr = gctl_issue(grq); + if (errstr == NULL) { + gctl_free(grq); + if (verbose) + printf("Done.\n"); + exit(EXIT_SUCCESS); + } + + fprintf(stderr, "%s\n", errstr); + gctl_free(grq); + exit(EXIT_FAILURE); +} + +static void +concat_destroy(int argc, char *argv[]) +{ + struct gctl_req *grq; + const char *errstr; + char *name; + + if (argc != 1) + usage(); + + name = add_suffix(argv[0]); + + if (verbose) + printf("Destroying device %s.\n", name); + + grq = init_gctl(name, "destroy device"); + gctl_ro_param(grq, "force", sizeof(force), &force); + errstr = gctl_issue(grq); + free(name); + if (errstr == NULL) { + gctl_free(grq); + if (verbose) + printf("Done.\n"); + exit(EXIT_SUCCESS); + } + + fprintf(stderr, "%s\n", errstr); + gctl_free(grq); + exit(EXIT_FAILURE); +} + +static void +concat_label(int argc, char *argv[]) +{ + struct g_concat_metadata md; + unsigned sectorsize; + off_t mediasize; + u_char *sector; + int fd, i, status; + + if (argc < 2) + usage(); + + load_module(); + + strlcpy(md.md_magic, G_CONCAT_MAGIC, sizeof(md.md_magic)); + md.md_version = G_CONCAT_VERSION; + strlcpy(md.md_name, argv[0], sizeof(md.md_name)); + md.md_id = arc4random(); + + argc--; + argv++; + status = EXIT_SUCCESS; + + md.md_all = argc; + for (i = 0; i < argc; i++) { + fd = open(argv[i], O_WRONLY); + if (fd == -1) { + fprintf(stderr, "Can't open %s: %s.\n", argv[i], + strerror(errno)); + status = EXIT_FAILURE; + continue; + } + if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) { + fprintf(stderr, "Can't get media size for %s: %s.\n", + argv[i], strerror(errno)); + status = EXIT_FAILURE; + continue; + } + if (ioctl(fd, DIOCGSECTORSIZE, §orsize) < 0) { + fprintf(stderr, "Can't get sector size for %s: %s.\n", + argv[i], strerror(errno)); + status = EXIT_FAILURE; + continue; + } + assert(sectorsize >= sizeof(md)); + sector = malloc(sectorsize); + if (sector == NULL) { + fprintf(stderr, "Can't allocate memory for %s: %s.\n", + argv[i], strerror(errno)); + status = EXIT_FAILURE; + continue; + } + md.md_no = i; + concat_metadata_encode(&md, sector); + if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != + (ssize_t)sectorsize) { + fprintf(stderr, "Problems with storing metadata on %s: " + "%s.\n", argv[i], strerror(errno)); + status = EXIT_FAILURE; + free(sector); + continue; + } + free(sector); + close(fd); + if (verbose) + printf("Metadata value stored on %s.\n", argv[i]); + } + + exit(status); +} + +static void +concat_clear(int argc, char *argv[]) +{ + struct g_concat_metadata md; + unsigned sectorsize; + off_t mediasize; + u_char *sector; + int fd, i, status; + + if (argc < 1) + usage(); + + status = EXIT_SUCCESS; + + md.md_all = argc; + for (i = 0; i < argc; i++) { + fd = open(argv[i], O_RDWR); + if (fd == -1) { + fprintf(stderr, "Can't open %s: %s.\n", argv[i], + strerror(errno)); + status = EXIT_FAILURE; + continue; + } + if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) { + fprintf(stderr, "Can't get media size for %s: %s.\n", + argv[i], strerror(errno)); + status = EXIT_FAILURE; + continue; + } + if (ioctl(fd, DIOCGSECTORSIZE, §orsize) < 0) { + fprintf(stderr, "Can't get sector size for %s: %s.\n", + argv[i], strerror(errno)); + status = EXIT_FAILURE; + continue; + } + assert(sectorsize >= sizeof(md)); + sector = malloc(sectorsize); + if (sector == NULL) { + fprintf(stderr, "Can't allocate memory for %s: %s.\n", + argv[i], strerror(errno)); + status = EXIT_FAILURE; + continue; + } + if (pread(fd, sector, sectorsize, mediasize - sectorsize) != + (ssize_t)sectorsize) { + fprintf(stderr, "Problems with reading metadata from " + "%s: %s.\n", argv[i], strerror(errno)); + status = EXIT_FAILURE; + free(sector); + continue; + } + concat_metadata_decode(sector, &md); + if (strcmp(md.md_magic, G_CONCAT_MAGIC) != 0) { + fprintf(stderr, "Cannot find metadata to clear on " + "%s.\n", argv[i]); + status = EXIT_FAILURE; + free(sector); + continue; + } + bzero(sector, sectorsize); + if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != + (ssize_t)sectorsize) { + fprintf(stderr, "Problems with clearing metadata on " + "%s: %s.\n", argv[i], strerror(errno)); + status = EXIT_FAILURE; + free(sector); + continue; + } + free(sector); + close(fd); + if (verbose) + printf("Metadata cleared on %s.\n", argv[i]); + } + + exit(status); +} + +static struct gclass * +find_class(struct gmesh *mesh, const char *name) +{ + struct gclass *class; + + LIST_FOREACH(class, &mesh->class, class) { + if (strcmp(class->name, name) == 0) + return (class); + } + + return (NULL); +} + +static const char * +get_conf(struct ggeom *gp, const char *name) +{ + struct gconfig *conf; + + LIST_FOREACH(conf, &gp->config, config) { + if (strcmp(conf->name, name) == 0) + return (conf->val); + } + + return (NULL); +} + +static void +show_config(struct ggeom *gp) +{ + struct gprovider *pp; + struct gconsumer *cp; + + pp = LIST_FIRST(&gp->provider); + if (pp == NULL) + return; + + printf(" NAME: %s\n", pp->name); + printf(" id: %s\n", get_conf(gp, "id")); + printf(" type: %s\n", get_conf(gp, "type")); + printf(" mediasize: %jd\n", pp->mediasize); + printf("sectorsize: %u\n", pp->sectorsize); + printf(" mode: %s\n", pp->mode); + printf(" providers:"); + LIST_FOREACH(cp, &gp->consumer, consumer) { + printf(" %s", cp->provider->name); + } + printf("\n"); +} + +static void +concat_list(void) +{ + struct gmesh mesh; + struct gclass *class; + struct ggeom *gp; + int error; + + error = geom_gettree(&mesh); + if (error != 0) + exit(EXIT_FAILURE); + + class = find_class(&mesh, G_CONCAT_CLASS_NAME); + if (class == NULL) { + geom_deletetree(&mesh); + exit(EXIT_SUCCESS); + } + + LIST_FOREACH(gp, &class->geom, geom) { + show_config(gp); + } + + geom_deletetree(&mesh); + exit(EXIT_SUCCESS); +} + +int +main(int argc, char *argv[]) +{ + int ch; + + comm = basename(argv[0]); + + if (argc < 2) + usage(); + + if (strcasecmp(argv[1], "create") == 0) + action = CREATE; + else if (strcasecmp(argv[1], "destroy") == 0) + action = DESTROY; + else if (strcasecmp(argv[1], "label") == 0) + action = LABEL; + else if (strcasecmp(argv[1], "clear") == 0) + action = CLEAR; + else if (strcasecmp(argv[1], "list") == 0) + action = LIST; + else + usage(); + argv++; + argc--; + + while ((ch = getopt(argc, argv, "fv")) != -1) { + switch (ch) { + case 'f': + if (action != DESTROY) + usage(); + force = 1; + break; + case 'v': + /* -v options is only not permited while listing */ + if (action == LIST) + usage(); + verbose = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + switch (action) { + case CREATE: + concat_create(argc, argv); + /* NOTREACHED */ + break; + case DESTROY: + concat_destroy(argc, argv); + /* NOTREACHED */ + break; + case LABEL: + concat_label(argc, argv); + /* NOTREACHED */ + break; + case CLEAR: + concat_clear(argc, argv); + /* NOTREACHED */ + break; + case LIST: + concat_list(); + /* NOTREACHED */ + break; + case UNSET: + default: + usage(); + /* NOTREACHED */ + } + + exit(EXIT_SUCCESS); +}