a957b51298
In practice this was not a problem because everything so far passes vfs_root to vfs_mount_or_decref().
228 lines
4.7 KiB
C
228 lines
4.7 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#include "taisei.h"
|
|
|
|
#include "private.h"
|
|
#include "vdir.h"
|
|
|
|
VFSNode *vfs_root;
|
|
|
|
typedef struct vfs_tls_s {
|
|
char *error_str;
|
|
} vfs_tls_t;
|
|
|
|
typedef struct vfs_shutdownhook_t {
|
|
LIST_INTERFACE(struct vfs_shutdownhook_t);
|
|
VFSShutdownHandler func;
|
|
void *arg;
|
|
} vfs_shutdownhook_t;
|
|
|
|
static SDL_TLSID vfs_tls_id;
|
|
static vfs_tls_t *vfs_tls_fallback;
|
|
static vfs_shutdownhook_t *shutdown_hooks;
|
|
|
|
static void vfs_free(VFSNode *node);
|
|
|
|
static void vfs_tls_free(void *vtls) {
|
|
if(vtls) {
|
|
vfs_tls_t *tls = vtls;
|
|
mem_free(tls->error_str);
|
|
mem_free(tls);
|
|
}
|
|
}
|
|
|
|
static vfs_tls_t* vfs_tls_get(void) {
|
|
if(vfs_tls_id) {
|
|
vfs_tls_t *tls = SDL_TLSGet(vfs_tls_id);
|
|
|
|
if(!tls) {
|
|
SDL_TLSSet(vfs_tls_id, ALLOC(vfs_tls_t), vfs_tls_free);
|
|
tls = SDL_TLSGet(vfs_tls_id);
|
|
}
|
|
|
|
assert(tls != NULL);
|
|
return tls;
|
|
}
|
|
|
|
assert(vfs_tls_fallback != NULL);
|
|
return vfs_tls_fallback;
|
|
}
|
|
|
|
void vfs_init(void) {
|
|
vfs_root = vfs_vdir_create();
|
|
vfs_tls_id = SDL_TLSCreate();
|
|
|
|
if(vfs_tls_id) {
|
|
vfs_tls_fallback = NULL;
|
|
} else {
|
|
log_warn("SDL_TLSCreate(): failed: %s", SDL_GetError());
|
|
vfs_tls_fallback = ALLOC(typeof(*vfs_tls_fallback));
|
|
}
|
|
}
|
|
|
|
static void* call_shutdown_hook(List **vlist, List *vhook, void *arg) {
|
|
vfs_shutdownhook_t *hook = (vfs_shutdownhook_t*)vhook;
|
|
hook->func(hook->arg);
|
|
mem_free(list_unlink(vlist, vhook));
|
|
return NULL;
|
|
}
|
|
|
|
void vfs_shutdown(void) {
|
|
vfs_sync(VFS_SYNC_STORE, NO_CALLCHAIN);
|
|
|
|
list_foreach(&shutdown_hooks, call_shutdown_hook, NULL);
|
|
|
|
vfs_decref(vfs_root);
|
|
vfs_tls_free(vfs_tls_fallback);
|
|
|
|
vfs_root = NULL;
|
|
vfs_tls_id = 0;
|
|
vfs_tls_fallback = NULL;
|
|
}
|
|
|
|
void vfs_hook_on_shutdown(VFSShutdownHandler func, void *arg) {
|
|
list_append(&shutdown_hooks, ALLOC(vfs_shutdownhook_t, {
|
|
.func = func,
|
|
.arg = arg,
|
|
}));
|
|
}
|
|
|
|
static void vfs_free(VFSNode *node) {
|
|
if(!node) {
|
|
return;
|
|
}
|
|
|
|
if(node->funcs && node->funcs->free) {
|
|
node->funcs->free(node);
|
|
}
|
|
|
|
mem_free(node);
|
|
}
|
|
|
|
void (vfs_incref)(VFSNode *node) {
|
|
SDL_AtomicIncRef(&node->refcount);
|
|
}
|
|
|
|
bool (vfs_decref)(VFSNode *node) {
|
|
if(!node) {
|
|
return true;
|
|
}
|
|
|
|
if(SDL_AtomicDecRef(&node->refcount)) {
|
|
vfs_free(node);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
VFSNode *(vfs_locate)(VFSNode *root, const char *path) {
|
|
if(!*path) {
|
|
vfs_incref(root);
|
|
return root;
|
|
}
|
|
|
|
return vfs_node_locate(root, path);
|
|
}
|
|
|
|
bool vfs_mount(VFSNode *root, const char *mountpoint, VFSNode *subtree) {
|
|
VFSNode *mpnode;
|
|
char buf[2][strlen(mountpoint)+1];
|
|
char *mpbase, *mpname;
|
|
bool result = false;
|
|
|
|
mountpoint = vfs_path_normalize(mountpoint, buf[0]);
|
|
strcpy(buf[1], buf[0]);
|
|
vfs_path_split_right(buf[1], &mpbase, &mpname);
|
|
|
|
if((mpnode = vfs_locate(root, mountpoint))) {
|
|
// mountpoint already exists - try to merge with the target node
|
|
|
|
result = vfs_node_mount(mpnode, NULL, subtree);
|
|
|
|
if(!result) {
|
|
vfs_set_error("Mountpoint '%s' already exists, merging failed: %s", mountpoint, vfs_get_error());
|
|
}
|
|
|
|
vfs_decref(mpnode);
|
|
return result;
|
|
}
|
|
|
|
if((mpnode = vfs_locate(root, mpbase))) {
|
|
// try to become a subnode of parent (conventional mount)
|
|
|
|
result = vfs_node_mount(mpnode, mpname, subtree);
|
|
|
|
if(!result) {
|
|
vfs_set_error("Can't create mountpoint '%s' in '%s': %s", mountpoint, mpbase, vfs_get_error());
|
|
}
|
|
|
|
vfs_decref(mpnode);
|
|
}
|
|
|
|
// error set by vfs_locate
|
|
return result;
|
|
}
|
|
|
|
bool vfs_mount_or_decref(VFSNode *root, const char *mountpoint, VFSNode *subtree) {
|
|
if(!vfs_mount(root, mountpoint, subtree)) {
|
|
vfs_decref(subtree);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void vfs_print_tree_recurse(SDL_RWops *dest, VFSNode *root, char *prefix, const char *name) {
|
|
void *o = NULL;
|
|
bool is_dir = vfs_node_query(root).is_dir;
|
|
char *newprefix = strfmt("%s%s%s", prefix, name, is_dir ? VFS_PATH_SEPARATOR_STR : "");
|
|
char *r;
|
|
|
|
r = vfs_node_repr(root, false);
|
|
SDL_RWprintf(dest, "%s = %s\n", newprefix, r);
|
|
mem_free(r);
|
|
|
|
if(!is_dir) {
|
|
mem_free(newprefix);
|
|
return;
|
|
}
|
|
|
|
for(const char *n; (n = vfs_node_iter(root, &o));) {
|
|
VFSNode *node = vfs_locate(root, n);
|
|
if(node) {
|
|
vfs_print_tree_recurse(dest, node, newprefix, n);
|
|
vfs_decref(node);
|
|
}
|
|
}
|
|
|
|
vfs_node_iter_stop(root, &o);
|
|
mem_free(newprefix);
|
|
}
|
|
|
|
const char* vfs_get_error(void) {
|
|
vfs_tls_t *tls = vfs_tls_get();
|
|
return tls->error_str ? tls->error_str : "No error";
|
|
}
|
|
|
|
void (vfs_set_error)(char *fmt, ...) {
|
|
vfs_tls_t *tls = vfs_tls_get();
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
char *err = vstrfmt(fmt, args);
|
|
mem_free(tls->error_str);
|
|
tls->error_str = err;
|
|
va_end(args);
|
|
|
|
// log_debug("%s", tls->error_str);
|
|
}
|
|
|
|
void vfs_set_error_from_sdl(void) {
|
|
vfs_set_error("SDL error: %s", SDL_GetError());
|
|
}
|