1 /* $NetBSD: icfs.c,v 1.9 2007/11/30 19:02:37 pooka 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 [-s]�[-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; 154 int ch; 155 156 setprogname(argv[0]); 157 158 if (argc < 3) 159 usage(); 160 161 pflags = mntflags = 0; 162 detach = 1; 163 while ((ch = getopt(argc, argv, "o:s")) != -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 's': 172 detach = 0; 173 break; 174 } 175 } 176 pflags |= PUFFS_FLAG_BUILDPATH; 177 pflags |= PUFFS_KFLAG_NOCACHE_NAME; 178 argv += optind; 179 argc -= optind; 180 181 if (pflags & PUFFS_FLAG_OPDUMP) 182 detach = 0; 183 184 if (argc != 2) 185 usage(); 186 187 if (lstat(argv[0], &sb) == -1) 188 err(1, "stat %s", argv[0]); 189 if ((sb.st_mode & S_IFDIR) == 0) 190 errx(1, "%s is not a directory", argv[0]); 191 192 PUFFSOP_INIT(pops); 193 puffs_null_setops(pops); 194 195 PUFFSOP_SET(pops, ic, node, readdir); 196 197 if ((pu = puffs_init(pops, argv[0], "ic", NULL, pflags)) == NULL) 198 err(1, "mount"); 199 200 pn_root = puffs_pn_new(pu, NULL); 201 if (pn_root == NULL) 202 err(1, "puffs_pn_new"); 203 puffs_setroot(pu, pn_root); 204 205 po_root = puffs_getrootpathobj(pu); 206 if (po_root == NULL) 207 err(1, "getrootpathobj"); 208 po_root->po_path = argv[0]; 209 po_root->po_len = strlen(argv[0]); 210 puffs_stat2vattr(&pn_root->pn_va, &sb); 211 212 puffs_set_pathcmp(pu, icpathcmp); 213 puffs_set_pathtransform(pu, icpathxform); 214 215 if (detach) 216 if (puffs_daemon(pu, 1, 1) == -1) 217 err(1, "puffs_daemon"); 218 219 if (puffs_mount(pu, argv[1], mntflags, pn_root) == -1) 220 err(1, "puffs_mount"); 221 if (puffs_mainloop(pu) == -1) 222 err(1, "mainloop"); 223 224 return 0; 225 } 226 227 int 228 ic_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent, 229 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr, 230 int *eofflag, off_t *cookies, size_t *ncookies) 231 { 232 struct dirent *dp; 233 size_t rl; 234 int rv; 235 236 dp = dent; 237 rl = *reslen; 238 239 rv = puffs_null_node_readdir(pu, opc, dent, readoff, reslen, pcr, 240 eofflag, cookies, ncookies); 241 if (rv) 242 return rv; 243 244 while (rl > *reslen) { 245 dotolower(dp->d_name, dp->d_namlen); 246 rl -= _DIRENT_SIZE(dp); 247 dp = _DIRENT_NEXT(dp); 248 } 249 250 return 0; 251 } 252