icfs.c revision 1.3 1 /* $NetBSD: icfs.c,v 1.3 2007/06/24 18:57:26 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 nocache now (to disable the namecache since a
39 * separate switch doesn't exist). Otherwise we might have
40 * two different nodes for e.g. FoO and foo.
41 */
42
43 #include <sys/types.h>
44
45 #include <assert.h>
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <puffs.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54
55 PUFFSOP_PROTOS(ic)
56
57 static void usage(void);
58
59 static void
60 usage()
61 {
62
63 errx(1, "usage: %s [-s][-o mntopts]icfs mountpath",
64 getprogname());
65 }
66
67 static void
68 dotolower(char *buf, size_t buflen)
69 {
70
71 while (buflen--) {
72 *buf = tolower((unsigned char)*buf);
73 buf++;
74 }
75 }
76
77 static int
78 icpathcmp(struct puffs_usermount *pu, struct puffs_pathobj *c1,
79 struct puffs_pathobj *c2, size_t clen, int checkprefix)
80 {
81 struct puffs_pathobj *po_root;
82 char *cp1, *cp2;
83 size_t rplen;
84
85 po_root = puffs_getrootpathobj(pu);
86 rplen = po_root->po_len;
87
88 assert(clen >= rplen);
89
90 cp1 = c1->po_path;
91 cp2 = c2->po_path;
92
93 if (strncasecmp(cp1 + rplen, cp2 + rplen, clen - rplen) != 0)
94 return 1;
95
96 if (checkprefix == 0)
97 return 0;
98
99 if (c1->po_len < c2->po_len)
100 return 1;
101
102 if (*(cp1 + clen) != '/')
103 return 1;
104
105 return 0;
106 }
107
108 static int
109 icpathxform(struct puffs_usermount *pu, const struct puffs_pathobj *po_base,
110 const struct puffs_cn *pcn, struct puffs_pathobj *po_new)
111 {
112 struct dirent entry, *result;
113 char *src;
114 size_t srclen;
115 DIR *dp;
116
117 dp = opendir(po_base->po_path);
118 if (dp == NULL)
119 return errno;
120
121 src = pcn->pcn_name;
122 srclen = pcn->pcn_namelen;
123 for (;;) {
124 if (readdir_r(dp, &entry, &result) != 0)
125 break;
126 if (!result)
127 break;
128 if (strcasecmp(result->d_name, pcn->pcn_name) == 0) {
129 src = result->d_name;
130 srclen = strlen(result->d_name);
131 break;
132 }
133 }
134
135 po_new->po_path = strdup(src);
136 po_new->po_len = srclen;
137 closedir(dp);
138
139 return 0;
140 }
141
142 int
143 main(int argc, char *argv[])
144 {
145 struct puffs_usermount *pu;
146 struct puffs_ops *pops;
147 struct puffs_pathobj *po_root;
148 struct puffs_node *pn_root;
149 struct stat sb;
150 mntoptparse_t mp;
151 int mntflags, pflags, lflags;
152 int ch;
153
154 setprogname(argv[0]);
155
156 if (argc < 3)
157 usage();
158
159 pflags = lflags = mntflags = 0;
160 while ((ch = getopt(argc, argv, "o:s")) != -1) {
161 switch (ch) {
162 case 'o':
163 mp = getmntopts(optarg, puffsmopts, &mntflags, &pflags);
164 if (mp == NULL)
165 err(1, "getmntopts");
166 freemntopts(mp);
167 break;
168 case 's':
169 lflags |= PUFFSLOOP_NODAEMON;
170 break;
171 }
172 }
173 pflags |= PUFFS_FLAG_BUILDPATH;
174 pflags |= PUFFS_KFLAG_NOCACHE;
175 argv += optind;
176 argc -= optind;
177
178 if (pflags & PUFFS_FLAG_OPDUMP)
179 lflags = PUFFSLOOP_NODAEMON;
180
181 if (argc != 2)
182 usage();
183
184 if (lstat(argv[0], &sb) == -1)
185 err(1, "stat %s", argv[0]);
186 if ((sb.st_mode & S_IFDIR) == 0)
187 errx(1, "%s is not a directory", argv[0]);
188
189 PUFFSOP_INIT(pops);
190 puffs_null_setops(pops);
191
192 PUFFSOP_SET(pops, ic, node, readdir);
193
194 if ((pu = puffs_init(pops, "ic", NULL, pflags)) == NULL)
195 err(1, "mount");
196
197 pn_root = puffs_pn_new(pu, NULL);
198 if (pn_root == NULL)
199 err(1, "puffs_pn_new");
200 puffs_setroot(pu, pn_root);
201
202 po_root = puffs_getrootpathobj(pu);
203 if (po_root == NULL)
204 err(1, "getrootpathobj");
205 po_root->po_path = argv[0];
206 po_root->po_len = strlen(argv[0]);
207 puffs_stat2vattr(&pn_root->pn_va, &sb);
208
209 puffs_set_pathcmp(pu, icpathcmp);
210 puffs_set_pathtransform(pu, icpathxform);
211
212 if (puffs_mount(pu, argv[1], mntflags, pn_root) == -1)
213 err(1, "puffs_mount");
214
215 return puffs_mainloop(pu, lflags);
216 }
217
218 int
219 ic_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *dent,
220 off_t *readoff, size_t *reslen, const struct puffs_cred *pcr,
221 int *eofflag, off_t *cookies, size_t *ncookies)
222 {
223 struct dirent *dp;
224 size_t rl;
225 int rv;
226
227 dp = dent;
228 rl = *reslen;
229
230 rv = puffs_null_node_readdir(pcc, opc, dent, readoff, reslen, pcr,
231 eofflag, cookies, ncookies);
232 if (rv)
233 return rv;
234
235 while (rl > *reslen) {
236 dotolower(dp->d_name, dp->d_namlen);
237 rl -= _DIRENT_SIZE(dp);
238 dp = _DIRENT_NEXT(dp);
239 }
240
241 return 0;
242 }
243