limine/common/lib/config.c

472 lines
13 KiB
C
Raw Normal View History

2020-01-22 09:13:19 +03:00
#include <stddef.h>
2020-10-17 06:37:39 +03:00
#include <stdbool.h>
2020-01-22 09:13:19 +03:00
#include <lib/config.h>
#include <lib/libc.h>
2022-08-27 00:44:47 +03:00
#include <lib/misc.h>
#include <lib/readline.h>
2020-09-20 13:03:44 +03:00
#include <mm/pmm.h>
2020-04-14 06:20:55 +03:00
#include <fs/file.h>
2020-11-05 03:37:45 +03:00
#include <lib/print.h>
#include <pxe/tftp.h>
#include <crypt/blake2b.h>
#include <sys/cpu.h>
#define CONFIG_B2SUM_SIGNATURE "++CONFIG_B2SUM_SIGNATURE++"
#define CONFIG_B2SUM_EMPTY "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
const char *config_b2sum = CONFIG_B2SUM_SIGNATURE CONFIG_B2SUM_EMPTY;
2020-01-22 09:13:19 +03:00
static bool config_get_entry_name(char *ret, size_t index, size_t limit);
static char *config_get_entry(size_t *size, size_t index);
2020-01-22 09:13:19 +03:00
#define SEPARATOR '\n'
2020-10-20 03:38:44 +03:00
bool config_ready = false;
2021-12-12 20:48:36 +03:00
no_unwind bool bad_config = false;
2020-10-20 03:38:44 +03:00
2020-03-30 22:24:36 +03:00
static char *config_addr;
int init_config_disk(struct volume *part) {
2021-10-21 02:27:05 +03:00
struct file_handle *f;
2020-03-30 22:24:36 +03:00
bool old_cif = case_insensitive_fopen;
case_insensitive_fopen = true;
2021-10-21 02:27:05 +03:00
if ((f = fopen(part, "/limine.cfg")) == NULL
&& (f = fopen(part, "/limine/limine.cfg")) == NULL
2021-10-21 02:27:05 +03:00
&& (f = fopen(part, "/boot/limine.cfg")) == NULL
&& (f = fopen(part, "/boot/limine/limine.cfg")) == NULL
2022-07-13 07:23:20 +03:00
&& (f = fopen(part, "/EFI/BOOT/limine.cfg")) == NULL) {
case_insensitive_fopen = old_cif;
return -1;
2020-03-30 22:24:36 +03:00
}
case_insensitive_fopen = old_cif;
2020-03-30 22:24:36 +03:00
size_t config_size = f->size + 2;
config_addr = ext_mem_alloc(config_size);
2020-03-30 22:24:36 +03:00
2021-10-21 02:27:05 +03:00
fread(f, config_addr, 0, f->size);
fclose(f);
2020-03-30 22:24:36 +03:00
2020-11-05 03:37:45 +03:00
return init_config(config_size);
}
#define NOT_CHILD (-1)
#define DIRECT_CHILD 0
#define INDIRECT_CHILD 1
static int is_child(char *buf, size_t limit,
size_t current_depth, size_t index) {
if (!config_get_entry_name(buf, index, limit))
return NOT_CHILD;
if (strlen(buf) < current_depth + 1)
return NOT_CHILD;
for (size_t j = 0; j < current_depth; j++)
if (buf[j] != ':')
return NOT_CHILD;
if (buf[current_depth] == ':')
return INDIRECT_CHILD;
return DIRECT_CHILD;
}
static bool is_directory(char *buf, size_t limit,
size_t current_depth, size_t index) {
switch (is_child(buf, limit, current_depth + 1, index + 1)) {
default:
case NOT_CHILD:
return false;
case INDIRECT_CHILD:
2021-12-12 20:48:36 +03:00
bad_config = true;
2021-12-11 21:58:00 +03:00
panic(true, "config: Malformed config file. Parentless child.");
case DIRECT_CHILD:
return true;
}
}
static struct menu_entry *create_menu_tree(struct menu_entry *parent,
size_t current_depth, size_t index) {
struct menu_entry *root = NULL, *prev = NULL;
for (size_t i = index; ; i++) {
static char name[64];
switch (is_child(name, 64, current_depth, i)) {
case NOT_CHILD:
return root;
case INDIRECT_CHILD:
continue;
case DIRECT_CHILD:
break;
}
struct menu_entry *entry = ext_mem_alloc(sizeof(struct menu_entry));
if (root == NULL)
root = entry;
config_get_entry_name(name, i, 64);
2020-12-01 04:52:03 +03:00
bool default_expanded = name[current_depth] == '+';
strcpy(entry->name, name + current_depth + default_expanded);
entry->parent = parent;
2021-07-08 17:57:40 +03:00
size_t entry_size;
char *config_entry = config_get_entry(&entry_size, i);
entry->body = ext_mem_alloc(entry_size + 1);
memcpy(entry->body, config_entry, entry_size);
entry->body[entry_size] = 0;
if (is_directory(name, 64, current_depth, i)) {
entry->sub = create_menu_tree(entry, current_depth + 1, i + 1);
2020-12-01 04:52:03 +03:00
entry->expanded = default_expanded;
2021-07-08 17:57:40 +03:00
}
char *comment = config_get_value(entry->body, 0, "COMMENT");
if (comment != NULL) {
entry->comment = comment;
}
if (prev != NULL)
prev->next = entry;
prev = entry;
}
}
struct menu_entry *menu_tree = NULL;
2021-12-15 21:29:12 +03:00
struct macro {
char name[1024];
char value[2048];
struct macro *next;
};
static struct macro *macros = NULL;
2020-11-05 03:37:45 +03:00
int init_config(size_t config_size) {
config_b2sum += sizeof(CONFIG_B2SUM_SIGNATURE) - 1;
if (memcmp((void *)config_b2sum, CONFIG_B2SUM_EMPTY, 128) != 0) {
editor_enabled = false;
uint8_t out_buf[BLAKE2B_OUT_BYTES];
blake2b(out_buf, config_addr, config_size - 2);
uint8_t hash_buf[BLAKE2B_OUT_BYTES];
for (size_t i = 0; i < BLAKE2B_OUT_BYTES; i++) {
hash_buf[i] = digit_to_int(config_b2sum[i * 2]) << 4 | digit_to_int(config_b2sum[i * 2 + 1]);
}
if (memcmp(hash_buf, out_buf, BLAKE2B_OUT_BYTES) != 0) {
panic(false, "!!! CHECKSUM MISMATCH FOR CONFIG FILE !!!");
}
}
// add trailing newline if not present
config_addr[config_size - 2] = '\n';
// remove windows carriage returns and spaces at the start of lines, if any
2020-07-02 17:14:09 +03:00
for (size_t i = 0; i < config_size; i++) {
size_t skip = 0;
while ((config_addr[i + skip] == '\r')
2022-03-21 03:38:37 +03:00
|| ((!i || config_addr[i - 1] == '\n')
2022-03-21 04:38:50 +03:00
&& (config_addr[i + skip] == ' ' || config_addr[i + skip] == '\t'))) {
skip++;
}
if (skip) {
for (size_t j = i; j < config_size - skip; j++)
config_addr[j] = config_addr[j + skip];
config_size -= skip;
}
}
2021-12-15 21:29:12 +03:00
// Load macros
struct macro *arch_macro = ext_mem_alloc(sizeof(struct macro));
strcpy(arch_macro->name, "ARCH");
#if defined (__x86_64__)
strcpy(arch_macro->value, "x86-64");
#elif defined (__i386__)
{
uint32_t eax, ebx, ecx, edx;
if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
strcpy(arch_macro->value, "ia-32");
} else {
strcpy(arch_macro->value, "x86-64");
}
}
#elif defined (__aarch64__)
strcpy(arch_macro->value, "aarch64");
#elif defined (__riscv64)
strcpy(arch_macro->value, "riscv64");
#else
#error "Unspecified architecture"
#endif
arch_macro->next = macros;
macros = arch_macro;
2021-12-15 21:29:12 +03:00
for (size_t i = 0; i < config_size;) {
if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0)
|| (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) {
struct macro *macro = ext_mem_alloc(sizeof(struct macro));
i += i ? 3 : 2;
size_t j;
for (j = 0; config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
macro->name[j] = config_addr[i];
}
if (config_addr[i] == '\n' || config_addr[i] == 0 || config_addr[i+1] != '=') {
continue;
2021-12-15 21:29:12 +03:00
}
i += 2;
macro->name[j] = 0;
for (j = 0; config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
macro->value[j] = config_addr[i];
}
macro->value[j] = 0;
macro->next = macros;
macros = macro;
continue;
}
i++;
}
// Expand macros
if (macros != NULL) {
2021-12-15 21:45:54 +03:00
size_t new_config_size = config_size * 4;
char *new_config = ext_mem_alloc(new_config_size);
2021-12-15 21:29:12 +03:00
size_t i, in;
for (i = 0, in = 0; i < config_size;) {
if ((config_size - i >= 3 && memcmp(config_addr + i, "\n${", 3) == 0)
|| (config_size - i >= 2 && i == 0 && memcmp(config_addr, "${", 2) == 0)) {
size_t orig_i = i;
2021-12-15 21:29:12 +03:00
i += i ? 3 : 2;
while (config_addr[i++] != '}') {
if (i >= config_size) {
bad_config = true;
panic(true, "config: Malformed macro usage");
}
}
if (config_addr[i++] != '=') {
i = orig_i;
goto next;
2021-12-15 21:29:12 +03:00
}
while (config_addr[i] != '\n' && config_addr[i] != 0) {
i++;
if (i >= config_size) {
bad_config = true;
panic(true, "config: Malformed macro usage");
}
}
2021-12-15 21:29:12 +03:00
continue;
}
next:
2021-12-15 21:29:12 +03:00
if (config_size - i >= 2 && memcmp(config_addr + i, "${", 2) == 0) {
char *macro_name = ext_mem_alloc(1024);
i += 2;
size_t j;
for (j = 0; config_addr[i] != '}' && config_addr[i] != '\n' && config_addr[i] != 0; j++, i++) {
macro_name[j] = config_addr[i];
}
if (config_addr[i] != '}') {
bad_config = true;
panic(true, "config: Malformed macro usage");
}
i++;
macro_name[j] = 0;
char *macro_value = "";
struct macro *macro = macros;
for (;;) {
if (macro == NULL) {
break;
}
if (strcmp(macro->name, macro_name) == 0) {
macro_value = macro->value;
break;
}
macro = macro->next;
}
pmm_free(macro_name, 1024);
for (j = 0; macro_value[j] != 0; j++, in++) {
2021-12-15 21:45:54 +03:00
if (in >= new_config_size) {
goto overflow;
}
2021-12-15 21:29:12 +03:00
new_config[in] = macro_value[j];
}
continue;
}
2021-12-15 21:45:54 +03:00
if (in >= new_config_size) {
overflow:
bad_config = true;
panic(true, "config: Macro-induced buffer overflow");
}
2021-12-15 21:29:12 +03:00
new_config[in++] = config_addr[i++];
}
2021-12-15 21:45:54 +03:00
pmm_free(config_addr, config_size);
2021-12-15 21:29:12 +03:00
config_addr = new_config;
config_size = in;
// Free macros
struct macro *macro = macros;
for (;;) {
if (macro == NULL) {
break;
}
struct macro *next = macro->next;
pmm_free(macro, sizeof(struct macro));
macro = next;
}
}
2020-10-20 03:38:44 +03:00
config_ready = true;
menu_tree = create_menu_tree(NULL, 1, 0);
size_t s;
char *c = config_get_entry(&s, 0);
while (*c != ':') {
c--;
}
if (c > config_addr) {
c[-1] = 0;
}
2020-03-30 22:24:36 +03:00
return 0;
}
static bool config_get_entry_name(char *ret, size_t index, size_t limit) {
if (!config_ready)
return false;
2020-04-21 17:23:30 +03:00
char *p = config_addr;
for (size_t i = 0; i <= index; i++) {
while (*p != ':') {
if (!*p)
return false;
2020-04-21 17:23:30 +03:00
p++;
}
p++;
2020-05-27 10:24:59 +03:00
if ((p - 1) != config_addr && *(p - 2) != '\n')
i--;
2020-04-21 17:23:30 +03:00
}
p--;
2020-04-21 17:23:30 +03:00
size_t i;
for (i = 0; i < (limit - 1); i++) {
if (p[i] == SEPARATOR)
break;
ret[i] = p[i];
}
ret[i] = 0;
return true;
2020-04-21 17:23:30 +03:00
}
static char *config_get_entry(size_t *size, size_t index) {
if (!config_ready)
return NULL;
char *ret;
2020-04-21 17:23:30 +03:00
char *p = config_addr;
for (size_t i = 0; i <= index; i++) {
while (*p != ':') {
if (!*p)
return NULL;
2020-04-21 17:23:30 +03:00
p++;
}
2020-04-21 19:27:05 +03:00
p++;
2020-05-27 10:24:59 +03:00
if ((p - 1) != config_addr && *(p - 2) != '\n')
i--;
2020-04-21 17:23:30 +03:00
}
do {
p++;
} while (*p != '\n');
ret = p;
2020-04-21 17:23:30 +03:00
cont:
2020-04-21 19:27:05 +03:00
while (*p != ':' && *p)
p++;
2020-04-21 17:23:30 +03:00
if (*p && *(p - 1) != '\n') {
p++;
goto cont;
}
*size = p - ret;
2020-04-21 17:23:30 +03:00
return ret;
2020-04-21 17:23:30 +03:00
}
static const char *lastkey;
struct conf_tuple config_get_tuple(const char *config, size_t index,
const char *key1, const char *key2) {
struct conf_tuple conf_tuple;
conf_tuple.value1 = config_get_value(config, index, key1);
if (conf_tuple.value1 == NULL) {
return (struct conf_tuple){0};
}
conf_tuple.value2 = config_get_value(lastkey, 0, key2);
const char *lk1 = lastkey;
const char *next_value1 = config_get_value(config, index + 1, key1);
const char *lk2 = lastkey;
if (conf_tuple.value2 != NULL && next_value1 != NULL) {
if ((uintptr_t)lk1 > (uintptr_t)lk2) {
conf_tuple.value2 = NULL;
}
}
return conf_tuple;
}
char *config_get_value(const char *config, size_t index, const char *key) {
2021-04-11 03:30:48 +03:00
if (!key || !config_ready)
2020-01-22 09:13:19 +03:00
return NULL;
if (config == NULL)
config = config_addr;
2020-01-22 09:13:19 +03:00
size_t key_len = strlen(key);
for (size_t i = 0; config[i]; i++) {
if (!strncmp(&config[i], key, key_len) && config[i + key_len] == '=') {
if (i && config[i - 1] != SEPARATOR)
2020-01-22 09:13:19 +03:00
continue;
2020-03-30 23:27:15 +03:00
if (index--)
continue;
2020-01-22 09:13:19 +03:00
i += key_len + 1;
size_t value_len;
for (value_len = 0;
config[i + value_len] != SEPARATOR && config[i + value_len];
value_len++);
char *buf = ext_mem_alloc(value_len + 1);
memcpy(buf, config + i, value_len);
lastkey = config + i;
2020-01-22 09:13:19 +03:00
return buf;
}
}
return NULL;
}