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