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