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 #define FUSE_USE_VERSION 26 32 33 #include <fuse.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 38 #include "virtdir.h" 39 #include "defs.h" 40 41 /* utility comparison routine for sorting and searching */ 42 static int 43 compare(const void *vp1, const void *vp2) 44 { 45 const virt_dirent_t *tp1 = (const virt_dirent_t *) vp1; 46 const virt_dirent_t *tp2 = (const virt_dirent_t *) vp2; 47 48 return strcmp(tp1->name, tp2->name); 49 } 50 51 /* save `n' chars of `s' in allocated storage */ 52 static char * 53 strnsave(const char *s, int n) 54 { 55 char *cp; 56 57 if (n < 0) { 58 n = strlen(s); 59 } 60 NEWARRAY(char, cp, n + 1, "strnsave", return NULL); 61 (void) memcpy(cp, s, n); 62 cp[n] = 0x0; 63 return cp; 64 } 65 66 /* ensure intermediate directories exist */ 67 static void 68 mkdirs(virtdir_t *tp, const char *path, size_t size) 69 { 70 virt_dirent_t *ep; 71 char name[MAXPATHLEN]; 72 char *slash; 73 74 (void) strlcpy(name, path, sizeof(name)); 75 for (slash = name + 1 ; (slash = strchr(slash + 1, '/')) != NULL ; ) { 76 *slash = 0x0; 77 if ((ep = virtdir_find(tp, name, strlen(name))) == NULL) { 78 virtdir_add(tp, name, strlen(name), 'd', NULL, 0); 79 } 80 *slash = '/'; 81 } 82 } 83 84 /* get rid of multiple slashes in input */ 85 static int 86 normalise(const char *name, size_t namelen, char *path, size_t pathsize) 87 { 88 const char *np; 89 char *pp; 90 int done; 91 92 for (pp = path, np = name, done = 0 ; !done && (int)(pp - path) < pathsize - 1 && (int)(np - name) <= namelen ; ) { 93 switch(*np) { 94 case '/': 95 if (pp == path || *(pp - 1) != '/') { 96 *pp++ = *np; 97 } 98 np += 1; 99 break; 100 case 0x0: 101 done = 1; 102 break; 103 default: 104 *pp++ = *np++; 105 break; 106 } 107 } 108 /* XXX - trailing slash? */ 109 *pp = 0x0; 110 return (int)(pp - path); 111 } 112 113 /* initialise the tree */ 114 int 115 virtdir_init(virtdir_t *tp, const char *rootdir, struct stat *d, struct stat *f, struct stat *l) 116 { 117 (void) memcpy(&tp->dir, d, sizeof(tp->dir)); 118 tp->dir.st_mode = S_IFDIR | 0755; 119 tp->dir.st_nlink = 2; 120 (void) memcpy(&tp->file, f, sizeof(tp->file)); 121 tp->file.st_mode = S_IFREG | 0644; 122 tp->file.st_nlink = 1; 123 (void) memcpy(&tp->lnk, l, sizeof(tp->lnk)); 124 tp->lnk.st_mode = S_IFLNK | 0644; 125 tp->lnk.st_nlink = 1; 126 if (rootdir != NULL) { 127 tp->rootdir = strdup(rootdir); 128 } 129 return 1; 130 } 131 132 /* add an entry to the tree */ 133 int 134 virtdir_add(virtdir_t *tp, const char *name, size_t size, uint8_t type, const char *tgt, size_t tgtlen) 135 { 136 struct stat st; 137 char path[MAXPATHLEN]; 138 int pathlen; 139 140 if (tp->v == NULL) { 141 (void) stat(".", &st); 142 virtdir_init(tp, NULL, &st, &st, &st); 143 } 144 pathlen = normalise(name, size, path, sizeof(path)); 145 if (virtdir_find(tp, path, pathlen) != NULL) { 146 /* attempt to add a duplicate directory entry */ 147 return 0; 148 } 149 ALLOC(virt_dirent_t, tp->v, tp->size, tp->c, 10, 10, "virtdir_add", 150 return 0); 151 tp->v[tp->c].namelen = pathlen; 152 if ((tp->v[tp->c].name = strnsave(path, pathlen)) == NULL) { 153 return 0; 154 } 155 tp->v[tp->c].d_name = strrchr(tp->v[tp->c].name, '/') + 1; 156 tp->v[tp->c].type = type; 157 tp->v[tp->c].ino = (ino_t) random() & 0xfffff; 158 if (tgt != NULL) { 159 tp->v[tp->c].tgtlen = tgtlen; 160 tp->v[tp->c].tgt = strnsave(tgt, tgtlen); 161 } 162 tp->c += 1; 163 qsort(tp->v, tp->c, sizeof(tp->v[0]), compare); 164 mkdirs(tp, path, pathlen); 165 return 1; 166 } 167 168 /* delete an entry from the tree */ 169 int 170 virtdir_del(virtdir_t *tp, const char *name, size_t size) 171 { 172 virt_dirent_t *ep; 173 int i; 174 175 if ((ep = virtdir_find(tp, name, size)) == NULL) { 176 return 0; 177 } 178 i = (int)(ep - tp->v); 179 for (tp->c -= 1 ; i < tp->c ; i++) { 180 tp->v[i] = tp->v[i + 1]; 181 } 182 return 1; 183 } 184 185 /* find an entry in the tree */ 186 virt_dirent_t * 187 virtdir_find(virtdir_t *tp, const char *name, size_t namelen) 188 { 189 virt_dirent_t e; 190 char path[MAXPATHLEN]; 191 192 (void) memset(&e, 0x0, sizeof(e)); 193 e.namelen = normalise(name, namelen, path, sizeof(path)); 194 e.name = path; 195 return bsearch(&e, tp->v, tp->c, sizeof(tp->v[0]), compare); 196 } 197 198 /* return the virtual offset in the tree */ 199 int 200 virtdir_offset(virtdir_t *tp, virt_dirent_t *dp) 201 { 202 return (int)(dp - tp->v); 203 } 204 205 /* analogous to opendir(3) - open a directory, save information, and 206 * return a pointer to the dynamically allocated structure */ 207 VIRTDIR * 208 openvirtdir(virtdir_t *tp, const char *d) 209 { 210 VIRTDIR *dirp; 211 212 NEW(VIRTDIR, dirp, "openvirtdir", exit(EXIT_FAILURE)); 213 dirp->dirname = strdup(d); 214 dirp->dirnamelen = strlen(d); 215 dirp->tp = tp; 216 dirp->i = 0; 217 return dirp; 218 } 219 220 /* analogous to readdir(3) - read the next entry in the directory that 221 * was opened, and return a pointer to it */ 222 virt_dirent_t * 223 readvirtdir(VIRTDIR *dirp) 224 { 225 char *from; 226 227 for ( ; dirp->i < dirp->tp->c ; dirp->i++) { 228 from = (strcmp(dirp->dirname, "/") == 0) ? 229 &dirp->tp->v[dirp->i].name[1] : 230 &dirp->tp->v[dirp->i].name[dirp->dirnamelen + 1]; 231 if (strncmp(dirp->tp->v[dirp->i].name, dirp->dirname, 232 dirp->dirnamelen) == 0 && 233 *from != 0x0 && 234 strchr(from, '/') == NULL) { 235 return &dirp->tp->v[dirp->i++]; 236 } 237 } 238 return NULL; 239 } 240 241 /* free the storage associated with the virtual directory structure */ 242 void 243 closevirtdir(VIRTDIR *dirp) 244 { 245 free(dirp->dirname); 246 FREE(dirp); 247 } 248 249 /* find a target in the tree -- not quick! */ 250 virt_dirent_t * 251 virtdir_find_tgt(virtdir_t *tp, const char *tgt, size_t tgtlen) 252 { 253 /* we don't need no stinking binary searches */ 254 char path[MAXPATHLEN]; 255 int i; 256 257 (void) normalise(tgt, tgtlen, path, sizeof(path)); 258 for (i = 0 ; i < tp->c ; i++) { 259 if (tp->v[i].tgt && strcmp(tp->v[i].tgt, path) == 0) { 260 return &tp->v[i]; 261 } 262 } 263 return NULL; 264 } 265 266 /* kill all of the space allocated to the tree */ 267 void 268 virtdir_drop(virtdir_t *tp) 269 { 270 int i; 271 272 for (i = 0 ; i < tp->c ; i++) { 273 FREE(tp->v[i].name); 274 if (tp->v[i].tgt) { 275 FREE(tp->v[i].tgt); 276 } 277 } 278 FREE(tp->v); 279 } 280 281 /* return the value of the root directory of the tree */ 282 char * 283 virtdir_rootdir(virtdir_t *tp) 284 { 285 return tp->rootdir; 286 } 287 288 #ifdef VIRTDIR_DEBUG 289 static void 290 ptree(virtdir_t * tp) 291 { 292 int i; 293 294 for (i = 0 ; i < tp->c ; i++) { 295 printf("%s, tgt %s\n", tp->v[i].name, tp->v[i].tgt); 296 } 297 } 298 #endif 299 300 #ifdef VIRTDIR_DEBUG 301 int 302 main(int argc, char **argv) 303 { 304 virt_dirent_t *tp; 305 virtdir_t t; 306 struct stat st; 307 308 (void) memset(&t, 0x0, sizeof(t)); 309 stat(".", &st); 310 virtdir_add(&t, ".", 1, 'd', NULL, 0); 311 stat("..", &st); 312 virtdir_add(&t, "..", 2, 'd', NULL, 0); 313 st.st_mode = S_IFREG | 0644; 314 virtdir_add(&t, "file1", 5, 'f', NULL, 0); 315 ptree(&t); 316 virtdir_add(&t, "file2", 5, 'f', NULL, 0); 317 virtdir_add(&t, "file0", 5, 'f', NULL, 0); 318 virtdir_add(&t, "abcde", 5, 'f', NULL, 0); 319 virtdir_add(&t, "bcdef", 5, 'f', NULL, 0); 320 virtdir_add(&t, "a", 1, 'f', NULL, 0); 321 ptree(&t); 322 if ((tp = virtdir_find(&t, "a", 1)) == NULL) { 323 printf("borked2\n"); 324 } else { 325 printf("a found\n"); 326 } 327 virtdir_drop(&t); 328 exit(EXIT_SUCCESS); 329 } 330 #endif 331