kern uuid: break format validation out into a separate KPI
This new KPI, validate_uuid, strictly validates the formatting of the input UUID and, optionally, populates a given struct uuid. As noted in the header, the key differences are that the new KPI won't recognize an empty string as a nil UUID and it won't do any kind of semantic validation on it. Also key is that populating a struct uuid is optional, so the caller doesn't necessarily need to allocate a bogus one on the stack just to validate the string. This KPI has specifically been broken out in support of D24288, which will preload /etc/hostid in loader so that early boot hostuuid users (e.g. anything that calls ether_gen_addr) can have a valid hostuuid to work with once it's been stashed in /etc/hostid.
This commit is contained in:
parent
ddde90ac81
commit
142ffb8bdc
|
@ -382,19 +382,16 @@ be_uuid_dec(void const *buf, struct uuid *uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parse_uuid(const char *str, struct uuid *uuid)
|
validate_uuid(const char *str, size_t size, struct uuid *uuid)
|
||||||
{
|
{
|
||||||
u_int c[11];
|
u_int c[11];
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
/* An empty string represents a nil UUID. */
|
if (size == 0 || *str == '\0')
|
||||||
if (*str == '\0') {
|
return (EINVAL);
|
||||||
bzero(uuid, sizeof(*uuid));
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The UUID string representation has a fixed length. */
|
/* The UUID string representation has a fixed length. */
|
||||||
if (strlen(str) != 36)
|
if (size != 36)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -406,25 +403,48 @@ parse_uuid(const char *str, struct uuid *uuid)
|
||||||
if (str[8] != '-')
|
if (str[8] != '-')
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
|
/* Now check the format. */
|
||||||
n = sscanf(str, "%8x-%4x-%4x-%2x%2x-%2x%2x%2x%2x%2x%2x", c + 0, c + 1,
|
n = sscanf(str, "%8x-%4x-%4x-%2x%2x-%2x%2x%2x%2x%2x%2x", c + 0, c + 1,
|
||||||
c + 2, c + 3, c + 4, c + 5, c + 6, c + 7, c + 8, c + 9, c + 10);
|
c + 2, c + 3, c + 4, c + 5, c + 6, c + 7, c + 8, c + 9, c + 10);
|
||||||
/* Make sure we have all conversions. */
|
/* Make sure we have all conversions. */
|
||||||
if (n != 11)
|
if (n != 11)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
/* Successful scan. Build the UUID. */
|
/* Successful scan. Build the UUID if requested. */
|
||||||
uuid->time_low = c[0];
|
if (uuid != NULL) {
|
||||||
uuid->time_mid = c[1];
|
uuid->time_low = c[0];
|
||||||
uuid->time_hi_and_version = c[2];
|
uuid->time_mid = c[1];
|
||||||
uuid->clock_seq_hi_and_reserved = c[3];
|
uuid->time_hi_and_version = c[2];
|
||||||
uuid->clock_seq_low = c[4];
|
uuid->clock_seq_hi_and_reserved = c[3];
|
||||||
for (n = 0; n < 6; n++)
|
uuid->clock_seq_low = c[4];
|
||||||
uuid->node[n] = c[n + 5];
|
for (n = 0; n < 6; n++)
|
||||||
|
uuid->node[n] = c[n + 5];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parse_uuid(const char *str, struct uuid *uuid)
|
||||||
|
{
|
||||||
|
unsigned int clock_seq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* An empty string represents a nil UUID. */
|
||||||
|
if (*str == '\0') {
|
||||||
|
bzero(uuid, sizeof(*uuid));
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = validate_uuid(str, strlen(str), uuid);
|
||||||
|
if (ret != 0)
|
||||||
|
return (ret);
|
||||||
|
|
||||||
/* Check semantics... */
|
/* Check semantics... */
|
||||||
return (((c[3] & 0x80) != 0x00 && /* variant 0? */
|
clock_seq = uuid->clock_seq_hi_and_reserved;
|
||||||
(c[3] & 0xc0) != 0x80 && /* variant 1? */
|
return (((clock_seq & 0x80) != 0x00 && /* variant 0? */
|
||||||
(c[3] & 0xe0) != 0xc0) ? EINVAL : 0); /* variant 2? */
|
(clock_seq & 0xc0) != 0x80 && /* variant 1? */
|
||||||
|
(clock_seq & 0xe0) != 0xc0) ? EINVAL : 0); /* variant 2? */
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -66,6 +66,21 @@ int uuid_ether_del(const uint8_t *);
|
||||||
int snprintf_uuid(char *, size_t, struct uuid *);
|
int snprintf_uuid(char *, size_t, struct uuid *);
|
||||||
int printf_uuid(struct uuid *);
|
int printf_uuid(struct uuid *);
|
||||||
int sbuf_printf_uuid(struct sbuf *, struct uuid *);
|
int sbuf_printf_uuid(struct sbuf *, struct uuid *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are a few key differences between validate_uuid and parse_uuid:
|
||||||
|
*
|
||||||
|
* - The struct uuid * parameter to validate_uuid is optional, so the caller
|
||||||
|
* can simply validate UUID format without doing anything with the result.
|
||||||
|
* - validate_uuid will not pass an empty string as a valid UUID, as it doesn't
|
||||||
|
* strictly meet the formatting requirements. parse_uuid will accept an
|
||||||
|
* empty string and zero out the uuid struct accordingly.
|
||||||
|
* - parse_uuid does additional semantic checks on clock_seq_hi_and_reserved
|
||||||
|
* that validate_uuid will not do.
|
||||||
|
*
|
||||||
|
* validate_uuid is intended to strictly check that it's a well-formed uuid.
|
||||||
|
*/
|
||||||
|
int validate_uuid(const char *, size_t, struct uuid *);
|
||||||
int parse_uuid(const char *, struct uuid *);
|
int parse_uuid(const char *, struct uuid *);
|
||||||
int uuidcmp(const struct uuid *, const struct uuid *);
|
int uuidcmp(const struct uuid *, const struct uuid *);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue