feat: multi-user system, bug fixes, security & performance fixes, and more
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user