diff --git a/sbin/geom/class/part/geom_part.c b/sbin/geom/class/part/geom_part.c index a72155b1c1d0..cc1145e1aa53 100644 --- a/sbin/geom/class/part/geom_part.c +++ b/sbin/geom/class/part/geom_part.c @@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include #include @@ -60,6 +62,9 @@ static char autofill[] = "*"; static char optional[] = ""; static char flags[] = "C"; +static char sstart[32]; +static char ssize[32]; + static const char const bootcode_param[] = "bootcode"; static const char const index_param[] = "index"; static const char const partcode_param[] = "partcode"; @@ -68,8 +73,7 @@ static struct gclass *find_class(struct gmesh *, const char *); static struct ggeom * find_geom(struct gclass *, const char *); static const char *find_geomcfg(struct ggeom *, const char *); static const char *find_provcfg(struct gprovider *, const char *); -static struct gprovider *find_provider(struct ggeom *, - unsigned long long); +static struct gprovider *find_provider(struct ggeom *, off_t); static const char *fmtsize(int64_t); static int gpart_autofill(struct gctl_req *); static int gpart_autofill_resize(struct gctl_req *); @@ -84,8 +88,8 @@ static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *); struct g_command PUBSYM(class_commands)[] = { { "add", 0, gpart_issue, { - { 'b', "start", autofill, G_TYPE_ASCLBA }, - { 's', "size", autofill, G_TYPE_ASCLBA }, + { 'b', "start", autofill, G_TYPE_STRING }, + { 's', "size", autofill, G_TYPE_STRING }, { 't', "type", NULL, G_TYPE_STRING }, { 'i', index_param, optional, G_TYPE_ASCNUM }, { 'l', "label", optional, G_TYPE_STRING }, @@ -149,7 +153,7 @@ struct g_command PUBSYM(class_commands)[] = { "geom", NULL }, { "resize", 0, gpart_issue, { - { 's', "size", autofill, G_TYPE_ASCLBA }, + { 's', "size", autofill, G_TYPE_STRING }, { 'i', index_param, NULL, G_TYPE_ASCNUM }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, @@ -207,11 +211,11 @@ find_provcfg(struct gprovider *pp, const char *cfg) } static struct gprovider * -find_provider(struct ggeom *gp, unsigned long long minsector) +find_provider(struct ggeom *gp, off_t minsector) { struct gprovider *pp, *bestpp; const char *s; - unsigned long long sector, bestsector; + off_t sector, bestsector; bestpp = NULL; bestsector = 0; @@ -219,9 +223,10 @@ find_provider(struct ggeom *gp, unsigned long long minsector) s = find_provcfg(pp, "start"); if (s == NULL) { s = find_provcfg(pp, "offset"); - sector = atoll(s) / pp->lg_sectorsize; + sector = + (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; } else - sector = atoll(s); + sector = (off_t)strtoimax(s, NULL, 0); if (sector < minsector) continue; @@ -271,18 +276,12 @@ gpart_autofill_resize(struct gctl_req *req) struct gclass *cp; struct ggeom *gp; struct gprovider *pp; - unsigned long long last, size, start, new_size; - unsigned long long lba, new_lba; + off_t last, size, start, new_size; + off_t lba, new_lba; const char *s; char *val; int error, idx; - s = gctl_get_ascii(req, "size"); - if (*s == '*') - new_size = (unsigned long long)atoll(s); - else - return (0); - s = gctl_get_ascii(req, index_param); idx = strtol(s, &val, 10); if (idx < 1 || *s == '\0' || *val != '\0') @@ -303,8 +302,22 @@ gpart_autofill_resize(struct gctl_req *req) gp = find_geom(cp, s); if (gp == NULL) errx(EXIT_FAILURE, "No such geom: %s.", s); - last = atoll(find_geomcfg(gp, "last")); + pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; + if (pp == NULL) + errx(EXIT_FAILURE, "Provider for geom %s not found.", s); + s = gctl_get_ascii(req, "size"); + if (*s == '*') + new_size = 0; + else { + error = g_parse_lba(s, pp->lg_sectorsize, &new_size); + if (error) + errc(EXIT_FAILURE, error, "Invalid size param"); + /* no autofill necessary. */ + goto done; + } + + last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0); LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { s = find_provcfg(pp, "index"); if (s == NULL) @@ -318,18 +331,21 @@ gpart_autofill_resize(struct gctl_req *req) s = find_provcfg(pp, "start"); if (s == NULL) { s = find_provcfg(pp, "offset"); - start = atoll(s) / pp->lg_sectorsize; + start = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; } else - start = atoll(s); + start = (off_t)strtoimax(s, NULL, 0); s = find_provcfg(pp, "end"); if (s == NULL) { s = find_provcfg(pp, "length"); - lba = start + atoll(s) / pp->lg_sectorsize; + lba = start + + (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; } else - lba = atoll(s) + 1; + lba = (off_t)strtoimax(s, NULL, 0) + 1; - if (lba > last) + if (lba > last) { + geom_deletetree(&mesh); return (ENOSPC); + } size = lba - start; pp = find_provider(gp, lba); if (pp == NULL) @@ -338,22 +354,25 @@ gpart_autofill_resize(struct gctl_req *req) s = find_provcfg(pp, "start"); if (s == NULL) { s = find_provcfg(pp, "offset"); - new_lba = atoll(s) / pp->lg_sectorsize; + new_lba = + (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; } else - new_lba = atoll(s); - /* Is there any free space between current and + new_lba = (off_t)strtoimax(s, NULL, 0); + /* + * Is there any free space between current and * next providers? */ if (new_lba > lba) new_size = new_lba - start; - else + else { + geom_deletetree(&mesh); return (ENOSPC); + } } - asprintf(&val, "%llu", new_size); - if (val == NULL) - return (ENOMEM); - gctl_change_param(req, "size", -1, val); - +done: + snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size); + gctl_change_param(req, "size", -1, ssize); + geom_deletetree(&mesh); return (0); } @@ -364,11 +383,11 @@ gpart_autofill(struct gctl_req *req) 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; + off_t first, last; + off_t size, start; + off_t lba, len; + uintmax_t grade; const char *s; - char *val; int error, has_size, has_start; s = gctl_get_ascii(req, "verb"); @@ -377,18 +396,6 @@ gpart_autofill(struct gctl_req *req) 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); @@ -404,22 +411,49 @@ gpart_autofill(struct gctl_req *req) gp = find_geom(cp, s); if (gp == NULL) errx(EXIT_FAILURE, "No such geom: %s.", s); - first = atoll(find_geomcfg(gp, "first")); - last = atoll(find_geomcfg(gp, "last")); + pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; + if (pp == NULL) + errx(EXIT_FAILURE, "Provider for geom %s not found.", s); + + s = gctl_get_ascii(req, "size"); + has_size = (*s == '*') ? 0 : 1; + size = 0; + if (has_size) { + error = g_parse_lba(s, pp->lg_sectorsize, &size); + if (error) + errc(EXIT_FAILURE, error, "Invalid size param"); + } + + s = gctl_get_ascii(req, "start"); + has_start = (*s == '*') ? 0 : 1; + start = 0ULL; + if (has_start) { + error = g_parse_lba(s, pp->lg_sectorsize, &start); + if (error) + errc(EXIT_FAILURE, error, "Invalid start param"); + } + + /* No autofill necessary. */ + if (has_size && has_start) + goto done; + + first = (off_t)strtoimax(find_geomcfg(gp, "first"), NULL, 0); + last = (off_t)strtoimax(find_geomcfg(gp, "last"), NULL, 0); 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; + lba = (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; } else - lba = atoll(s); + lba = (off_t)strtoimax(s, NULL, 0); if (first < lba) { /* Free space [first, lba> */ len = lba - first; if (has_size) { - if (len >= size && len - size < grade) { + if (len >= size && + (uintmax_t)(len - size) < grade) { start = first; grade = len - size; } @@ -440,15 +474,17 @@ gpart_autofill(struct gctl_req *req) s = find_provcfg(pp, "end"); if (s == NULL) { s = find_provcfg(pp, "length"); - first = lba + atoll(s) / pp->lg_sectorsize; + first = lba + + (off_t)strtoimax(s, NULL, 0) / pp->lg_sectorsize; } else - first = atoll(s) + 1; + first = (off_t)strtoimax(s, NULL, 0) + 1; } if (first <= last) { /* Free space [first-last] */ len = last - first + 1; if (has_size) { - if (len >= size && len - size < grade) { + if (len >= size && + (uintmax_t)(len - size) < grade) { start = first; grade = len - size; } @@ -466,21 +502,17 @@ gpart_autofill(struct gctl_req *req) } } - if (grade == ~0ULL) + if (grade == ~0ULL) { + geom_deletetree(&mesh); 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); - } +done: + snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size); + gctl_change_param(req, "size", -1, ssize); + snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start); + gctl_change_param(req, "start", -1, sstart); + geom_deletetree(&mesh); return (0); } @@ -489,21 +521,21 @@ 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; + off_t first, last, sector, end; + off_t length, secsz; int idx, wblocks, wname; scheme = find_geomcfg(gp, "scheme"); s = find_geomcfg(gp, "first"); - first = atoll(s); + first = (off_t)strtoimax(s, NULL, 0); s = find_geomcfg(gp, "last"); - last = atoll(s); + last = (off_t)strtoimax(s, NULL, 0); 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), + printf("=>%*jd %*jd %*s %s (%s)\n", + wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1), wname, gp->lg_name, scheme, fmtsize(pp->lg_mediasize)); @@ -511,37 +543,37 @@ gpart_show_geom(struct ggeom *gp, const char *element) s = find_provcfg(pp, "start"); if (s == NULL) { s = find_provcfg(pp, "offset"); - sector = atoll(s) / secsz; + sector = (off_t)strtoimax(s, NULL, 0) / secsz; } else - sector = atoll(s); + sector = (off_t)strtoimax(s, NULL, 0); s = find_provcfg(pp, "end"); if (s == NULL) { s = find_provcfg(pp, "length"); - length = atoll(s) / secsz; + length = (off_t)strtoimax(s, NULL, 0) / secsz; end = sector + length - 1; } else { - end = atoll(s); + end = (off_t)strtoimax(s, NULL, 0); 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, "", + printf(" %*jd %*jd %*s - free - (%s)\n", + wblocks, (intmax_t)first, wblocks, + (intmax_t)(sector - first), wname, "", fmtsize((sector - first) * secsz)); } - printf(" %*llu %*llu %*d %s %s (%s)\n", - wblocks, sector, wblocks, length, + printf(" %*jd %*jd %*d %s %s (%s)\n", + wblocks, (intmax_t)sector, wblocks, (intmax_t)length, wname, idx, find_provcfg(pp, element), fmtattrib(pp), fmtsize(pp->lg_mediasize)); first = end + 1; } if (first <= last) { length = last - first + 1; - printf(" %*llu %*llu %*s - free - (%s)\n", - wblocks, first, wblocks, length, + printf(" %*jd %*jd %*s - free - (%s)\n", + wblocks, (intmax_t)first, wblocks, (intmax_t)length, wname, "", fmtsize(length * secsz)); } diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c index 66f2a8f3874a..49ebdc5364a5 100644 --- a/sbin/geom/core/geom.c +++ b/sbin/geom/core/geom.c @@ -259,94 +259,6 @@ set_option(struct gctl_req *req, struct g_option *opt, const char *val) opt->go_val); } else gctl_ro_param(req, opt->go_name, -1, opt->go_val); - } else if (G_OPT_TYPE(opt) == G_TYPE_ASCLBA) { - /* - * LBAs are ugly. The argument is a sector. The size of a - * sector is context specific (i.e. determined by the media), - * which we don't know here. 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. - * So how can we map the byte-oriented value into a sector- - * oriented value if we don't know the sector size in bytes? - * The approach taken here is: - * o Sectors are 512 bytes in size. Mostly the case anyway. - * 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. - * - * "This seemed like a good idea at the time" - */ - intmax_t mult, unit; - - number = strtoimax(val, &s, 0); - if (s == val) - errc(EXIT_FAILURE, EINVAL, "argument '%c'", - opt->go_char); - mult = 1; - unit = 512; /* sector */ - 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 = 512; /* sector */ - break; - case 'b': case 'B': - unit = 1; /* bytes */ - break; - default: - errc(EXIT_FAILURE, EINVAL, "argument '%c': suffix '%c'", - opt->go_char, *s); - } - s++; - if (*s != '\0') - errx(EXIT_FAILURE, "argument '%c': junk at end (%s)", - opt->go_char, s); -done: - if (mult * unit < mult || number * mult * unit < number) - errc(EXIT_FAILURE, ERANGE, "argument '%c'", - opt->go_char); - number *= mult * unit; - if (number % 512) - errx(EXIT_FAILURE, "argument '%c': " - "not a valid block address", opt->go_char); - number /= 512; - asprintf(&s, "%jd", number); - if (s == NULL) - err(EXIT_FAILURE, NULL); - opt->go_val = s; - gctl_ro_param(req, opt->go_name, -1, s); } else if (G_OPT_TYPE(opt) == G_TYPE_STRING) { gctl_ro_param(req, opt->go_name, -1, val); } else if (G_OPT_TYPE(opt) == G_TYPE_BOOL) { @@ -439,8 +351,7 @@ parse_arguments(struct g_command *cmd, struct gctl_req *req, int *argc, gctl_ro_param(req, opt->go_name, sizeof(intmax_t), opt->go_val); } else if (G_OPT_TYPE(opt) == G_TYPE_STRING || - G_OPT_TYPE(opt) == G_TYPE_ASCNUM || - G_OPT_TYPE(opt) == G_TYPE_ASCLBA) { + G_OPT_TYPE(opt) == G_TYPE_ASCNUM) { if (cmd->gc_argname == NULL || opt->go_val == NULL || *(char *)opt->go_val != '\0') diff --git a/sbin/geom/core/geom.h b/sbin/geom/core/geom.h index 5c253115bef4..424695bd76e6 100644 --- a/sbin/geom/core/geom.h +++ b/sbin/geom/core/geom.h @@ -39,7 +39,6 @@ #define G_TYPE_STRING 0x02 #define G_TYPE_NUMBER 0x03 #define G_TYPE_ASCNUM 0x04 -#define G_TYPE_ASCLBA 0x05 #define G_TYPE_MASK 0x0f #define G_TYPE_DONE 0x10 diff --git a/sbin/geom/misc/subr.c b/sbin/geom/misc/subr.c index e71ec7489b0c..7a4786fc87f2 100644 --- a/sbin/geom/misc/subr.c +++ b/sbin/geom/misc/subr.c @@ -36,6 +36,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include #include @@ -107,6 +109,94 @@ bitcount32(uint32_t x) 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 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) + 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 (mult * unit < mult || number * 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) { diff --git a/sbin/geom/misc/subr.h b/sbin/geom/misc/subr.h index c3242a3bdc9b..2b43bdb60129 100644 --- a/sbin/geom/misc/subr.h +++ b/sbin/geom/misc/subr.h @@ -32,6 +32,7 @@ unsigned g_lcm(unsigned a, unsigned b); uint32_t bitcount32(uint32_t x); +int g_parse_lba(const char *lbastr, unsigned sectorsize, off_t *sectors); off_t g_get_mediasize(const char *name); unsigned g_get_sectorsize(const char *name);