1 1.3 christos /* $NetBSD: virtdir.c,v 1.3 2017/11/26 03:51:45 christos Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /* 4 1.1 jmcneill * Copyright 2007 Alistair Crooks. All rights reserved. 5 1.1 jmcneill * 6 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 7 1.1 jmcneill * modification, are permitted provided that the following conditions 8 1.1 jmcneill * are met: 9 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 10 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 11 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 13 1.1 jmcneill * documentation and/or other materials provided with the distribution. 14 1.1 jmcneill * 3. The name of the author may not be used to endorse or promote 15 1.1 jmcneill * products derived from this software without specific prior written 16 1.1 jmcneill * permission. 17 1.1 jmcneill * 18 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 19 1.1 jmcneill * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 1.1 jmcneill * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 1.1 jmcneill * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 22 1.1 jmcneill * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 1.1 jmcneill * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 24 1.1 jmcneill * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 1.1 jmcneill * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 1.1 jmcneill * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 1.1 jmcneill * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 1.1 jmcneill */ 30 1.1 jmcneill 31 1.1 jmcneill #include <sys/types.h> 32 1.2 christos #include <sys/param.h> 33 1.1 jmcneill #include <sys/stat.h> 34 1.1 jmcneill 35 1.1 jmcneill #include <stdio.h> 36 1.2 christos #include <string.h> 37 1.1 jmcneill #include <stdlib.h> 38 1.1 jmcneill #include <unistd.h> 39 1.2 christos #include <util.h> 40 1.1 jmcneill 41 1.1 jmcneill #include "virtdir.h" 42 1.1 jmcneill 43 1.1 jmcneill /* utility comparison routine for sorting and searching */ 44 1.1 jmcneill static int 45 1.1 jmcneill compare(const void *vp1, const void *vp2) 46 1.1 jmcneill { 47 1.3 christos const virt_dirent_t *tp1 = vp1; 48 1.3 christos const virt_dirent_t *tp2 = vp2; 49 1.1 jmcneill 50 1.1 jmcneill return strcmp(tp1->name, tp2->name); 51 1.1 jmcneill } 52 1.1 jmcneill 53 1.1 jmcneill /* ensure intermediate directories exist */ 54 1.1 jmcneill static void 55 1.1 jmcneill mkdirs(virtdir_t *tp, const char *path, size_t size) 56 1.1 jmcneill { 57 1.1 jmcneill virt_dirent_t *ep; 58 1.1 jmcneill char name[MAXPATHLEN]; 59 1.1 jmcneill char *slash; 60 1.1 jmcneill 61 1.1 jmcneill (void) strlcpy(name, path, sizeof(name)); 62 1.1 jmcneill for (slash = name + 1 ; (slash = strchr(slash + 1, '/')) != NULL ; ) { 63 1.3 christos *slash = '\0'; 64 1.1 jmcneill if ((ep = virtdir_find(tp, name, strlen(name))) == NULL) { 65 1.1 jmcneill virtdir_add(tp, name, strlen(name), 'd', NULL, 0, 0); 66 1.1 jmcneill } 67 1.1 jmcneill *slash = '/'; 68 1.1 jmcneill } 69 1.1 jmcneill } 70 1.1 jmcneill 71 1.1 jmcneill /* get rid of multiple slashes in input */ 72 1.2 christos static size_t 73 1.1 jmcneill normalise(const char *name, size_t namelen, char *path, size_t pathsize) 74 1.1 jmcneill { 75 1.1 jmcneill const char *np; 76 1.1 jmcneill char *pp; 77 1.1 jmcneill int done; 78 1.1 jmcneill 79 1.2 christos for (pp = path, np = name, done = 0 ; !done && 80 1.2 christos (size_t)(pp - path) < pathsize - 1 && 81 1.2 christos (size_t)(np - name) <= namelen; ) { 82 1.2 christos switch (*np) { 83 1.1 jmcneill case '/': 84 1.1 jmcneill if (pp == path || *(pp - 1) != '/') { 85 1.1 jmcneill *pp++ = *np; 86 1.1 jmcneill } 87 1.1 jmcneill np += 1; 88 1.1 jmcneill break; 89 1.2 christos case '\0': 90 1.1 jmcneill done = 1; 91 1.1 jmcneill break; 92 1.1 jmcneill default: 93 1.1 jmcneill *pp++ = *np++; 94 1.1 jmcneill break; 95 1.1 jmcneill } 96 1.1 jmcneill } 97 1.1 jmcneill /* XXX - trailing slash? */ 98 1.2 christos *pp = '\0'; 99 1.2 christos return (size_t)(pp - path); 100 1.1 jmcneill } 101 1.1 jmcneill 102 1.1 jmcneill /* initialise the tree */ 103 1.1 jmcneill int 104 1.3 christos virtdir_init(virtdir_t *tp, const char *rootdir, const struct stat *d, 105 1.3 christos const struct stat *f, const struct stat *l) 106 1.1 jmcneill { 107 1.2 christos tp->dir = *d; 108 1.1 jmcneill tp->dir.st_mode = S_IFDIR | 0755; 109 1.1 jmcneill tp->dir.st_nlink = 2; 110 1.2 christos tp->file = *f; 111 1.1 jmcneill tp->file.st_mode = S_IFREG | 0644; 112 1.1 jmcneill tp->file.st_nlink = 1; 113 1.2 christos tp->lnk = *l; 114 1.1 jmcneill tp->lnk.st_mode = S_IFLNK | 0644; 115 1.1 jmcneill tp->lnk.st_nlink = 1; 116 1.1 jmcneill if (rootdir != NULL) { 117 1.2 christos tp->rootdir = estrdup(rootdir); 118 1.1 jmcneill } 119 1.1 jmcneill return 1; 120 1.1 jmcneill } 121 1.1 jmcneill 122 1.1 jmcneill /* add an entry to the tree */ 123 1.1 jmcneill int 124 1.3 christos virtdir_add(virtdir_t *tp, const char *name, size_t size, uint8_t type, 125 1.3 christos const char *tgt, size_t tgtlen, uint16_t select) 126 1.1 jmcneill { 127 1.1 jmcneill char path[MAXPATHLEN]; 128 1.2 christos size_t pathlen; 129 1.1 jmcneill 130 1.1 jmcneill pathlen = normalise(name, size, path, sizeof(path)); 131 1.1 jmcneill if (virtdir_find(tp, path, pathlen) != NULL) { 132 1.1 jmcneill /* attempt to add a duplicate directory entry */ 133 1.1 jmcneill return 0; 134 1.1 jmcneill } 135 1.2 christos if (tp->c == tp->size || tp->size == 0) { 136 1.3 christos tp->size += 10; 137 1.2 christos tp->v = erealloc(tp->v, tp->size * sizeof(*tp->v)); 138 1.2 christos } 139 1.1 jmcneill tp->v[tp->c].namelen = pathlen; 140 1.3 christos tp->v[tp->c].name = estrndup(path, pathlen); 141 1.1 jmcneill tp->v[tp->c].d_name = strrchr(tp->v[tp->c].name, '/') + 1; 142 1.1 jmcneill tp->v[tp->c].type = type; 143 1.1 jmcneill tp->v[tp->c].ino = (ino_t) random() & 0xfffff; 144 1.1 jmcneill tp->v[tp->c].tgtlen = tgtlen; 145 1.1 jmcneill if (tgt != NULL) { 146 1.2 christos tp->v[tp->c].tgt = estrndup(tgt, tgtlen); 147 1.1 jmcneill } 148 1.1 jmcneill tp->v[tp->c].select = select; 149 1.1 jmcneill tp->c += 1; 150 1.1 jmcneill qsort(tp->v, tp->c, sizeof(tp->v[0]), compare); 151 1.1 jmcneill mkdirs(tp, path, pathlen); 152 1.1 jmcneill return 1; 153 1.1 jmcneill } 154 1.1 jmcneill 155 1.1 jmcneill /* find an entry in the tree */ 156 1.1 jmcneill virt_dirent_t * 157 1.1 jmcneill virtdir_find(virtdir_t *tp, const char *name, size_t namelen) 158 1.1 jmcneill { 159 1.1 jmcneill virt_dirent_t e; 160 1.1 jmcneill char path[MAXPATHLEN]; 161 1.1 jmcneill 162 1.3 christos (void) memset(&e, 0, sizeof(e)); 163 1.1 jmcneill e.namelen = normalise(name, namelen, path, sizeof(path)); 164 1.1 jmcneill e.name = path; 165 1.1 jmcneill return bsearch(&e, tp->v, tp->c, sizeof(tp->v[0]), compare); 166 1.1 jmcneill } 167 1.1 jmcneill 168 1.1 jmcneill /* return the virtual offset in the tree */ 169 1.2 christos off_t 170 1.3 christos virtdir_offset(const virtdir_t *tp, const virt_dirent_t *dp) 171 1.1 jmcneill { 172 1.2 christos return dp - tp->v; 173 1.1 jmcneill } 174 1.1 jmcneill 175 1.1 jmcneill /* analogous to opendir(3) - open a directory, save information, and 176 1.1 jmcneill * return a pointer to the dynamically allocated structure */ 177 1.1 jmcneill VIRTDIR * 178 1.1 jmcneill openvirtdir(virtdir_t *tp, const char *d) 179 1.1 jmcneill { 180 1.1 jmcneill VIRTDIR *dirp; 181 1.1 jmcneill 182 1.2 christos dirp = emalloc(sizeof(*dirp)); 183 1.2 christos dirp->dirname = estrdup(d); 184 1.1 jmcneill dirp->dirnamelen = strlen(d); 185 1.1 jmcneill dirp->tp = tp; 186 1.1 jmcneill dirp->i = 0; 187 1.1 jmcneill return dirp; 188 1.1 jmcneill } 189 1.1 jmcneill 190 1.1 jmcneill /* analogous to readdir(3) - read the next entry in the directory that 191 1.1 jmcneill * was opened, and return a pointer to it */ 192 1.1 jmcneill virt_dirent_t * 193 1.1 jmcneill readvirtdir(VIRTDIR *dirp) 194 1.1 jmcneill { 195 1.1 jmcneill char *from; 196 1.1 jmcneill 197 1.3 christos for ( ; dirp->i < dirp->tp->c; dirp->i++) { 198 1.1 jmcneill from = (strcmp(dirp->dirname, "/") == 0) ? 199 1.2 christos &dirp->tp->v[dirp->i].name[1] : 200 1.2 christos &dirp->tp->v[dirp->i].name[dirp->dirnamelen + 1]; 201 1.1 jmcneill if (strncmp(dirp->tp->v[dirp->i].name, dirp->dirname, 202 1.2 christos dirp->dirnamelen) == 0 && 203 1.2 christos *from != '\0' && strchr(from, '/') == NULL) { 204 1.1 jmcneill return &dirp->tp->v[dirp->i++]; 205 1.1 jmcneill } 206 1.1 jmcneill } 207 1.1 jmcneill return NULL; 208 1.1 jmcneill } 209 1.1 jmcneill 210 1.1 jmcneill /* free the storage associated with the virtual directory structure */ 211 1.1 jmcneill void 212 1.1 jmcneill closevirtdir(VIRTDIR *dirp) 213 1.1 jmcneill { 214 1.1 jmcneill free(dirp->dirname); 215 1.2 christos free(dirp); 216 1.1 jmcneill } 217