null.c revision 1.5 1 /* $NetBSD: null.c,v 1.5 2007/01/27 11:49:44 agc 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.5 2007/01/27 11:49:44 agc 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 /*ARGSUSED*/
55 static void *
56 pathcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
57 {
58 struct puffs_pathobj *po = arg;
59
60 if (strcmp(po->po_path, PNPATH(pn)) == 0)
61 return pn;
62 return NULL;
63 }
64
65 /*
66 * set attributes to what is specified. XXX: no rollback in case of failure
67 */
68 static int
69 processvattr(const char *path, const struct vattr *va, int regular)
70 {
71 struct timeval tv[2];
72
73 /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */
74 if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1)
75 if (lchown(path, va->va_uid, va->va_gid) == -1)
76 return errno;
77
78 if (va->va_mode != (unsigned)PUFFS_VNOVAL)
79 if (lchmod(path, va->va_mode) == -1)
80 return errno;
81
82 /* sloppy */
83 if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL
84 || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) {
85 TIMESPEC_TO_TIMEVAL(&tv[0], &va->va_atime);
86 TIMESPEC_TO_TIMEVAL(&tv[1], &va->va_mtime);
87
88 if (lutimes(path, tv) == -1)
89 return errno;
90 }
91
92 if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL)
93 if (truncate(path, (off_t)va->va_size) == -1)
94 return errno;
95
96 return 0;
97 }
98
99 /*
100 * Kludge to open files which aren't writable *any longer*. This kinda
101 * works because the vfs layer does validation checks based on the file's
102 * permissions to allow writable opening before opening them. However,
103 * the problem arises if we want to create a file, write to it (cache),
104 * adjust permissions and then flush the file.
105 */
106 static int
107 writeableopen(const char *path)
108 {
109 struct stat sb;
110 mode_t origmode;
111 int sverr = 0;
112 int fd;
113
114 fd = open(path, O_WRONLY);
115 if (fd == -1) {
116 if (errno == EACCES) {
117 if (stat(path, &sb) == -1)
118 return errno;
119 origmode = sb.st_mode & ALLPERMS;
120
121 if (chmod(path, 0200) == -1)
122 return errno;
123
124 fd = open(path, O_WRONLY);
125 if (fd == -1)
126 sverr = errno;
127
128 chmod(path, origmode);
129 if (sverr)
130 errno = sverr;
131 } else
132 return errno;
133 }
134
135 return fd;
136 }
137
138 /*ARGSUSED*/
139 int
140 puffs_null_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
141 {
142 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
143
144 if (statvfs(PNPATH(pu->pu_pn_root), svfsb) == -1)
145 return errno;
146
147 return 0;
148 }
149
150 /*
151 * Wow, this kinda looks like a real lookup since it bounces all
152 * over the place
153 */
154 int
155 puffs_null_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
156 enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
157 const struct puffs_cn *pcn)
158 {
159 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
160 struct puffs_node *pn = opc, *pn_res;
161 struct dirent entry, *result;
162 char dpath[MAXPATHLEN+1];
163 char *npath;
164 struct stat sb;
165 DIR *dp;
166 int rv;
167
168 assert(pn->pn_va.va_type == VDIR);
169
170 /* XXX: push this out of here */
171 if (pcn->pcn_flags & PUFFS_ISDOTDOT) {
172 struct puffs_pathobj mypo;
173 char *p;
174
175 strcpy(dpath, PNPATH(pn));
176 p = strrchr(dpath, '/');
177 assert(p != NULL);
178 *p = '\0';
179
180 mypo.po_path = dpath;
181 mypo.po_len = strlen(dpath);
182 pn_res = puffs_pn_nodewalk(pu, pathcmp, &mypo);
183 if (pn_res)
184 goto success;
185 npath = dpath;
186 goto getnew;
187 }
188
189 dp = opendir(PNPATH(pn));
190 if (dp == NULL)
191 return errno;
192
193 /* more readable than for(), IMHO */
194 rv = readdir_r(dp, &entry, &result);
195 while (rv == 0 && result) {
196 if (strcmp(result->d_name, pcn->pcn_name) == 0)
197 break;
198 rv = readdir_r(dp, &entry, &result);
199 }
200 closedir(dp);
201
202 if (rv)
203 return rv;
204 if (!result)
205 return ENOENT;
206
207 /* XXX: UNCONST is wrong, fix the interface */
208 pn_res = puffs_pn_nodewalk(pu, pathcmp, __UNCONST(&pcn->pcn_po_full));
209 if (pn_res)
210 goto success;
211 npath = PCNPATH(pcn);
212
213 getnew:
214 rv = lstat(npath, &sb);
215 if (rv)
216 return rv;
217 pn_res = puffs_pn_new(pu, NULL);
218 if (pn_res == NULL)
219 return ENOMEM;
220 puffs_stat2vattr(&pn_res->pn_va, &sb);
221
222 success:
223 *newnode = pn_res;
224 *newtype = pn_res->pn_va.va_type;
225 *newsize = pn_res->pn_va.va_size;
226 *newrdev = pn_res->pn_va.va_rdev;
227
228 return 0;
229 }
230
231 /*ARGSUSED*/
232 int
233 puffs_null_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
234 const struct puffs_cn *pcn, const struct vattr *va)
235 {
236 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
237 struct puffs_node *pn;
238 int fd, rv;
239
240 fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC);
241 if (fd == -1)
242 return errno;
243 close(fd);
244 if ((rv = processvattr(PCNPATH(pcn), va, 1)) != 0) {
245 unlink(PCNPATH(pcn));
246 return rv;
247 }
248
249 pn = puffs_pn_new(pu, NULL);
250 if (!pn) {
251 unlink(PCNPATH(pcn));
252 return ENOMEM;
253 }
254 puffs_setvattr(&pn->pn_va, va);
255
256 *newnode = pn;
257 return 0;
258 }
259
260 /*ARGSUSED*/
261 int
262 puffs_null_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
263 const struct puffs_cn *pcn, const struct vattr *va)
264 {
265 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
266 struct puffs_node *pn;
267 int rv;
268
269 if (mknod(PCNPATH(pcn), va->va_mode, va->va_rdev) == -1)
270 return errno;
271
272 if ((rv = processvattr(PCNPATH(pcn), va, 0)) != 0) {
273 unlink(PCNPATH(pcn));
274 return rv;
275 }
276
277 pn = puffs_pn_new(pu, NULL);
278 if (!pn) {
279 unlink(PCNPATH(pcn));
280 return ENOMEM;
281 }
282 puffs_setvattr(&pn->pn_va, va);
283
284 *newnode = pn;
285 return 0;
286 }
287
288 /*ARGSUSED*/
289 int
290 puffs_null_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
291 const struct puffs_cred *pcred, pid_t pid)
292 {
293 struct puffs_node *pn = opc;
294 struct stat sb;
295
296 if (lstat(PNPATH(pn), &sb) == -1)
297 return errno;
298 puffs_stat2vattr(va, &sb);
299
300 return 0;
301 }
302
303 /*ARGSUSED*/
304 int
305 puffs_null_node_setattr(struct puffs_cc *pcc, void *opc,
306 const struct vattr *va, const struct puffs_cred *pcred, pid_t pid)
307 {
308 struct puffs_node *pn = opc;
309 int rv;
310
311 rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG);
312 if (rv)
313 return rv;
314
315 puffs_setvattr(&pn->pn_va, va);
316
317 return 0;
318 }
319
320 /*ARGSUSED*/
321 int
322 puffs_null_node_fsync(struct puffs_cc *pcc, void *opc,
323 const struct puffs_cred *pcred, int how,
324 off_t offlo, off_t offhi, pid_t pid)
325 {
326 struct puffs_node *pn = opc;
327 int fd, rv;
328 int fflags;
329
330 rv = 0;
331 fd = writeableopen(PNPATH(pn));
332 if (fd == -1)
333 return errno;
334
335 if (how & PUFFS_FSYNC_DATAONLY)
336 fflags = FDATASYNC;
337 else
338 fflags = FFILESYNC;
339 if (how & PUFFS_FSYNC_CACHE)
340 fflags |= FDISKSYNC;
341
342 if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1)
343 rv = errno;
344
345 close(fd);
346
347 return rv;
348 }
349
350 /*ARGSUSED*/
351 int
352 puffs_null_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
353 const struct puffs_cn *pcn)
354 {
355 struct puffs_node *pn_targ = targ;
356
357 if (unlink(PNPATH(pn_targ)) == -1)
358 return errno;
359
360 return 0;
361 }
362
363 /*ARGSUSED*/
364 int
365 puffs_null_node_link(struct puffs_cc *pcc, void *opc, void *targ,
366 const struct puffs_cn *pcn)
367 {
368 struct puffs_node *pn_targ = targ;
369
370 if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1)
371 return errno;
372
373 return 0;
374 }
375
376 /*ARGSUSED*/
377 int
378 puffs_null_node_rename(struct puffs_cc *pcc, void *opc, void *src,
379 const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
380 const struct puffs_cn *pcn_targ)
381 {
382
383 if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1)
384 return errno;
385
386 return 0;
387 }
388
389 /*ARGSUSED*/
390 int
391 puffs_null_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
392 const struct puffs_cn *pcn, const struct vattr *va)
393 {
394 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
395 struct puffs_node *pn;
396 int rv;
397
398 if (mkdir(PCNPATH(pcn), va->va_mode) == -1)
399 return errno;
400
401 if ((rv = processvattr(PCNPATH(pcn), va, 0)) != 0) {
402 rmdir(PCNPATH(pcn));
403 return rv;
404 }
405
406 pn = puffs_pn_new(pu, NULL);
407 if (pn == NULL) {
408 rmdir(PCNPATH(pcn));
409 return ENOMEM;
410 }
411 puffs_setvattr(&pn->pn_va, va);
412
413 *newnode = pn;
414 return 0;
415 }
416
417 /*ARGSUSED*/
418 int
419 puffs_null_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
420 const struct puffs_cn *pcn)
421 {
422 struct puffs_node *pn_targ = targ;
423
424 if (rmdir(PNPATH(pn_targ)) == -1)
425 return errno;
426
427 return 0;
428 }
429
430 /*ARGSUSED*/
431 int
432 puffs_null_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
433 const struct puffs_cn *pcn, const struct vattr *va,
434 const char *linkname)
435 {
436 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
437 struct puffs_node *pn;
438 int rv;
439
440 if (symlink(linkname, PCNPATH(pcn)) == -1)
441 return errno;
442
443 if ((rv = processvattr(PCNPATH(pcn), va, 0)) != 0) {
444 unlink(PCNPATH(pcn));
445 return rv;
446 }
447
448 pn = puffs_pn_new(pu, NULL);
449 if (pn == NULL) {
450 rmdir(PCNPATH(pcn));
451 return ENOMEM;
452 }
453 puffs_setvattr(&pn->pn_va, va);
454
455 *newnode = pn;
456 return 0;
457 }
458
459 /*ARGSUSED*/
460 int
461 puffs_null_node_readlink(struct puffs_cc *pcc, void *opc,
462 const struct puffs_cred *pcred, char *linkname, size_t *linklen)
463 {
464 struct puffs_node *pn = opc;
465 ssize_t rv;
466
467 rv = readlink(PNPATH(pn), linkname, *linklen);
468 if (rv == -1)
469 return errno;
470
471 *linklen -= rv;
472 return 0;
473 }
474
475 /*ARGSUSED*/
476 int
477 puffs_null_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *de,
478 const struct puffs_cred *pcred, off_t *off, size_t *reslen)
479 {
480 struct puffs_node *pn = opc;
481 struct dirent entry, *result;
482 DIR *dp;
483 off_t i;
484 int rv;
485
486 dp = opendir(PNPATH(pn));
487 if (dp == NULL)
488 return errno;
489
490 rv = 0;
491 i = *off;
492
493 /*
494 * XXX: need to do trickery here, telldir/seekdir would be nice, but
495 * then we'd need to keep state, which I'm too lazy to keep
496 */
497 while (i--) {
498 rv = readdir_r(dp, &entry, &result);
499 if (rv || !result)
500 goto out;
501 }
502
503 for (;;) {
504 rv = readdir_r(dp, &entry, &result);
505 if (rv != 0)
506 goto out;
507
508 if (!result)
509 goto out;
510
511 if (_DIRENT_SIZE(result) > *reslen)
512 goto out;
513
514 *de = *result;
515 *reslen -= _DIRENT_SIZE(result);
516 de = _DIRENT_NEXT(de);
517
518 (*off)++;
519 }
520
521 out:
522 closedir(dp);
523 return 0;
524 }
525
526 /*ARGSUSED*/
527 int
528 puffs_null_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
529 off_t offset, size_t *buflen, const struct puffs_cred *pcred,
530 int ioflag)
531 {
532 struct puffs_node *pn = opc;
533 ssize_t n;
534 off_t off;
535 int fd, rv;
536
537 rv = 0;
538 fd = open(PNPATH(pn), O_RDONLY);
539 if (fd == -1)
540 return errno;
541 off = lseek(fd, offset, SEEK_SET);
542 if (off == -1) {
543 rv = errno;
544 goto out;
545 }
546
547 n = read(fd, buf, *buflen);
548 if (n == -1)
549 rv = errno;
550 else
551 *buflen -= n;
552
553 out:
554 close(fd);
555 return rv;
556 }
557
558 /*ARGSUSED*/
559 int
560 puffs_null_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
561 off_t offset, size_t *buflen, const struct puffs_cred *pcred,
562 int ioflag)
563 {
564 struct puffs_node *pn = opc;
565 ssize_t n;
566 off_t off;
567 int fd, rv;
568
569 rv = 0;
570 fd = writeableopen(PNPATH(pn));
571 if (fd == -1)
572 return errno;
573
574 off = lseek(fd, offset, SEEK_SET);
575 if (off == -1) {
576 rv = errno;
577 goto out;
578 }
579
580 n = write(fd, buf, *buflen);
581 if (n == -1)
582 rv = errno;
583 else
584 *buflen -= n;
585
586 out:
587 close(fd);
588 return rv;
589 }
590