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