feat: multi-user system, bug fixes, security & performance fixes, and more

This commit is contained in:
2026-03-14 13:28:46 +01:00
parent 576ad34f95
commit 261b536041
389 changed files with 231853 additions and 591 deletions
+110 -12
View File
@@ -247,14 +247,98 @@ namespace config {
char* text = serialize(doc);
int textLen = montauk::slen(text);
// Try to open existing file, create if not found
// Delete and recreate to ensure file is exactly the right size
// (no ftruncate syscall available, so this avoids stale trailing data)
montauk::fdelete(path);
int handle = montauk::fcreate(path);
if (handle < 0) {
montauk::mfree(text);
return -1;
}
int ret = montauk::fwrite(handle, (const uint8_t*)text, 0, textLen);
montauk::close(handle);
montauk::mfree(text);
return ret < 0 ? ret : 0;
}
// ---- Per-user config ----
// Build path: "0:/users/<username>/config/<name>.toml"
inline void build_user_path(char* out, int outSz, const char* username, const char* name) {
int p = 0;
const char* prefix = "0:/users/";
while (*prefix && p < outSz - 2) out[p++] = *prefix++;
while (*username && p < outSz - 2) out[p++] = *username++;
const char* mid = "/config/";
while (*mid && p < outSz - 2) out[p++] = *mid++;
while (*name && p < outSz - 6) out[p++] = *name++;
const char* ext = ".toml";
while (*ext && p < outSz - 1) out[p++] = *ext++;
out[p] = '\0';
}
// Ensure per-user config directory exists
inline void ensure_user_dir(const char* username) {
char dir[128];
int p = 0;
const char* prefix = "0:/users/";
while (*prefix && p < 126) dir[p++] = *prefix++;
while (*username && p < 126) dir[p++] = *username++;
dir[p] = '\0';
montauk::fmkdir(dir);
const char* suffix = "/config";
while (*suffix && p < 126) dir[p++] = *suffix++;
dir[p] = '\0';
montauk::fmkdir(dir);
}
// Load a per-user config file
inline toml::Doc load_user(const char* username, const char* name) {
char path[192];
build_user_path(path, sizeof(path), username, name);
int handle = montauk::open(path);
if (handle < 0) {
handle = montauk::fcreate(path);
if (handle < 0) {
montauk::mfree(text);
return -1;
}
toml::Doc doc;
doc.init();
return doc;
}
uint64_t size = montauk::getsize(handle);
if (size == 0) {
montauk::close(handle);
toml::Doc doc;
doc.init();
return doc;
}
char* text = (char*)montauk::malloc(size + 1);
montauk::read(handle, (uint8_t*)text, 0, size);
montauk::close(handle);
text[size] = '\0';
toml::Doc doc = toml::parse(text);
montauk::mfree(text);
return doc;
}
// Save a per-user config file
inline int save_user(const char* username, const char* name, toml::Doc* doc) {
ensure_user_dir(username);
char path[192];
build_user_path(path, sizeof(path), username, name);
char* text = serialize(doc);
int textLen = montauk::slen(text);
montauk::fdelete(path);
int handle = montauk::fcreate(path);
if (handle < 0) {
montauk::mfree(text);
return -1;
}
int ret = montauk::fwrite(handle, (const uint8_t*)text, 0, textLen);
@@ -272,12 +356,28 @@ namespace config {
// ---- In-place modification helpers ----
// Free any dynamically allocated data owned by a value (without freeing key)
namespace detail {
inline void free_value_data(toml::Value* v) {
if (v->type == toml::Type::String && v->str)
montauk::mfree(v->str);
else if (v->type == toml::Type::Array || v->type == toml::Type::Table) {
for (int i = 0; i < v->array.count; i++) {
if (v->array.items[i]) v->array.items[i]->destroy();
}
if (v->array.items) montauk::mfree(v->array.items);
v->array.items = nullptr;
v->array.count = 0;
v->array.cap = 0;
}
}
}
// Set or overwrite a string value in the doc.
inline void set_string(toml::Doc* doc, const char* key, const char* val) {
auto* existing = doc->get(key);
if (existing) {
if (existing->type == toml::Type::String && existing->str)
montauk::mfree(existing->str);
detail::free_value_data(existing);
existing->type = toml::Type::String;
existing->str = toml::Value::dup(val);
} else {
@@ -289,8 +389,7 @@ namespace config {
inline void set_int(toml::Doc* doc, const char* key, int64_t val) {
auto* existing = doc->get(key);
if (existing) {
if (existing->type == toml::Type::String && existing->str)
montauk::mfree(existing->str);
detail::free_value_data(existing);
existing->type = toml::Type::Int;
existing->ival = val;
} else {
@@ -302,8 +401,7 @@ namespace config {
inline void set_bool(toml::Doc* doc, const char* key, bool val) {
auto* existing = doc->get(key);
if (existing) {
if (existing->type == toml::Type::String && existing->str)
montauk::mfree(existing->str);
detail::free_value_data(existing);
existing->type = toml::Type::Bool;
existing->bval = val;
} else {