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