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 "vdir.h"
12 
13 #define VDIR_TABLE(vdir) ((ht_str2ptr_t*)((vdir)->data1))
14 
vfs_vdir_attach_node(VFSNode * vdir,const char * name,VFSNode * node)15 static void vfs_vdir_attach_node(VFSNode *vdir, const char *name, VFSNode *node) {
16 	VFSNode *oldnode = ht_get(VDIR_TABLE(vdir), name, NULL);
17 
18 	if(oldnode) {
19 		vfs_decref(oldnode);
20 	}
21 
22 	ht_set(VDIR_TABLE(vdir), name, node);
23 }
24 
vfs_vdir_locate(VFSNode * vdir,const char * path)25 static VFSNode* vfs_vdir_locate(VFSNode *vdir, const char *path) {
26 	VFSNode *node;
27 	char mutpath[strlen(path)+1];
28 	char *primpath, *subpath;
29 
30 	strcpy(mutpath, path);
31 	vfs_path_split_left(mutpath, &primpath, &subpath);
32 
33 	if((node = ht_get(VDIR_TABLE(vdir), mutpath, NULL))) {
34 		return vfs_locate(node, subpath);
35 	}
36 
37 	return NULL;
38 }
39 
vfs_vdir_iter(VFSNode * vdir,void ** opaque)40 static const char* vfs_vdir_iter(VFSNode *vdir, void **opaque) {
41 	ht_str2ptr_iter_t *iter = *opaque;
42 
43 	if(!iter) {
44 		iter = *opaque = calloc(1, sizeof(*iter));
45 		ht_iter_begin(VDIR_TABLE(vdir), iter);
46 	} else {
47 		ht_iter_next(iter);
48 	}
49 
50 	return iter->has_data ? iter->key : NULL;
51 }
52 
vfs_vdir_iter_stop(VFSNode * vdir,void ** opaque)53 static void vfs_vdir_iter_stop(VFSNode *vdir, void **opaque) {
54 	if(*opaque) {
55 		ht_iter_end((ht_str2ptr_iter_t*)*opaque);
56 		free(*opaque);
57 		*opaque = NULL;
58 	}
59 }
60 
vfs_vdir_query(VFSNode * vdir)61 static VFSInfo vfs_vdir_query(VFSNode *vdir) {
62 	return (VFSInfo) {
63 		.exists = true,
64 		.is_dir = true,
65 	};
66 }
67 
vfs_vdir_free(VFSNode * vdir)68 static void vfs_vdir_free(VFSNode *vdir) {
69 	ht_str2ptr_t *ht = VDIR_TABLE(vdir);
70 	ht_str2ptr_iter_t iter;
71 
72 	ht_iter_begin(ht, &iter);
73 
74 	for(; iter.has_data; ht_iter_next(&iter)) {
75 		vfs_decref(iter.value);
76 	}
77 
78 	ht_iter_end(&iter);
79 	ht_destroy(ht);
80 	free(ht);
81 }
82 
vfs_vdir_mount(VFSNode * vdir,const char * mountpoint,VFSNode * subtree)83 static bool vfs_vdir_mount(VFSNode *vdir, const char *mountpoint, VFSNode *subtree) {
84 	if(!mountpoint) {
85 		vfs_set_error("Virtual directories don't support merging");
86 		return false;
87 	}
88 
89 	vfs_vdir_attach_node(vdir, mountpoint, subtree);
90 	return true;
91 }
92 
vfs_vdir_unmount(VFSNode * vdir,const char * mountpoint)93 static bool vfs_vdir_unmount(VFSNode *vdir, const char *mountpoint) {
94 	VFSNode *mountee;
95 
96 	if(!(mountee = ht_get(VDIR_TABLE(vdir), mountpoint, NULL))) {
97 		vfs_set_error("Mountpoint '%s' doesn't exist", mountpoint);
98 		return false;
99 	}
100 
101 	ht_unset(VDIR_TABLE(vdir), mountpoint);
102 	vfs_decref(mountee);
103 	return true;
104 }
105 
vfs_vdir_mkdir(VFSNode * node,const char * subdir)106 static bool vfs_vdir_mkdir(VFSNode *node, const char *subdir) {
107 	if(!subdir) {
108 		vfs_set_error("Virtual directory trying to create itself? How did you even get here?");
109 		return false;
110 	}
111 
112 	VFSNode *subnode = vfs_alloc();
113 	vfs_vdir_init(subnode);
114 	vfs_vdir_mount(node, subdir, subnode);
115 
116 	return true;
117 }
118 
vfs_vdir_repr(VFSNode * node)119 static char* vfs_vdir_repr(VFSNode *node) {
120 	return strdup("virtual directory");
121 }
122 
123 static VFSNodeFuncs vfs_funcs_vdir = {
124 	.repr = vfs_vdir_repr,
125 	.query = vfs_vdir_query,
126 	.free = vfs_vdir_free,
127 	.mount = vfs_vdir_mount,
128 	.unmount = vfs_vdir_unmount,
129 	.locate = vfs_vdir_locate,
130 	.iter = vfs_vdir_iter,
131 	.iter_stop = vfs_vdir_iter_stop,
132 	.mkdir = vfs_vdir_mkdir,
133 };
134 
vfs_vdir_init(VFSNode * node)135 void vfs_vdir_init(VFSNode *node) {
136 	node->funcs = &vfs_funcs_vdir;
137 	node->data1 = ht_str2ptr_new();
138 }
139