virtdir.c revision 1.9 1 1.1 agc /*
2 1.1 agc * Copyright 2007 Alistair Crooks. All rights reserved.
3 1.1 agc *
4 1.1 agc * Redistribution and use in source and binary forms, with or without
5 1.1 agc * modification, are permitted provided that the following conditions
6 1.1 agc * are met:
7 1.1 agc * 1. Redistributions of source code must retain the above copyright
8 1.1 agc * notice, this list of conditions and the following disclaimer.
9 1.1 agc * 2. Redistributions in binary form must reproduce the above copyright
10 1.1 agc * notice, this list of conditions and the following disclaimer in the
11 1.1 agc * documentation and/or other materials provided with the distribution.
12 1.1 agc * 3. The name of the author may not be used to endorse or promote
13 1.1 agc * products derived from this software without specific prior written
14 1.1 agc * permission.
15 1.1 agc *
16 1.1 agc * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 1.1 agc * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 1.1 agc * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.1 agc * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 1.1 agc * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 1.1 agc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
22 1.1 agc * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 agc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 1.1 agc * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 1.1 agc * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 1.1 agc * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 1.1 agc */
28 1.1 agc #include <sys/types.h>
29 1.1 agc #include <sys/stat.h>
30 1.1 agc
31 1.9 agc #define FUSE_USE_VERSION 26
32 1.9 agc
33 1.1 agc #include <fuse.h>
34 1.1 agc #include <stdio.h>
35 1.1 agc #include <stdlib.h>
36 1.1 agc #include <unistd.h>
37 1.1 agc
38 1.1 agc #include "virtdir.h"
39 1.1 agc #include "defs.h"
40 1.1 agc
41 1.1 agc /* utility comparison routine for sorting and searching */
42 1.1 agc static int
43 1.1 agc compare(const void *vp1, const void *vp2)
44 1.1 agc {
45 1.1 agc const virt_dirent_t *tp1 = (const virt_dirent_t *) vp1;
46 1.1 agc const virt_dirent_t *tp2 = (const virt_dirent_t *) vp2;
47 1.1 agc
48 1.1 agc return strcmp(tp1->name, tp2->name);
49 1.1 agc }
50 1.1 agc
51 1.1 agc /* save `n' chars of `s' in allocated storage */
52 1.1 agc static char *
53 1.1 agc strnsave(const char *s, int n)
54 1.1 agc {
55 1.1 agc char *cp;
56 1.1 agc
57 1.1 agc if (n < 0) {
58 1.1 agc n = strlen(s);
59 1.1 agc }
60 1.1 agc NEWARRAY(char, cp, n + 1, "strnsave", return NULL);
61 1.1 agc (void) memcpy(cp, s, n);
62 1.1 agc cp[n] = 0x0;
63 1.1 agc return cp;
64 1.1 agc }
65 1.1 agc
66 1.3 agc /* ensure intermediate directories exist */
67 1.3 agc static void
68 1.3 agc mkdirs(virtdir_t *tp, const char *path, size_t size)
69 1.3 agc {
70 1.3 agc virt_dirent_t *ep;
71 1.3 agc char name[MAXPATHLEN];
72 1.3 agc char *slash;
73 1.3 agc
74 1.3 agc (void) strlcpy(name, path, sizeof(name));
75 1.3 agc for (slash = name + 1 ; (slash = strchr(slash + 1, '/')) != NULL ; ) {
76 1.3 agc *slash = 0x0;
77 1.3 agc if ((ep = virtdir_find(tp, name, strlen(name))) == NULL) {
78 1.5 agc virtdir_add(tp, name, strlen(name), 'd', NULL, 0);
79 1.3 agc }
80 1.3 agc *slash = '/';
81 1.3 agc }
82 1.3 agc }
83 1.3 agc
84 1.6 agc /* get rid of multiple slashes in input */
85 1.6 agc static int
86 1.6 agc normalise(const char *name, size_t namelen, char *path, size_t pathsize)
87 1.6 agc {
88 1.6 agc const char *np;
89 1.6 agc char *pp;
90 1.6 agc int done;
91 1.6 agc
92 1.6 agc for (pp = path, np = name, done = 0 ; !done && (int)(pp - path) < pathsize - 1 && (int)(np - name) <= namelen ; ) {
93 1.6 agc switch(*np) {
94 1.6 agc case '/':
95 1.6 agc if (pp == path || *(pp - 1) != '/') {
96 1.6 agc *pp++ = *np;
97 1.6 agc }
98 1.6 agc np += 1;
99 1.6 agc break;
100 1.6 agc case 0x0:
101 1.6 agc done = 1;
102 1.6 agc break;
103 1.6 agc default:
104 1.6 agc *pp++ = *np++;
105 1.6 agc break;
106 1.6 agc }
107 1.6 agc }
108 1.6 agc /* XXX - trailing slash? */
109 1.6 agc *pp = 0x0;
110 1.6 agc return (int)(pp - path);
111 1.6 agc }
112 1.6 agc
113 1.1 agc /* initialise the tree */
114 1.1 agc int
115 1.6 agc virtdir_init(virtdir_t *tp, const char *rootdir, struct stat *d, struct stat *f, struct stat *l)
116 1.1 agc {
117 1.1 agc (void) memcpy(&tp->dir, d, sizeof(tp->dir));
118 1.1 agc tp->dir.st_mode = S_IFDIR | 0755;
119 1.1 agc tp->dir.st_nlink = 2;
120 1.1 agc (void) memcpy(&tp->file, f, sizeof(tp->file));
121 1.1 agc tp->file.st_mode = S_IFREG | 0644;
122 1.1 agc tp->file.st_nlink = 1;
123 1.1 agc (void) memcpy(&tp->lnk, l, sizeof(tp->lnk));
124 1.1 agc tp->lnk.st_mode = S_IFLNK | 0644;
125 1.1 agc tp->lnk.st_nlink = 1;
126 1.6 agc if (rootdir != NULL) {
127 1.6 agc tp->rootdir = strdup(rootdir);
128 1.6 agc }
129 1.1 agc return 1;
130 1.1 agc }
131 1.1 agc
132 1.1 agc /* add an entry to the tree */
133 1.1 agc int
134 1.6 agc virtdir_add(virtdir_t *tp, const char *name, size_t size, uint8_t type, const char *tgt, size_t tgtlen)
135 1.1 agc {
136 1.6 agc struct stat st;
137 1.6 agc char path[MAXPATHLEN];
138 1.6 agc int pathlen;
139 1.1 agc
140 1.1 agc if (tp->v == NULL) {
141 1.1 agc (void) stat(".", &st);
142 1.6 agc virtdir_init(tp, NULL, &st, &st, &st);
143 1.1 agc }
144 1.6 agc pathlen = normalise(name, size, path, sizeof(path));
145 1.6 agc if (virtdir_find(tp, path, pathlen) != NULL) {
146 1.2 agc /* attempt to add a duplicate directory entry */
147 1.2 agc return 0;
148 1.2 agc }
149 1.1 agc ALLOC(virt_dirent_t, tp->v, tp->size, tp->c, 10, 10, "virtdir_add",
150 1.1 agc return 0);
151 1.6 agc tp->v[tp->c].namelen = pathlen;
152 1.6 agc if ((tp->v[tp->c].name = strnsave(path, pathlen)) == NULL) {
153 1.1 agc return 0;
154 1.1 agc }
155 1.1 agc tp->v[tp->c].d_name = strrchr(tp->v[tp->c].name, '/') + 1;
156 1.1 agc tp->v[tp->c].type = type;
157 1.8 agc tp->v[tp->c].ino = (ino_t) random() & 0xfffff;
158 1.1 agc if (tgt != NULL) {
159 1.5 agc tp->v[tp->c].tgtlen = tgtlen;
160 1.5 agc tp->v[tp->c].tgt = strnsave(tgt, tgtlen);
161 1.1 agc }
162 1.1 agc tp->c += 1;
163 1.1 agc qsort(tp->v, tp->c, sizeof(tp->v[0]), compare);
164 1.6 agc mkdirs(tp, path, pathlen);
165 1.1 agc return 1;
166 1.1 agc }
167 1.1 agc
168 1.2 agc /* delete an entry from the tree */
169 1.1 agc int
170 1.1 agc virtdir_del(virtdir_t *tp, const char *name, size_t size)
171 1.1 agc {
172 1.1 agc virt_dirent_t *ep;
173 1.1 agc int i;
174 1.1 agc
175 1.1 agc if ((ep = virtdir_find(tp, name, size)) == NULL) {
176 1.1 agc return 0;
177 1.1 agc }
178 1.9 agc i = (int)(ep - tp->v);
179 1.1 agc for (tp->c -= 1 ; i < tp->c ; i++) {
180 1.1 agc tp->v[i] = tp->v[i + 1];
181 1.1 agc }
182 1.1 agc return 1;
183 1.1 agc }
184 1.1 agc
185 1.1 agc /* find an entry in the tree */
186 1.1 agc virt_dirent_t *
187 1.1 agc virtdir_find(virtdir_t *tp, const char *name, size_t namelen)
188 1.1 agc {
189 1.1 agc virt_dirent_t e;
190 1.6 agc char path[MAXPATHLEN];
191 1.1 agc
192 1.1 agc (void) memset(&e, 0x0, sizeof(e));
193 1.6 agc e.namelen = normalise(name, namelen, path, sizeof(path));
194 1.6 agc e.name = path;
195 1.1 agc return bsearch(&e, tp->v, tp->c, sizeof(tp->v[0]), compare);
196 1.1 agc }
197 1.1 agc
198 1.4 agc /* return the virtual offset in the tree */
199 1.4 agc int
200 1.4 agc virtdir_offset(virtdir_t *tp, virt_dirent_t *dp)
201 1.4 agc {
202 1.5 agc return (int)(dp - tp->v);
203 1.4 agc }
204 1.4 agc
205 1.1 agc /* analogous to opendir(3) - open a directory, save information, and
206 1.1 agc * return a pointer to the dynamically allocated structure */
207 1.1 agc VIRTDIR *
208 1.1 agc openvirtdir(virtdir_t *tp, const char *d)
209 1.1 agc {
210 1.1 agc VIRTDIR *dirp;
211 1.1 agc
212 1.1 agc NEW(VIRTDIR, dirp, "openvirtdir", exit(EXIT_FAILURE));
213 1.1 agc dirp->dirname = strdup(d);
214 1.1 agc dirp->dirnamelen = strlen(d);
215 1.1 agc dirp->tp = tp;
216 1.1 agc dirp->i = 0;
217 1.1 agc return dirp;
218 1.1 agc }
219 1.1 agc
220 1.1 agc /* analogous to readdir(3) - read the next entry in the directory that
221 1.1 agc * was opened, and return a pointer to it */
222 1.1 agc virt_dirent_t *
223 1.1 agc readvirtdir(VIRTDIR *dirp)
224 1.1 agc {
225 1.1 agc char *from;
226 1.1 agc
227 1.1 agc for ( ; dirp->i < dirp->tp->c ; dirp->i++) {
228 1.1 agc from = (strcmp(dirp->dirname, "/") == 0) ?
229 1.6 agc &dirp->tp->v[dirp->i].name[1] :
230 1.6 agc &dirp->tp->v[dirp->i].name[dirp->dirnamelen + 1];
231 1.1 agc if (strncmp(dirp->tp->v[dirp->i].name, dirp->dirname,
232 1.1 agc dirp->dirnamelen) == 0 &&
233 1.3 agc *from != 0x0 &&
234 1.1 agc strchr(from, '/') == NULL) {
235 1.1 agc return &dirp->tp->v[dirp->i++];
236 1.1 agc }
237 1.1 agc }
238 1.1 agc return NULL;
239 1.1 agc }
240 1.1 agc
241 1.1 agc /* free the storage associated with the virtual directory structure */
242 1.1 agc void
243 1.1 agc closevirtdir(VIRTDIR *dirp)
244 1.1 agc {
245 1.1 agc free(dirp->dirname);
246 1.1 agc FREE(dirp);
247 1.1 agc }
248 1.1 agc
249 1.1 agc /* find a target in the tree -- not quick! */
250 1.1 agc virt_dirent_t *
251 1.1 agc virtdir_find_tgt(virtdir_t *tp, const char *tgt, size_t tgtlen)
252 1.1 agc {
253 1.1 agc /* we don't need no stinking binary searches */
254 1.6 agc char path[MAXPATHLEN];
255 1.1 agc int i;
256 1.1 agc
257 1.6 agc (void) normalise(tgt, tgtlen, path, sizeof(path));
258 1.1 agc for (i = 0 ; i < tp->c ; i++) {
259 1.6 agc if (tp->v[i].tgt && strcmp(tp->v[i].tgt, path) == 0) {
260 1.1 agc return &tp->v[i];
261 1.1 agc }
262 1.1 agc }
263 1.1 agc return NULL;
264 1.1 agc }
265 1.1 agc
266 1.1 agc /* kill all of the space allocated to the tree */
267 1.1 agc void
268 1.1 agc virtdir_drop(virtdir_t *tp)
269 1.1 agc {
270 1.1 agc int i;
271 1.1 agc
272 1.1 agc for (i = 0 ; i < tp->c ; i++) {
273 1.1 agc FREE(tp->v[i].name);
274 1.1 agc if (tp->v[i].tgt) {
275 1.1 agc FREE(tp->v[i].tgt);
276 1.1 agc }
277 1.1 agc }
278 1.1 agc FREE(tp->v);
279 1.1 agc }
280 1.1 agc
281 1.6 agc /* return the value of the root directory of the tree */
282 1.6 agc char *
283 1.6 agc virtdir_rootdir(virtdir_t *tp)
284 1.6 agc {
285 1.6 agc return tp->rootdir;
286 1.6 agc }
287 1.6 agc
288 1.1 agc #ifdef VIRTDIR_DEBUG
289 1.1 agc static void
290 1.1 agc ptree(virtdir_t * tp)
291 1.1 agc {
292 1.1 agc int i;
293 1.1 agc
294 1.1 agc for (i = 0 ; i < tp->c ; i++) {
295 1.1 agc printf("%s, tgt %s\n", tp->v[i].name, tp->v[i].tgt);
296 1.1 agc }
297 1.1 agc }
298 1.1 agc #endif
299 1.1 agc
300 1.1 agc #ifdef VIRTDIR_DEBUG
301 1.1 agc int
302 1.1 agc main(int argc, char **argv)
303 1.1 agc {
304 1.1 agc virt_dirent_t *tp;
305 1.1 agc virtdir_t t;
306 1.1 agc struct stat st;
307 1.1 agc
308 1.1 agc (void) memset(&t, 0x0, sizeof(t));
309 1.1 agc stat(".", &st);
310 1.5 agc virtdir_add(&t, ".", 1, 'd', NULL, 0);
311 1.1 agc stat("..", &st);
312 1.5 agc virtdir_add(&t, "..", 2, 'd', NULL, 0);
313 1.1 agc st.st_mode = S_IFREG | 0644;
314 1.5 agc virtdir_add(&t, "file1", 5, 'f', NULL, 0);
315 1.1 agc ptree(&t);
316 1.5 agc virtdir_add(&t, "file2", 5, 'f', NULL, 0);
317 1.5 agc virtdir_add(&t, "file0", 5, 'f', NULL, 0);
318 1.5 agc virtdir_add(&t, "abcde", 5, 'f', NULL, 0);
319 1.5 agc virtdir_add(&t, "bcdef", 5, 'f', NULL, 0);
320 1.5 agc virtdir_add(&t, "a", 1, 'f', NULL, 0);
321 1.1 agc ptree(&t);
322 1.1 agc if ((tp = virtdir_find(&t, "a", 1)) == NULL) {
323 1.1 agc printf("borked2\n");
324 1.1 agc } else {
325 1.1 agc printf("a found\n");
326 1.1 agc }
327 1.1 agc virtdir_drop(&t);
328 1.1 agc exit(EXIT_SUCCESS);
329 1.1 agc }
330 1.1 agc #endif
331