Remove G_TYPE_ASCLBA type and replace it with G_TYPE_STRING in gpart.

Move code that converts params from humanized numbers to sectors count
to subr.c and adjust comment.
Add post-processing for "size" and "start offset" params in gpart,
now they are properly converted to sectors count with known sector size
that can be greater that 512 bytes.
Also replace "unsigned long long" type to "off_t" for unify code since
it used for medium size in libgeom(3) and DIOCGMEDIASIZE ioctl.

PR:		bin/146277
Reviewed by:	marcel (previous version)
Approved by:	kib (mentor)
MFC after:	1 month
This commit is contained in:
ae 2010-06-21 08:24:50 +00:00
parent d1175426d7
commit 9f1bacec49
5 changed files with 210 additions and 177 deletions

View File

@ -40,6 +40,8 @@ __FBSDID("$FreeBSD$");
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <inttypes.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
@ -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));
}

View File

@ -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')

View File

@ -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

View File

@ -36,6 +36,8 @@ __FBSDID("$FreeBSD$");
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <inttypes.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
@ -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)
{

View File

@ -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);