1 /* $NetBSD: icfs.c,v 1.11 2008/09/12 14:40:46 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * icfs: layer everything in lowercase 30 * (unfinished, as is probably fairly easy to tell) 31 * 32 * Now, this "layered" file system demostrates a nice gotcha with 33 * mangling readdir entries. In case we can't unmangle the name 34 * (cf. rot13fs), we need to map the mangled name back to the real 35 * name in lookup and store the actual lower layer name instead of 36 * the mangled name. 37 * 38 * This is mounted without namecache. Otherwise we might have 39 * two different nodes for e.g. FoO and foo. It might be possible 40 * support name cache by having all namespace-altering operations 41 * flush the directory namecache ... 42 */ 43 44 #include <sys/types.h> 45 46 #include <assert.h> 47 #include <ctype.h> 48 #include <dirent.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <puffs.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <unistd.h> 55 56 PUFFSOP_PROTOS(ic) 57 58 static void usage(void); 59 60 static void 61 usage() 62 { 63 64 errx(1, "usage: %s [-sp] [-o mntopts] icfs mountpath", 65 getprogname()); 66 } 67 68 static void 69 dotolower(char *buf, size_t buflen) 70 { 71 72 while (buflen--) { 73 *buf = tolower((unsigned char)*buf); 74 buf++; 75 } 76 } 77 78 static int 79 icpathcmp(struct puffs_usermount *pu, struct puffs_pathobj *c1, 80 struct puffs_pathobj *c2, size_t clen, int checkprefix) 81 { 82 struct puffs_pathobj *po_root; 83 char *cp1, *cp2; 84 size_t rplen; 85 86 po_root = puffs_getrootpathobj(pu); 87 rplen = po_root->po_len; 88 89 assert(clen >= rplen); 90 91 cp1 = c1->po_path; 92 cp2 = c2->po_path; 93 94 if (strncasecmp(cp1 + rplen, cp2 + rplen, clen - rplen) != 0) 95 return 1; 96 97 if (checkprefix == 0) 98 return 0; 99 100 if (c1->po_len < c2->po_len) 101 return 1; 102 103 if (*(cp1 + clen) != '/') 104 return 1; 105 106 return 0; 107 } 108 109 static int 110 icpathxform(struct puffs_usermount *pu, const struct puffs_pathobj *po_base, 111 const struct puffs_cn *pcn, struct puffs_pathobj *po_new) 112 { 113 struct dirent entry, *result; 114 char *src; 115 size_t srclen; 116 DIR *dp; 117 118 dp = opendir(po_base->po_path); 119 if (dp == NULL) 120 return errno; 121 122 src = pcn->pcn_name; 123 srclen = pcn->pcn_namelen; 124 for (;;) { 125 if (readdir_r(dp, &entry, &result) != 0) 126 break; 127 if (!result) 128 break; 129 if (strcasecmp(result->d_name, pcn->pcn_name) == 0) { 130 src = result->d_name; 131 srclen = strlen(result->d_name); 132 break; 133 } 134 } 135 136 po_new->po_path = strdup(src); 137 po_new->po_len = srclen; 138 closedir(dp); 139 140 return 0; 141 } 142 143 int 144 main(int argc, char *argv[]) 145 { 146 struct puffs_usermount *pu; 147 struct puffs_ops *pops; 148 struct puffs_pathobj *po_root; 149 struct puffs_node *pn_root; 150 struct stat sb; 151 mntoptparse_t mp; 152 int mntflags, pflags; 153 int detach, dpres; 154 int ch; 155 156 setprogname(argv[0]); 157 158 if (argc < 3) 159 usage(); 160 161 pflags = mntflags = dpres = 0; 162 detach = 1; 163 while ((ch = getopt(argc, argv, "o:sp")) != -1) { 164 switch (ch) { 165 case 'o': 166 mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags); 167 if (mp == NULL) 168 err(1, "getmntopts"); 169 freemntopts(mp); 170 break; 171 case 'p': 172 dpres = 1; 173 break; 174 case 's': 175 detach = 0; 176 break; 177 } 178 } 179 pflags |= PUFFS_FLAG_BUILDPATH; 180 pflags |= PUFFS_KFLAG_NOCACHE_NAME; 181 argv += optind; 182 argc -= optind; 183 184 if (pflags & PUFFS_FLAG_OPDUMP) 185 detach = 0; 186 187 if (argc != 2) 188 usage(); 189 190 if (lstat(argv[0], &sb) == -1) 191 err(1, "stat %s", argv[0]); 192 if ((sb.st_mode & S_IFDIR) == 0) 193 errx(1, "%s is not a directory", argv[0]); 194 195 PUFFSOP_INIT(pops); 196 puffs_null_setops(pops); 197 198 if (dpres == 0) 199 PUFFSOP_SET(pops, ic, node, readdir); 200 201 if ((pu = puffs_init(pops, argv[0], "ic", NULL, pflags)) == NULL) 202 err(1, "mount"); 203 204 pn_root = puffs_pn_new(pu, NULL); 205 if (pn_root == NULL) 206 err(1, "puffs_pn_new"); 207 puffs_setroot(pu, pn_root); 208 209 po_root = puffs_getrootpathobj(pu); 210 if (po_root == NULL) 211 err(1, "getrootpathobj"); 212 po_root->po_path = argv[0]; 213 po_root->po_len = strlen(argv[0]); 214 puffs_stat2vattr(&pn_root->pn_va, &sb); 215 216 puffs_set_pathcmp(pu, icpathcmp); 217 puffs_set_pathtransform(pu, icpathxform); 218 219 if (detach) 220 if (puffs_daemon(pu, 1, 1) == -1) 221 err(1, "puffs_daemon"); 222 223 if (puffs_mount(pu, argv[1], mntflags, pn_root) == -1) 224 err(1, "puffs_mount"); 225 if (puffs_mainloop(pu) == -1) 226 err(1, "mainloop"); 227 228 return 0; 229 } 230 231 int 232 ic_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent, 233 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr, 234 int *eofflag, off_t *cookies, size_t *ncookies) 235 { 236 struct dirent *dp; 237 size_t rl; 238 int rv; 239 240 dp = dent; 241 rl = *reslen; 242 243 rv = puffs_null_node_readdir(pu, opc, dent, readoff, reslen, pcr, 244 eofflag, cookies, ncookies); 245 if (rv) 246 return rv; 247 248 while (rl > *reslen) { 249 dotolower(dp->d_name, dp->d_namlen); 250 rl -= _DIRENT_SIZE(dp); 251 dp = _DIRENT_NEXT(dp); 252 } 253 254 return 0; 255 } 256