/** * Validate an NVRAM variable name. * * Scans for special characters (path delimiters, value delimiters, path * alias prefixes), returning false if the given name cannot be used * as a relative NVRAM key. * * @param name A relative NVRAM variable name to validate. * @param name_len The length of @p name, in bytes. * * @retval true If @p name is a valid relative NVRAM key. * @retval false If @p name should not be used as a relative NVRAM key. */ bool bhnd_nvram_validate_name(const char *name, size_t name_len) { size_t limit; limit = strnlen(name, name_len); if (limit == 0) return (false); /* Disallow path alias prefixes ([0-9]+:.*) */ if (limit >= 2 && bhnd_nv_isdigit(*name)) { for (const char *p = name; (size_t)(p - name) < limit; p++) { if (bhnd_nv_isdigit(*p)) continue; else if (*p == ':') return (false); else break; } } /* Scan for special characters */ for (const char *p = name; (size_t)(p - name) < limit; p++) { switch (*p) { case '/': /* path delimiter */ case '=': /* key=value delimiter */ return (false); default: if (!isascii(*p) || bhnd_nv_isspace(*p)) return (false); } } return (true); }
/** * Trim leading path (pci/1/1) or path alias (0:) prefix from @p name, if any, * returning a pointer to the start of the relative variable name. * * @par Examples * * - "/foo" -> "foo" * - "dev/pci/foo" -> "foo" * - "0:foo" -> "foo" * - "foo" -> "foo" * * @param name The string to be trimmed. * * @return A pointer to the start of the relative variable name in @p name. */ const char * bhnd_nvram_trim_path_name(const char *name) { char *endp; /* path alias prefix? (0:varname) */ if (bhnd_nv_isdigit(*name)) { /* Parse '0...:' alias prefix, if it exists */ strtoul(name, &endp, 10); if (endp != name && *endp == ':') { /* Variable name follows 0: prefix */ return (endp+1); } } /* device path prefix? (pci/1/1/varname) */ if ((endp = strrchr(name, '/')) != NULL) { /* Variable name follows the final path separator '/' */ return (endp+1); } /* variable name is not prefixed */ return (name); }
/** * Parses the string in the optionally NUL-terminated @p str to as an integer * value of @p otype, accepting any integer format supported by the standard * strtoul(). * * - Any leading whitespace in @p str -- as defined by the equivalent of * calling isspace_l() with an ASCII locale -- will be ignored. * - A @p str may be prefixed with a single optional '+' or '-' sign denoting * signedness. * - A hexadecimal @p str may include an '0x' or '0X' prefix, denoting that a * base 16 integer follows. * - An octal @p str may include a '0' prefix, denoting that an octal integer * follows. * * If a @p base of 0 is specified, the base will be determined according * to the string's initial prefix, as per strtoul()'s documented behavior. * * When parsing a base 16 integer to a signed representation, if no explicit * sign prefix is given, the string will be parsed as the raw two's complement * representation of the signed integer value. * * @param str The string to be parsed. * @param maxlen The maximum number of bytes to be read in * @p str. * @param base The input string's base (2-36), or 0. * @param[out] nbytes On success or failure, will be set to the total * number of parsed bytes. If the total number of * bytes is not desired, a NULL pointer may be * provided. * @param[out] outp On success, the parsed integer value will be * written to @p outp. This argment may be NULL if * the value is not desired. * @param[in,out] olen The capacity of @p outp. On success, will be set * to the actual size of the requested value. * @param otype The integer type to be parsed. * * @retval 0 success * @retval EINVAL if an invalid @p base is specified. * @retval EINVAL if an unsupported (or non-integer) @p otype is * specified. * @retval ENOMEM If @p outp is non-NULL and a buffer of @p olen is too * small to hold the requested value. * @retval EFTYPE if @p str cannot be parsed as an integer of @p base. * @retval ERANGE If the integer parsed from @p str is too large to be * represented as a value of @p otype. */ int bhnd_nvram_parse_int(const char *str, size_t maxlen, u_int base, size_t *nbytes, void *outp, size_t *olen, bhnd_nvram_type otype) { uint64_t value; uint64_t carry_max, value_max; uint64_t type_max; size_t limit, local_nbytes; size_t ndigits; bool negative, sign, twos_compl; /* Must be an integer type */ if (!bhnd_nvram_is_int_type(otype)) return (EINVAL); /* Determine output byte limit */ if (outp != NULL) limit = *olen; else limit = 0; /* We always need a byte count. If the caller provides a NULL nbytes, * track our position in a stack variable */ if (nbytes == NULL) nbytes = &local_nbytes; value = 0; ndigits = 0; *nbytes = 0; negative = false; sign = false; /* Validate the specified base */ if (base != 0 && !(base >= 2 && base <= 36)) return (EINVAL); /* Skip any leading whitespace */ for (; *nbytes < maxlen; (*nbytes)++) { if (!bhnd_nv_isspace(str[*nbytes])) break; } /* Empty string? */ if (*nbytes == maxlen) return (EFTYPE); /* Parse and skip sign */ if (str[*nbytes] == '-') { negative = true; sign = true; (*nbytes)++; } else if (str[*nbytes] == '+') { sign = true; (*nbytes)++; } /* Truncated after sign character? */ if (*nbytes == maxlen) return (EFTYPE); /* Identify (or validate) hex base, skipping 0x/0X prefix */ if (base == 16 || base == 0) { /* Check for (and skip) 0x/0X prefix */ if (maxlen - *nbytes >= 2 && str[*nbytes] == '0' && (str[*nbytes+1] == 'x' || str[*nbytes+1] == 'X')) { base = 16; (*nbytes) += 2; } } /* Truncated after hex prefix? */ if (*nbytes == maxlen) return (EFTYPE); /* Differentiate decimal/octal by looking for a leading 0 */ if (base == 0) { if (str[*nbytes] == '0') { base = 8; } else { base = 10; } } /* Only enable twos-compliment signed integer parsing enabled if the * input is base 16, and no explicit sign prefix was provided */ if (!sign && base == 16) twos_compl = true; else twos_compl = false; /* Determine the maximum value representable by the requested type */ switch (otype) { case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_UINT8: type_max = (uint64_t)UINT8_MAX; break; case BHND_NVRAM_TYPE_UINT16: type_max = (uint64_t)UINT16_MAX; break; case BHND_NVRAM_TYPE_UINT32: type_max = (uint64_t)UINT32_MAX; break; case BHND_NVRAM_TYPE_UINT64: type_max = (uint64_t)UINT64_MAX; break; case BHND_NVRAM_TYPE_INT8: if (twos_compl) type_max = (uint64_t)UINT8_MAX; else if (negative) type_max = -(uint64_t)INT8_MIN; else type_max = (uint64_t)INT8_MAX; break; case BHND_NVRAM_TYPE_INT16: if (twos_compl) type_max = (uint64_t)UINT16_MAX; else if (negative) type_max = -(uint64_t)INT16_MIN; else type_max = (uint64_t)INT16_MAX; break; case BHND_NVRAM_TYPE_INT32: if (twos_compl) type_max = (uint64_t)UINT32_MAX; else if (negative) type_max = -(uint64_t)INT32_MIN; else type_max = (uint64_t)INT32_MAX; break; case BHND_NVRAM_TYPE_INT64: if (twos_compl) type_max = (uint64_t)UINT64_MAX; else if (negative) type_max = -(uint64_t)INT64_MIN; else type_max = (uint64_t)INT64_MAX; break; default: BHND_NV_LOG("unsupported integer type: %d\n", otype); return (EINVAL); } /* The maximum value after which an additional carry would overflow */ value_max = type_max / (uint64_t)base; /* The maximum carry value given a value equal to value_max */ carry_max = type_max % (uint64_t)base; /* Consume input until we hit maxlen or a non-digit character */ for (; *nbytes < maxlen; (*nbytes)++) { u_long carry; char c; /* Parse carry value */ c = str[*nbytes]; if (bhnd_nv_isdigit(c)) { carry = c - '0'; } else if (bhnd_nv_isxdigit(c)) { if (bhnd_nv_isupper(c)) carry = (c - 'A') + 10; else carry = (c - 'a') + 10; } else { /* Hit first non-digit character */ break; } /* If carry is outside the base, it's not a valid digit * in the current parse context; consider it a non-digit * character */ if (carry >= (uint64_t)base) break; /* Increment count of parsed digits */ ndigits++; if (value > value_max) { /* -Any- carry value would overflow */ return (ERANGE); } else if (value == value_max && carry > carry_max) { /* -This- carry value would overflow */ return (ERANGE); } value *= (uint64_t)base; value += carry; } /* If we hit a non-digit character before parsing the first digit, * we hit an empty integer string. */ if (ndigits == 0) return (EFTYPE); if (negative) value = -value; /* Provide (and verify) required length */ *olen = bhnd_nvram_value_size(otype, NULL, 0, 1); if (outp == NULL) return (0); else if (limit < *olen) return (ENOMEM); /* Provide result */ switch (otype) { case BHND_NVRAM_TYPE_CHAR: case BHND_NVRAM_TYPE_UINT8: *(uint8_t *)outp = (uint8_t)value; break; case BHND_NVRAM_TYPE_UINT16: *(uint16_t *)outp = (uint16_t)value; break; case BHND_NVRAM_TYPE_UINT32: *(uint32_t *)outp = (uint32_t)value; break; case BHND_NVRAM_TYPE_UINT64: *(uint64_t *)outp = (uint64_t)value; break; case BHND_NVRAM_TYPE_INT8: *(int8_t *)outp = (int8_t)(int64_t)value; break; case BHND_NVRAM_TYPE_INT16: *(int16_t *)outp = (int16_t)(int64_t)value; break; case BHND_NVRAM_TYPE_INT32: *(int32_t *)outp = (int32_t)(int64_t)value; break; case BHND_NVRAM_TYPE_INT64: *(int64_t *)outp = (int64_t)value; break; default: /* unreachable */ BHND_NV_PANIC("unhandled type %d\n", otype); } return (0); }