100 lines
3.2 KiB
C
100 lines
3.2 KiB
C
// Shared parser for the canonical structural boot-volume selector grammar.
|
|
|
|
#include <common/boot_volume_selector.h>
|
|
|
|
#include <string.h>
|
|
|
|
// Converts one hexadecimal character without accepting non-hexadecimal text.
|
|
static bool BootVolumeHexDigit(char c, uint8_t* digitOut) {
|
|
if (!digitOut)
|
|
return false;
|
|
if (c >= '0' && c <= '9') {
|
|
*digitOut = (uint8_t)(c - '0');
|
|
return true;
|
|
}
|
|
if (c >= 'a' && c <= 'f') {
|
|
*digitOut = (uint8_t)(10 + c - 'a');
|
|
return true;
|
|
}
|
|
if (c >= 'A' && c <= 'F') {
|
|
*digitOut = (uint8_t)(10 + c - 'A');
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Parses an exact hexadecimal integer span into a 32-bit value.
|
|
static bool BootVolumeParseHex32(const char* text, size_t length, uint32_t* valueOut) {
|
|
if (!text || !valueOut || length == 0 || length > 8)
|
|
return false;
|
|
|
|
uint32_t value = 0;
|
|
for (size_t i = 0; i < length; ++i) {
|
|
uint8_t digit = 0;
|
|
if (!BootVolumeHexDigit(text[i], &digit))
|
|
return false;
|
|
value = (value << 4) | digit;
|
|
}
|
|
*valueOut = value;
|
|
return true;
|
|
}
|
|
|
|
// Parses one exact GUID hexadecimal group.
|
|
static bool BootVolumeParseGuidGroup(const char* text, size_t length, uint32_t* valueOut) {
|
|
return BootVolumeParseHex32(text, length, valueOut);
|
|
}
|
|
|
|
// Parses a canonical 8-4-4-4-12 GUID into EFI/GPT field layout.
|
|
static bool BootVolumeParseGuid(const char* text, size_t length, BOOT_VOLUME_GUID* guidOut) {
|
|
if (!text || !guidOut || length != 36 ||
|
|
text[8] != '-' || text[13] != '-' || text[18] != '-' || text[23] != '-') {
|
|
return false;
|
|
}
|
|
|
|
BOOT_VOLUME_GUID guid = {0};
|
|
uint32_t value = 0;
|
|
if (!BootVolumeParseGuidGroup(text, 8, &guid.Data1) ||
|
|
!BootVolumeParseGuidGroup(text + 9, 4, &value)) {
|
|
return false;
|
|
}
|
|
guid.Data2 = (uint16_t)value;
|
|
if (!BootVolumeParseGuidGroup(text + 14, 4, &value))
|
|
return false;
|
|
guid.Data3 = (uint16_t)value;
|
|
|
|
const size_t byteOffsets[8] = {19, 21, 24, 26, 28, 30, 32, 34};
|
|
for (size_t i = 0; i < 8; ++i) {
|
|
if (!BootVolumeParseGuidGroup(text + byteOffsets[i], 2, &value))
|
|
return false;
|
|
guid.Data4[i] = (uint8_t)value;
|
|
}
|
|
|
|
*guidOut = guid;
|
|
return true;
|
|
}
|
|
|
|
// Parses one canonical structural system-volume selector.
|
|
bool BootVolumeSelectorParse(const char* text, size_t length, BOOT_VOLUME_SELECTOR* selectorOut) {
|
|
if (!text || !selectorOut)
|
|
return false;
|
|
|
|
BOOT_VOLUME_SELECTOR selector = {0};
|
|
if (length == 14 && memcmp(text, "mbr/", 4) == 0 && text[12] == '/' &&
|
|
BootVolumeParseHex32(text + 4, 8, &selector.Identity.Mbr.DiskSignature) &&
|
|
text[13] >= '1' && text[13] <= '4') {
|
|
selector.Kind = BootVolumeSelectorMbr;
|
|
selector.Identity.Mbr.PartitionNumber = (uint32_t)(text[13] - '0');
|
|
*selectorOut = selector;
|
|
return true;
|
|
}
|
|
|
|
if (length == 40 && memcmp(text, "gpt/", 4) == 0 &&
|
|
BootVolumeParseGuid(text + 4, 36, &selector.Identity.Gpt.PartitionGuid)) {
|
|
selector.Kind = BootVolumeSelectorGpt;
|
|
*selectorOut = selector;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|