null.c revision 1.1 1 /* $NetBSD: null.c,v 1.1 2007/01/11 01:01:55 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 * 3. The name of the company nor the name of the author may be used to
15 * endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #if !defined(lint)
33 __RCSID("$NetBSD: null.c,v 1.1 2007/01/11 01:01:55 pooka Exp $");
34 #endif /* !lint */
35
36 /*
37 * A "nullfs" using puffs, i.e. maps one location in the hierarchy
38 * to another using standard system calls.
39 */
40
41 #include <sys/types.h>
42 #include <sys/time.h>
43
44 #include <assert.h>
45 #include <dirent.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <puffs.h>
49 #include <stdio.h>
50 #include <unistd.h>
51
52 PUFFSOP_PROTOS(puffs_null)
53
54 static void *
55 pathcmp(struct puffs_node *pn, void *arg)
56 {
57
58 if (strcmp(arg, pn->pn_path) == 0)
59 return pn;
60 return NULL;
61 }
62
63 /*
64 * set attributes to what is specified. XXX: no rollback in case of failure
65 */
66 static int
67 processvattr(const char *path, const struct vattr *va)
68 {
69 struct timeval tv[2];
70
71 /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */
72 if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1)
73 if (chown(path, va->va_uid, va->va_gid) == -1)
74 return errno;
75
76 if (va->va_mode != (unsigned)PUFFS_VNOVAL)
77 if (chmod(path, va->va_mode) == -1)
78 return errno;
79
80 /* sloppy */
81 if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL
82 || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) {
83 TIMESPEC_TO_TIMEVAL(&tv[0], &va->va_atime);
84 TIMESPEC_TO_TIMEVAL(&tv[1], &va->va_mtime);
85
86 if (lutimes(path, tv) == -1)
87 return errno;
88 }
89
90 if (va->va_type == VREG && va->va_size != (unsigned)PUFFS_VNOVAL)
91 if (truncate(path, (off_t)va->va_size) == -1)
92 return errno;
93
94 return 0;
95 }
96
97 /*ARGSUSED*/
98 int
99 puffs_null_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
100 {
101 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
102
103 if (statvfs(pu->pu_pn_root->pn_path, svfsb) == -1)
104 return errno;
105
106 return 0;
107 }
108
109 /*
110 * Wow, this kinda looks like a real lookup since it bounces all
111 * over the place
112 */
113 int
114 puffs_null_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
115 enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
116 const struct puffs_cn *pcn)
117 {
118 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
119 struct puffs_node *pn = opc, *pn_res;
120 struct dirent entry, *result;
121 char dpath[MAXPATHLEN+1];
122 char *npath;
123 struct stat sb;
124 DIR *dp;
125 int rv;
126
127 assert(pn->pn_va.va_type == VDIR);
128
129 if (pcn->pcn_flags & PUFFS_ISDOTDOT) {
130 char *p;
131 strcpy(dpath, pn->pn_path);
132 p = strrchr(dpath, '/');
133 assert(p != NULL);
134 *p = '\0';
135
136 pn_res = puffs_pn_nodewalk(pu, pathcmp, dpath);
137 if (pn_res)
138 goto success;
139 npath = dpath;
140 goto getnew;
141 }
142
143 dp = opendir(pn->pn_path);
144 if (dp == NULL)
145 return errno;
146
147 /* more readable than for(), IMHO */
148 rv = readdir_r(dp, &entry, &result);
149 while (rv == 0 && result) {
150 if (strcmp(result->d_name, pcn->pcn_name) == 0)
151 break;
152 rv = readdir_r(dp, &entry, &result);
153 }
154 closedir(dp);
155
156 if (rv)
157 return rv;
158 if (!result)
159 return ENOENT;
160
161 pn_res = puffs_pn_nodewalk(pu, pathcmp, pcn->pcn_fullpath);
162 if (pn_res)
163 goto success;
164 npath = pcn->pcn_fullpath;
165
166 getnew:
167 rv = lstat(npath, &sb);
168 if (rv)
169 return rv;
170 pn_res = puffs_pn_new(pu, NULL);
171 if (pn_res == NULL)
172 return ENOMEM;
173 puffs_stat2vattr(&pn_res->pn_va, &sb);
174
175 success:
176 *newnode = pn_res;
177 *newtype = pn_res->pn_va.va_type;
178 *newsize = pn_res->pn_va.va_size;
179 *newrdev = pn_res->pn_va.va_rdev;
180
181 return 0;
182 }
183
184 /*ARGSUSED*/
185 int
186 puffs_null_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
187 const struct puffs_cn *pcn, const struct vattr *va)
188 {
189 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
190 struct puffs_node *pn;
191 int fd, rv;
192
193 fd = open(pcn->pcn_fullpath, O_RDWR | O_CREAT | O_TRUNC);
194 if (fd == -1)
195 return errno;
196 close(fd);
197 if ((rv = processvattr(pcn->pcn_fullpath, va)) != 0) {
198 unlink(pcn->pcn_fullpath);
199 return rv;
200 }
201
202 pn = puffs_pn_new(pu, NULL);
203 if (!pn) {
204 unlink(pcn->pcn_fullpath);
205 return ENOMEM;
206 }
207 puffs_setvattr(&pn->pn_va, va);
208
209 *newnode = pn;
210 return 0;
211 }
212
213 /*ARGSUSED*/
214 int
215 puffs_null_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
216 const struct puffs_cn *pcn, const struct vattr *va)
217 {
218 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
219 struct puffs_node *pn;
220 int rv;
221
222 if (mknod(pcn->pcn_fullpath, va->va_mode, va->va_rdev) == -1)
223 return errno;
224
225 if ((rv = processvattr(pcn->pcn_fullpath, va)) != 0) {
226 unlink(pcn->pcn_fullpath);
227 return rv;
228 }
229
230 pn = puffs_pn_new(pu, NULL);
231 if (!pn) {
232 unlink(pcn->pcn_fullpath);
233 return ENOMEM;
234 }
235 puffs_setvattr(&pn->pn_va, va);
236
237 *newnode = pn;
238 return 0;
239 }
240
241 /*ARGSUSED*/
242 int
243 puffs_null_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
244 const struct puffs_cred *pcred, pid_t pid)
245 {
246 struct puffs_node *pn = opc;
247 struct stat sb;
248
249 if (lstat(pn->pn_path, &sb) == -1)
250 return errno;
251 puffs_stat2vattr(va, &sb);
252
253 return 0;
254 }
255
256 /*ARGSUSED*/
257 int
258 puffs_null_node_setattr(struct puffs_cc *pcc, void *opc,
259 const struct vattr *va, const struct puffs_cred *pcred, pid_t pid)
260 {
261 struct puffs_node *pn = opc;
262 int rv;
263
264 rv = processvattr(pn->pn_path, va);
265 if (rv)
266 return rv;
267
268 puffs_setvattr(&pn->pn_va, va);
269
270 return 0;
271 }
272
273 /*ARGSUSED*/
274 int
275 puffs_null_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
276 const struct puffs_cn *pcn)
277 {
278 struct puffs_node *pn_targ = targ;
279
280 if (unlink(pn_targ->pn_path) == -1)
281 return errno;
282
283 return 0;
284 }
285
286 /*ARGSUSED*/
287 int
288 puffs_null_node_link(struct puffs_cc *pcc, void *opc, void *targ,
289 const struct puffs_cn *pcn)
290 {
291 struct puffs_node *pn_targ = targ;
292
293 if (link(pn_targ->pn_path, pcn->pcn_fullpath) == -1)
294 return errno;
295
296 return 0;
297 }
298
299 /*ARGSUSED*/
300 int
301 puffs_null_node_rename(struct puffs_cc *pcc, void *opc, void *src,
302 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
303 const struct puffs_cn *pcn_targ)
304 {
305
306 if (rename(pcn_src->pcn_fullpath, pcn_targ->pcn_fullpath) == -1)
307 return errno;
308
309 return 0;
310 }
311
312 /*ARGSUSED*/
313 int
314 puffs_null_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
315 const struct puffs_cn *pcn, const struct vattr *va)
316 {
317 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
318 struct puffs_node *pn;
319 int rv;
320
321 if (mkdir(pcn->pcn_fullpath, va->va_mode) == -1)
322 return errno;
323
324 if ((rv = processvattr(pcn->pcn_fullpath, va)) != 0) {
325 unlink(pcn->pcn_fullpath);
326 return rv;
327 }
328
329 pn = puffs_pn_new(pu, NULL);
330 if (pn == NULL) {
331 rmdir(pcn->pcn_fullpath);
332 return ENOMEM;
333 }
334 puffs_setvattr(&pn->pn_va, va);
335
336 *newnode = pn;
337 return 0;
338 }
339
340 /*ARGSUSED*/
341 int
342 puffs_null_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
343 const struct puffs_cn *pcn)
344 {
345 struct puffs_node *pn_targ = targ;
346
347 if (rmdir(pn_targ->pn_path) == -1)
348 return errno;
349
350 return 0;
351 }
352
353 /*ARGSUSED*/
354 int
355 puffs_null_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
356 const struct puffs_cn *pcn, const struct vattr *va,
357 const char *linkname)
358 {
359 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
360 struct puffs_node *pn;
361 int rv;
362
363 if (symlink(linkname, pcn->pcn_fullpath) == -1)
364 return errno;
365
366 if ((rv = processvattr(pcn->pcn_fullpath, va)) != 0) {
367 unlink(pcn->pcn_fullpath);
368 return rv;
369 }
370
371 pn = puffs_pn_new(pu, NULL);
372 if (pn == NULL) {
373 rmdir(pcn->pcn_fullpath);
374 return ENOMEM;
375 }
376 puffs_setvattr(&pn->pn_va, va);
377
378 *newnode = pn;
379 return 0;
380 }
381
382 /*ARGSUSED*/
383 int
384 puffs_null_node_readlink(struct puffs_cc *pcc, void *opc,
385 const struct puffs_cred *pcred, char *linkname, size_t *linklen)
386 {
387 struct puffs_node *pn = opc;
388 ssize_t rv;
389
390 rv = readlink(pn->pn_path, linkname, *linklen);
391 if (rv == -1)
392 return errno;
393
394 *linklen -= rv;
395 return 0;
396 }
397
398 /*ARGSUSED*/
399 int
400 puffs_null_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *de,
401 const struct puffs_cred *pcred, off_t *off, size_t *reslen)
402 {
403 struct puffs_node *pn = opc;
404 struct dirent entry, *result;
405 DIR *dp;
406 off_t i;
407 int rv;
408
409 dp = opendir(pn->pn_path);
410 if (dp == NULL)
411 return errno;
412
413 rv = 0;
414 i = *off;
415
416 /*
417 * XXX: need to do trickery here, telldir/seekdir would be nice, but
418 * then we'd need to keep state, which I'm too lazy to keep
419 */
420 while (i--) {
421 rv = readdir_r(dp, &entry, &result);
422 if (rv || !result)
423 goto out;
424 }
425
426 for (;;) {
427 rv = readdir_r(dp, &entry, &result);
428 if (rv != 0)
429 goto out;
430
431 if (!result)
432 goto out;
433
434 if (_DIRENT_SIZE(result) > *reslen)
435 goto out;
436
437 *de = *result;
438 *reslen -= _DIRENT_SIZE(result);
439 de = _DIRENT_NEXT(de);
440
441 (*off)++;
442 }
443
444 out:
445 closedir(dp);
446 return 0;
447 }
448
449 /*ARGSUSED*/
450 int
451 puffs_null_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
452 off_t offset, size_t *buflen, const struct puffs_cred *pcred,
453 int ioflag)
454 {
455 struct puffs_node *pn = opc;
456 ssize_t n;
457 off_t off;
458 int fd, rv;
459
460 rv = 0;
461 fd = open(pn->pn_path, O_RDONLY);
462 if (fd == -1)
463 return errno;
464 off = lseek(fd, offset, SEEK_SET);
465 if (off == -1) {
466 rv = errno;
467 goto out;
468 }
469
470 n = read(fd, buf, *buflen);
471 if (n == -1)
472 rv = errno;
473 else
474 *buflen -= n;
475
476 out:
477 close(fd);
478 return rv;
479 }
480
481 /*ARGSUSED*/
482 int
483 puffs_null_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
484 off_t offset, size_t *buflen, const struct puffs_cred *pcred,
485 int ioflag)
486 {
487 struct puffs_node *pn = opc;
488 ssize_t n;
489 off_t off;
490 int fd, rv;
491
492 rv = 0;
493 fd = open(pn->pn_path, O_WRONLY);
494 if (fd == -1)
495 return errno;
496 off = lseek(fd, offset, SEEK_SET);
497 if (off == -1) {
498 rv = errno;
499 goto out;
500 }
501
502 n = write(fd, buf, *buflen);
503 if (n == -1)
504 rv = errno;
505 else
506 *buflen -= n;
507
508 out:
509 close(fd);
510 return rv;
511 }
512