/*- * Copyright (c) 2007, 2008 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/geom.h" #include "misc/subr.h" #ifdef STATIC_GEOM_CLASSES #define PUBSYM(x) gpart_##x #else #define PUBSYM(x) x #endif uint32_t PUBSYM(lib_version) = G_LIB_VERSION; uint32_t PUBSYM(version) = 0; static char autofill[] = "*"; static char optional[] = ""; static char flags[] = "C"; static char bootcode_param[] = "bootcode"; static char index_param[] = "index"; static char partcode_param[] = "partcode"; static void gpart_bootcode(struct gctl_req *, unsigned int); static void gpart_issue(struct gctl_req *, unsigned int); static void gpart_show(struct gctl_req *, unsigned int); struct g_command PUBSYM(class_commands)[] = { { "add", 0, gpart_issue, { { 'b', "start", autofill, G_TYPE_ASCLBA }, { 's', "size", autofill, G_TYPE_ASCLBA }, { 't', "type", NULL, G_TYPE_STRING }, { 'i', index_param, optional, G_TYPE_ASCNUM }, { 'l', "label", optional, G_TYPE_STRING }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, { "bootcode", 0, gpart_bootcode, { { 'b', bootcode_param, optional, G_TYPE_STRING }, { 'p', partcode_param, optional, G_TYPE_STRING }, { 'i', index_param, optional, G_TYPE_ASCNUM }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, { "commit", 0, gpart_issue, G_NULL_OPTS, "geom", NULL }, { "create", 0, gpart_issue, { { 's', "scheme", NULL, G_TYPE_STRING }, { 'n', "entries", optional, G_TYPE_ASCNUM }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "provider", NULL }, { "delete", 0, gpart_issue, { { 'i', index_param, NULL, G_TYPE_ASCNUM }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, { "destroy", 0, gpart_issue, { { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, { "modify", 0, gpart_issue, { { 'i', index_param, NULL, G_TYPE_ASCNUM }, { 'l', "label", optional, G_TYPE_STRING }, { 't', "type", optional, G_TYPE_STRING }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, { "set", 0, gpart_issue, { { 'a', "attrib", NULL, G_TYPE_STRING }, { 'i', index_param, NULL, G_TYPE_ASCNUM }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, { "show", 0, gpart_show, { { 'l', "show_label", NULL, G_TYPE_BOOL }, { 'r', "show_rawtype", NULL, G_TYPE_BOOL }, G_OPT_SENTINEL }, NULL, "[-lr] [geom ...]" }, { "undo", 0, gpart_issue, G_NULL_OPTS, "geom", NULL }, { "unset", 0, gpart_issue, { { 'a', "attrib", NULL, G_TYPE_STRING }, { 'i', index_param, NULL, G_TYPE_ASCNUM }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, G_CMD_SENTINEL }; static struct gclass * find_class(struct gmesh *mesh, const char *name) { struct gclass *classp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) { if (strcmp(classp->lg_name, name) == 0) return (classp); } return (NULL); } static struct ggeom * find_geom(struct gclass *classp, const char *name) { struct ggeom *gp; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { if (strcmp(gp->lg_name, name) == 0) return (gp); } return (NULL); } static const char * find_geomcfg(struct ggeom *gp, const char *cfg) { struct gconfig *gc; LIST_FOREACH(gc, &gp->lg_config, lg_config) { if (!strcmp(gc->lg_name, cfg)) return (gc->lg_val); } return (NULL); } static const char * find_provcfg(struct gprovider *pp, const char *cfg) { struct gconfig *gc; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (!strcmp(gc->lg_name, cfg)) return (gc->lg_val); } return (NULL); } static struct gprovider * find_provider(struct ggeom *gp, unsigned long long minsector) { struct gprovider *pp, *bestpp; const char *s; unsigned long long sector, bestsector; bestpp = NULL; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "start"); if (s == NULL) { s = find_provcfg(pp, "offset"); sector = atoll(s) / pp->lg_sectorsize; } else sector = atoll(s); if (sector < minsector) continue; if (bestpp != NULL && sector >= bestsector) continue; bestpp = pp; bestsector = sector; } return (bestpp); } static const char * fmtsize(int64_t rawsz) { static char buf[5]; humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); return (buf); } static const char * fmtattrib(struct gprovider *pp) { static char buf[128]; struct gconfig *gc; u_int idx; buf[0] = '\0'; idx = 0; LIST_FOREACH(gc, &pp->lg_config, lg_config) { if (strcmp(gc->lg_name, "attrib") != 0) continue; idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s", (idx == 0) ? " [" : ",", gc->lg_val); } if (idx > 0) snprintf(buf + idx, sizeof(buf) - idx, "] "); return (buf); } static int gpart_autofill(struct gctl_req *req) { struct gmesh mesh; struct gclass *cp; struct ggeom *gp; struct gprovider *pp; unsigned long long first, last; unsigned long long size, start; unsigned long long lba, len, grade; const char *s; char *val; int error, has_size, has_start; s = gctl_get_ascii(req, "verb"); if (strcmp(s, "add") != 0) return (0); s = gctl_get_ascii(req, "size"); has_size = (*s == '*') ? 0 : 1; size = (has_size) ? (unsigned long long)atoll(s) : 0ULL; s = gctl_get_ascii(req, "start"); has_start = (*s == '*') ? 0 : 1; start = (has_start) ? (unsigned long long)atoll(s) : ~0ULL; /* No autofill necessary. */ if (has_size && has_start) return (0); error = geom_gettree(&mesh); if (error) return (error); cp = find_class(&mesh, gctl_get_ascii(req, "class")); gp = find_geom(cp, gctl_get_ascii(req, "geom")); first = atoll(find_geomcfg(gp, "first")); last = atoll(find_geomcfg(gp, "last")); grade = ~0ULL; while ((pp = find_provider(gp, first)) != NULL) { s = find_provcfg(pp, "start"); if (s == NULL) { s = find_provcfg(pp, "offset"); lba = atoll(s) / pp->lg_sectorsize; } else lba = atoll(s); if (first < lba) { /* Free space [first, lba> */ len = lba - first; if (has_size) { if (len >= size && len - size < grade) { start = first; grade = len - size; } } else if (has_start) { if (start >= first && start < lba) { size = lba - start; grade = start - first; } } else { if (grade == ~0ULL || len > size) { start = first; size = len; grade = 0; } } } s = find_provcfg(pp, "end"); if (s == NULL) { s = find_provcfg(pp, "length"); first = lba + atoll(s) / pp->lg_sectorsize; } else first = atoll(s) + 1; } if (first <= last) { /* Free space [first-last] */ len = last - first + 1; if (has_size) { if (len >= size && len - size < grade) { start = first; grade = len - size; } } else if (has_start) { if (start >= first && start <= last) { size = last - start + 1; grade = start - first; } } else { if (grade == ~0ULL || len > size) { start = first; size = len; grade = 0; } } } if (grade == ~0ULL) return (ENOSPC); if (!has_size) { asprintf(&val, "%llu", size); if (val == NULL) return (ENOMEM); gctl_change_param(req, "size", -1, val); } if (!has_start) { asprintf(&val, "%llu", start); if (val == NULL) return (ENOMEM); gctl_change_param(req, "start", -1, val); } return (0); } static void gpart_show_geom(struct ggeom *gp, const char *element) { struct gprovider *pp; const char *s, *scheme; unsigned long long first, last, sector, end; unsigned long long length, secsz; int idx, wblocks, wname; scheme = find_geomcfg(gp, "scheme"); s = find_geomcfg(gp, "first"); first = atoll(s); s = find_geomcfg(gp, "last"); last = atoll(s); wblocks = strlen(s); wname = strlen(gp->lg_name); pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; secsz = pp->lg_sectorsize; printf("=>%*llu %*llu %*s %s (%s)\n", wblocks, first, wblocks, (last - first + 1), wname, gp->lg_name, scheme, fmtsize(pp->lg_mediasize)); while ((pp = find_provider(gp, first)) != NULL) { s = find_provcfg(pp, "start"); if (s == NULL) { s = find_provcfg(pp, "offset"); sector = atoll(s) / secsz; } else sector = atoll(s); s = find_provcfg(pp, "end"); if (s == NULL) { s = find_provcfg(pp, "length"); length = atoll(s) / secsz; end = sector + length - 1; } else { end = atoll(s); length = end - sector + 1; } s = find_provcfg(pp, "index"); idx = atoi(s); if (first < sector) { printf(" %*llu %*llu %*s - free - (%s)\n", wblocks, first, wblocks, sector - first, wname, "", fmtsize((sector - first) * secsz)); } printf(" %*llu %*llu %*d %s %s (%s)\n", wblocks, sector, wblocks, length, wname, idx, find_provcfg(pp, element), fmtattrib(pp), fmtsize(pp->lg_mediasize)); first = end + 1; } if (first <= last) { length = last - first + 1; printf(" %*llu %*llu %*s - free - (%s)\n", wblocks, first, wblocks, length, wname, "", fmtsize(length * secsz)); } printf("\n"); } static int gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt) { if (!gctl_get_int(req, opt)) return (0); if (elt != NULL) errx(EXIT_FAILURE, "-l and -r are mutually exclusive"); return (1); } static void gpart_show(struct gctl_req *req, unsigned int fl __unused) { struct gmesh mesh; struct gclass *classp; struct ggeom *gp; const char *element, *name; int error, i, nargs; element = NULL; if (gpart_show_hasopt(req, "show_label", element)) element = "label"; if (gpart_show_hasopt(req, "show_rawtype", element)) element = "rawtype"; if (element == NULL) element = "type"; name = gctl_get_ascii(req, "class"); if (name == NULL) abort(); error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, name); if (classp == NULL) { geom_deletetree(&mesh); errx(EXIT_FAILURE, "Class %s not found.", name); } nargs = gctl_get_int(req, "nargs"); if (nargs > 0) { for (i = 0; i < nargs; i++) { name = gctl_get_ascii(req, "arg%d", i); gp = find_geom(classp, name); if (gp != NULL) gpart_show_geom(gp, element); else errx(EXIT_FAILURE, "No such geom: %s.", name); } } else { LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { gpart_show_geom(gp, element); } } geom_deletetree(&mesh); } static void * gpart_bootfile_read(const char *bootfile, ssize_t *size) { struct stat sb; void *code; int fd; if (stat(bootfile, &sb) == -1) err(EXIT_FAILURE, "%s", bootfile); if (!S_ISREG(sb.st_mode)) errx(EXIT_FAILURE, "%s: not a regular file", bootfile); if (sb.st_size == 0) errx(EXIT_FAILURE, "%s: empty file", bootfile); if (*size > 0 && sb.st_size >= *size) errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, *size); *size = sb.st_size; fd = open(bootfile, O_RDONLY); if (fd == -1) err(EXIT_FAILURE, "%s", bootfile); code = malloc(*size); if (code == NULL) err(EXIT_FAILURE, NULL); if (read(fd, code, *size) != *size) err(EXIT_FAILURE, "%s", bootfile); close(fd); return (code); } static void gpart_write_partcode(struct gctl_req *req, int idx, void *code, ssize_t size) { char dsf[128]; struct gmesh mesh; struct gclass *classp; struct ggeom *gp; struct gprovider *pp; const char *s; char *buf; off_t bsize; int error, fd; s = gctl_get_ascii(req, "class"); if (s == NULL) abort(); error = geom_gettree(&mesh); if (error != 0) errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); classp = find_class(&mesh, s); if (classp == NULL) { geom_deletetree(&mesh); errx(EXIT_FAILURE, "Class %s not found.", s); } s = gctl_get_ascii(req, "geom"); gp = find_geom(classp, s); if (gp == NULL) errx(EXIT_FAILURE, "No such geom: %s.", s); LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "index"); if (s == NULL) continue; if (atoi(s) == idx) break; } if (pp != NULL) { snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); fd = open(dsf, O_WRONLY); if (fd == -1) err(EXIT_FAILURE, "%s", dsf); if (lseek(fd, size, SEEK_SET) != size) errx(EXIT_FAILURE, "%s: not enough space", dsf); if (lseek(fd, 0, SEEK_SET) != 0) err(EXIT_FAILURE, "%s", dsf); /* * When writing to a disk device, the write must be * sector aligned and not write to any partial sectors, * so round up the buffer size to the next sector and zero it. */ bsize = (size + pp->lg_sectorsize - 1) / pp->lg_sectorsize * pp->lg_sectorsize; buf = calloc(1, bsize); if (buf == NULL) err(EXIT_FAILURE, "%s", dsf); bcopy(code, buf, size); if (write(fd, buf, bsize) != bsize) err(EXIT_FAILURE, "%s", dsf); free(buf); close(fd); } else errx(EXIT_FAILURE, "invalid partition index"); geom_deletetree(&mesh); } static void gpart_bootcode(struct gctl_req *req, unsigned int fl) { const char *s; char *sp; void *bootcode, *partcode; size_t bootsize, partsize; int error, idx; if (gctl_has_param(req, bootcode_param)) { s = gctl_get_ascii(req, bootcode_param); bootsize = 64 * 1024; /* Arbitrary limit. */ bootcode = gpart_bootfile_read(s, &bootsize); error = gctl_change_param(req, bootcode_param, bootsize, bootcode); if (error) errc(EXIT_FAILURE, error, "internal error"); } else { bootcode = NULL; bootsize = 0; } if (gctl_has_param(req, partcode_param)) { s = gctl_get_ascii(req, partcode_param); partsize = bootsize * 1024; partcode = gpart_bootfile_read(s, &partsize); error = gctl_delete_param(req, partcode_param); if (error) errc(EXIT_FAILURE, error, "internal error"); } else { partcode = NULL; partsize = 0; } if (gctl_has_param(req, index_param)) { if (partcode == NULL) errx(EXIT_FAILURE, "-i is only valid with -p"); s = gctl_get_ascii(req, index_param); idx = strtol(s, &sp, 10); if (idx < 1 || *s == '\0' || *sp != '\0') errx(EXIT_FAILURE, "invalid partition index"); error = gctl_delete_param(req, index_param); if (error) errc(EXIT_FAILURE, error, "internal error"); } else idx = 0; if (partcode != NULL) { if (idx == 0) errx(EXIT_FAILURE, "missing -i option"); gpart_write_partcode(req, idx, partcode, partsize); } else { if (bootcode == NULL) errx(EXIT_FAILURE, "no -b nor -p"); } if (bootcode != NULL) gpart_issue(req, fl); } static void gpart_issue(struct gctl_req *req, unsigned int fl __unused) { char buf[4096]; char *errmsg; const char *errstr; int error, status; /* autofill parameters (if applicable). */ error = gpart_autofill(req); if (error) { warnc(error, "autofill"); status = EXIT_FAILURE; goto done; } bzero(buf, sizeof(buf)); gctl_rw_param(req, "output", sizeof(buf), buf); errstr = gctl_issue(req); if (errstr == NULL || errstr[0] == '\0') { if (buf[0] != '\0') printf("%s", buf); status = EXIT_SUCCESS; goto done; } error = strtol(errstr, &errmsg, 0); if (errmsg != errstr) { while (errmsg[0] == ' ') errmsg++; if (errmsg[0] != '\0') warnc(error, "%s", errmsg); else warnc(error, NULL); } else warnx("%s", errmsg); status = EXIT_FAILURE; done: gctl_free(req); exit(status); }