1 /*
2 * This software is licensed under the terms of the MIT License.
3 * See COPYING for further information.
4 * ---
5 * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
6 * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
7 */
8
9 #include "taisei.h"
10
11 #include "private.h"
12 #include "vdir.h"
13
14 VFSNode *vfs_root;
15
16 typedef struct vfs_tls_s {
17 char *error_str;
18 } vfs_tls_t;
19
20 typedef struct vfs_shutdownhook_t {
21 LIST_INTERFACE(struct vfs_shutdownhook_t);
22 VFSShutdownHandler func;
23 void *arg;
24 } vfs_shutdownhook_t;
25
26 static SDL_TLSID vfs_tls_id;
27 static vfs_tls_t *vfs_tls_fallback;
28 static vfs_shutdownhook_t *shutdown_hooks;
29
30 static void vfs_free(VFSNode *node);
31
vfs_tls_free(void * vtls)32 static void vfs_tls_free(void *vtls) {
33 if(vtls) {
34 vfs_tls_t *tls = vtls;
35 free(tls->error_str);
36 free(tls);
37 }
38 }
39
vfs_tls_get(void)40 static vfs_tls_t* vfs_tls_get(void) {
41 if(vfs_tls_id) {
42 vfs_tls_t *tls = SDL_TLSGet(vfs_tls_id);
43
44 if(!tls) {
45 SDL_TLSSet(vfs_tls_id, calloc(1, sizeof(vfs_tls_t)), vfs_tls_free);
46 tls = SDL_TLSGet(vfs_tls_id);
47 }
48
49 assert(tls != NULL);
50 return tls;
51 }
52
53 assert(vfs_tls_fallback != NULL);
54 return vfs_tls_fallback;
55 }
56
vfs_init(void)57 void vfs_init(void) {
58 vfs_root = vfs_alloc();
59 vfs_vdir_init(vfs_root);
60
61 vfs_tls_id = SDL_TLSCreate();
62
63 if(vfs_tls_id) {
64 vfs_tls_fallback = NULL;
65 } else {
66 log_warn("SDL_TLSCreate(): failed: %s", SDL_GetError());
67 vfs_tls_fallback = calloc(1, sizeof(vfs_tls_t));
68 }
69 }
70
call_shutdown_hook(List ** vlist,List * vhook,void * arg)71 static void* call_shutdown_hook(List **vlist, List *vhook, void *arg) {
72 vfs_shutdownhook_t *hook = (vfs_shutdownhook_t*)vhook;
73 hook->func(hook->arg);
74 free(list_unlink(vlist, vhook));
75 return NULL;
76 }
77
vfs_shutdown(void)78 void vfs_shutdown(void) {
79 vfs_sync(VFS_SYNC_STORE, NO_CALLCHAIN);
80
81 list_foreach(&shutdown_hooks, call_shutdown_hook, NULL);
82
83 vfs_decref(vfs_root);
84 vfs_tls_free(vfs_tls_fallback);
85
86 vfs_root = NULL;
87 vfs_tls_id = 0;
88 vfs_tls_fallback = NULL;
89 }
90
vfs_hook_on_shutdown(VFSShutdownHandler func,void * arg)91 void vfs_hook_on_shutdown(VFSShutdownHandler func, void *arg) {
92 vfs_shutdownhook_t *hook = malloc(sizeof(vfs_shutdownhook_t));
93 hook->func = func;
94 hook->arg = arg;
95 list_append(&shutdown_hooks, hook);
96 }
97
vfs_alloc(void)98 VFSNode* vfs_alloc(void) {
99 VFSNode *node = calloc(1, sizeof(VFSNode));
100 vfs_incref(node);
101 return node;
102 }
103
vfs_free(VFSNode * node)104 static void vfs_free(VFSNode *node) {
105 if(!node) {
106 return;
107 }
108
109 if(node->funcs && node->funcs->free) {
110 node->funcs->free(node);
111 }
112
113 free(node);
114 }
115
vfs_incref(VFSNode * node)116 void vfs_incref(VFSNode *node) {
117 SDL_AtomicIncRef(&node->refcount);
118 }
119
vfs_decref(VFSNode * node)120 bool vfs_decref(VFSNode *node) {
121 if(!node) {
122 return true;
123 }
124
125 if(SDL_AtomicDecRef(&node->refcount)) {
126 vfs_free(node);
127 return true;
128 }
129
130 return false;
131 }
132
vfs_locate(VFSNode * root,const char * path)133 VFSNode* vfs_locate(VFSNode *root, const char *path) {
134 if(!*path) {
135 vfs_incref(root);
136 return root;
137 }
138
139 return vfs_node_locate(root, path);
140 }
141
vfs_mount(VFSNode * root,const char * mountpoint,VFSNode * subtree)142 bool vfs_mount(VFSNode *root, const char *mountpoint, VFSNode *subtree) {
143 VFSNode *mpnode;
144 char buf[2][strlen(mountpoint)+1];
145 char *mpbase, *mpname;
146 bool result = false;
147
148 mountpoint = vfs_path_normalize(mountpoint, buf[0]);
149 strcpy(buf[1], buf[0]);
150 vfs_path_split_right(buf[1], &mpbase, &mpname);
151
152 if((mpnode = vfs_locate(root, mountpoint))) {
153 // mountpoint already exists - try to merge with the target node
154
155 result = vfs_node_mount(mpnode, NULL, subtree);
156
157 if(!result) {
158 vfs_set_error("Mountpoint '%s' already exists, merging failed: %s", mountpoint, vfs_get_error());
159 }
160
161 vfs_decref(mpnode);
162 return result;
163 }
164
165 if((mpnode = vfs_locate(root, mpbase))) {
166 // try to become a subnode of parent (conventional mount)
167
168 result = vfs_node_mount(mpnode, mpname, subtree);
169
170 if(!result) {
171 vfs_set_error("Can't create mountpoint '%s' in '%s': %s", mountpoint, mpbase, vfs_get_error());
172 }
173
174 vfs_decref(mpnode);
175 }
176
177 // error set by vfs_locate
178 return result;
179 }
180
vfs_mount_or_decref(VFSNode * root,const char * mountpoint,VFSNode * subtree)181 bool vfs_mount_or_decref(VFSNode *root, const char *mountpoint, VFSNode *subtree) {
182 if(!vfs_mount(vfs_root, mountpoint, subtree)) {
183 vfs_decref(subtree);
184 return false;
185 }
186
187 return true;
188 }
189
vfs_print_tree_recurse(SDL_RWops * dest,VFSNode * root,char * prefix,const char * name)190 void vfs_print_tree_recurse(SDL_RWops *dest, VFSNode *root, char *prefix, const char *name) {
191 void *o = NULL;
192 bool is_dir = vfs_node_query(root).is_dir;
193 char *newprefix = strfmt("%s%s%s", prefix, name, is_dir ? VFS_PATH_SEPARATOR_STR : "");
194 char *r;
195
196 SDL_RWprintf(dest, "%s = %s\n", newprefix, r = vfs_node_repr(root, false));
197 free(r);
198
199 if(!is_dir) {
200 free(newprefix);
201 return;
202 }
203
204 for(const char *n; (n = vfs_node_iter(root, &o));) {
205 VFSNode *node = vfs_locate(root, n);
206 if(node) {
207 vfs_print_tree_recurse(dest, node, newprefix, n);
208 vfs_decref(node);
209 }
210 }
211
212 vfs_node_iter_stop(root, &o);
213 free(newprefix);
214 }
215
vfs_get_error(void)216 const char* vfs_get_error(void) {
217 vfs_tls_t *tls = vfs_tls_get();
218 return tls->error_str ? tls->error_str : "No error";
219 }
220
vfs_set_error(char * fmt,...)221 void vfs_set_error(char *fmt, ...) {
222 vfs_tls_t *tls = vfs_tls_get();
223 va_list args;
224 va_start(args, fmt);
225 char *err = vstrfmt(fmt, args);
226 free(tls->error_str);
227 tls->error_str = err;
228 va_end(args);
229
230 log_debug("%s", tls->error_str);
231 }
232
vfs_set_error_from_sdl(void)233 void vfs_set_error_from_sdl(void) {
234 vfs_set_error("SDL error: %s", SDL_GetError());
235 }
236