add opendir alias
[minix.git] / lib / libpuffs / null.c
blob5e192406290ca7e482940fd16280cc10267025e8
1 /* $NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka Exp $ */
3 /*
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
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.
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.
28 #include <sys/cdefs.h>
29 #if !defined(lint)
30 __RCSID("$NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka Exp $");
31 #endif /* !lint */
34 * A "nullfs" using puffs, i.e. maps one location in the hierarchy
35 * to another using standard system calls.
38 #include <sys/types.h>
39 #include <sys/time.h>
41 #include <assert.h>
42 #include <dirent.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <utime.h>
50 #include "puffs.h"
53 PUFFSOP_PROTOS(puffs_null)
56 * set attributes to what is specified. XXX: no rollback in case of failure
58 static int
59 processvattr(const char *path, const struct vattr *va, int regular)
61 struct utimbuf tbuf;
63 /* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */
64 if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1)
65 /* FIXME: lchown */
66 if (chown(path, va->va_uid, va->va_gid) == -1)
67 return errno;
69 #ifndef __minix
70 if (va->va_mode != (unsigned)PUFFS_VNOVAL)
71 #endif
72 /* FIXME: lchmod */
73 if (chmod(path, va->va_mode) == -1)
74 return errno;
76 /* sloppy */
77 if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL
78 || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) {
79 /* FIXME: nsec too */
80 tbuf.actime = va->va_atime.tv_sec;
81 tbuf.modtime = va->va_mtime.tv_sec;
83 /* FIXME: lutimes */
84 if (utime(path, &tbuf) == -1)
85 return errno;
88 if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL)
89 if (truncate(path, (off_t)va->va_size) == -1)
90 return errno;
92 return 0;
96 * Kludge to open files which aren't writable *any longer*. This kinda
97 * works because the vfs layer does validation checks based on the file's
98 * permissions to allow writable opening before opening them. However,
99 * the problem arises if we want to create a file, write to it (cache),
100 * adjust permissions and then flush the file.
102 static int
103 writeableopen(const char *path)
105 struct stat sb;
106 mode_t origmode;
107 int sverr = 0;
108 int fd;
110 fd = open(path, O_WRONLY);
111 if (fd == -1) {
112 if (errno == EACCES) {
113 if (stat(path, &sb) == -1)
114 return -1;
115 origmode = sb.st_mode & ALLPERMS;
117 if (chmod(path, 0200) == -1)
118 return -1;
120 fd = open(path, O_WRONLY);
121 if (fd == -1)
122 sverr = errno;
124 chmod(path, origmode);
125 if (sverr)
126 errno = sverr;
127 } else
128 return -1;
131 return fd;
134 /*ARGSUSED*/
135 static void *
136 inodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
138 ino_t *cmpino = arg;
140 if (pn->pn_va.va_fileid == *cmpino)
141 return pn;
142 return NULL;
145 static int
146 makenode(struct puffs_usermount *pu, struct puffs_newinfo *pni,
147 const struct puffs_cn *pcn, const struct vattr *va, int regular)
149 struct puffs_node *pn;
150 struct stat sb;
151 int rv;
153 if ((rv = processvattr(PCNPATH(pcn), va, regular)) != 0)
154 return rv;
156 pn = puffs_pn_new(pu, NULL);
157 if (!pn)
158 return ENOMEM;
159 puffs_setvattr(&pn->pn_va, va);
161 if (lstat(PCNPATH(pcn), &sb) == -1)
162 return errno;
163 puffs_stat2vattr(&pn->pn_va, &sb);
165 puffs_newinfo_setcookie(pni, pn);
166 return 0;
169 /* This should be called first and overriden from the file system */
170 void
171 puffs_null_setops(struct puffs_ops *pops)
174 PUFFSOP_SET(pops, puffs_null, fs, statvfs);
175 PUFFSOP_SETFSNOP(pops, unmount);
176 PUFFSOP_SETFSNOP(pops, sync);
178 PUFFSOP_SET(pops, puffs_null, node, lookup);
179 PUFFSOP_SET(pops, puffs_null, node, create);
180 PUFFSOP_SET(pops, puffs_null, node, mknod);
181 PUFFSOP_SET(pops, puffs_null, node, getattr);
182 PUFFSOP_SET(pops, puffs_null, node, setattr);
183 PUFFSOP_SET(pops, puffs_null, node, fsync);
184 PUFFSOP_SET(pops, puffs_null, node, remove);
185 PUFFSOP_SET(pops, puffs_null, node, link);
186 PUFFSOP_SET(pops, puffs_null, node, rename);
187 PUFFSOP_SET(pops, puffs_null, node, mkdir);
188 PUFFSOP_SET(pops, puffs_null, node, rmdir);
189 PUFFSOP_SET(pops, puffs_null, node, symlink);
190 PUFFSOP_SET(pops, puffs_null, node, readlink);
191 PUFFSOP_SET(pops, puffs_null, node, readdir);
192 PUFFSOP_SET(pops, puffs_null, node, read);
193 PUFFSOP_SET(pops, puffs_null, node, write);
194 PUFFSOP_SET(pops, puffs_genfs, node, reclaim);
197 /*ARGSUSED*/
199 puffs_null_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb)
202 if (statvfs(PNPATH(puffs_getroot(pu)), svfsb) == -1)
203 return errno;
205 return 0;
209 puffs_null_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
210 struct puffs_newinfo *pni, const struct puffs_cn *pcn)
212 struct puffs_node *pn = opc, *pn_res;
213 struct stat sb;
214 int rv;
216 assert(pn->pn_va.va_type == VDIR);
219 * Note to whoever is copypasting this: you must first check
220 * if the node is there and only then do nodewalk. Alternatively
221 * you could make sure that you don't return unlinked/rmdir'd
222 * nodes in some other fashion
224 rv = lstat(PCNPATH(pcn), &sb);
225 if (rv)
226 return errno;
228 /* XXX2: nodewalk is a bit too slow here */
229 pn_res = puffs_pn_nodewalk(pu, inodecmp, &sb.st_ino);
231 if (pn_res == NULL) {
232 pn_res = puffs_pn_new(pu, NULL);
233 if (pn_res == NULL)
234 return ENOMEM;
235 puffs_stat2vattr(&pn_res->pn_va, &sb);
238 puffs_newinfo_setcookie(pni, pn_res);
239 puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type);
240 puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size);
241 puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev);
243 return 0;
246 /*ARGSUSED*/
248 puffs_null_node_create(struct puffs_usermount *pu, puffs_cookie_t opc,
249 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
250 const struct vattr *va)
252 int fd, rv;
254 fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC);
255 if (fd == -1)
256 return errno;
257 close(fd);
259 rv = makenode(pu, pni, pcn, va, 1);
260 if (rv)
261 unlink(PCNPATH(pcn));
262 return rv;
265 /*ARGSUSED*/
267 puffs_null_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc,
268 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
269 const struct vattr *va)
271 mode_t mode;
272 int rv;
274 mode = puffs_addvtype2mode(va->va_mode, va->va_type);
275 if (mknod(PCNPATH(pcn), mode, va->va_rdev) == -1)
276 return errno;
278 rv = makenode(pu, pni, pcn, va, 0);
279 if (rv)
280 unlink(PCNPATH(pcn));
281 return rv;
284 /*ARGSUSED*/
286 puffs_null_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
287 struct vattr *va, const struct puffs_cred *pcred)
289 struct puffs_node *pn = opc;
290 struct stat sb;
292 if (lstat(PNPATH(pn), &sb) == -1)
293 return errno;
294 puffs_stat2vattr(va, &sb);
296 return 0;
299 /*ARGSUSED*/
301 puffs_null_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
302 const struct vattr *va, const struct puffs_cred *pcred)
304 struct puffs_node *pn = opc;
305 int rv;
307 rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG);
308 if (rv)
309 return rv;
311 puffs_setvattr(&pn->pn_va, va);
313 return 0;
316 /*ARGSUSED*/
318 puffs_null_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc,
319 const struct puffs_cred *pcred, int how,
320 off_t offlo, off_t offhi)
322 /* FIXME: implement me. */
323 #if 0
324 struct puffs_node *pn = opc;
325 int fd, rv;
326 int fflags;
328 rv = 0;
329 fd = writeableopen(PNPATH(pn));
330 if (fd == -1)
331 return errno;
333 if (how & PUFFS_FSYNC_DATAONLY)
334 fflags = FDATASYNC;
335 else
336 fflags = FFILESYNC;
337 if (how & PUFFS_FSYNC_CACHE)
338 fflags |= FDISKSYNC;
340 if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1)
341 rv = errno;
343 close(fd);
345 return rv;
346 #endif
347 return 0;
350 /*ARGSUSED*/
352 puffs_null_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc,
353 puffs_cookie_t targ, const struct puffs_cn *pcn)
355 struct puffs_node *pn_targ = targ;
357 if (unlink(PCNPATH(pcn)) == -1)
358 return errno;
359 puffs_pn_remove(pn_targ);
361 return 0;
364 /*ARGSUSED*/
366 puffs_null_node_link(struct puffs_usermount *pu, puffs_cookie_t opc,
367 puffs_cookie_t targ, const struct puffs_cn *pcn)
369 struct puffs_node *pn_targ = targ;
371 if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1)
372 return errno;
374 return 0;
377 /*ARGSUSED*/
379 puffs_null_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc,
380 puffs_cookie_t src, const struct puffs_cn *pcn_src,
381 puffs_cookie_t targ_dir, puffs_cookie_t targ,
382 const struct puffs_cn *pcn_targ)
384 struct puffs_node *pn_targ = targ;
386 if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1)
387 return errno;
389 if (pn_targ)
390 puffs_pn_remove(pn_targ);
392 return 0;
395 /*ARGSUSED*/
397 puffs_null_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc,
398 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
399 const struct vattr *va)
401 int rv;
403 if (mkdir(PCNPATH(pcn), va->va_mode) == -1)
404 return errno;
406 rv = makenode(pu, pni, pcn, va, 0);
407 if (rv)
408 rmdir(PCNPATH(pcn));
409 return rv;
412 /*ARGSUSED*/
414 puffs_null_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc,
415 puffs_cookie_t targ, const struct puffs_cn *pcn)
417 struct puffs_node *pn_targ = targ;
419 if (rmdir(PNPATH(pn_targ)) == -1)
420 return errno;
421 puffs_pn_remove(pn_targ);
423 return 0;
426 /*ARGSUSED*/
428 puffs_null_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc,
429 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
430 const struct vattr *va, const char *linkname)
432 int rv;
434 if (symlink(linkname, PCNPATH(pcn)) == -1)
435 return errno;
437 rv = makenode(pu, pni, pcn, va, 0);
438 if (rv)
439 unlink(PCNPATH(pcn));
440 return rv;
443 /*ARGSUSED*/
445 puffs_null_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc,
446 const struct puffs_cred *pcred, char *linkname, size_t *linklen)
448 struct puffs_node *pn = opc;
449 ssize_t rv;
451 rv = readlink(PNPATH(pn), linkname, *linklen);
452 if (rv == -1)
453 return errno;
455 *linklen = rv;
456 return 0;
459 /*ARGSUSED*/
461 puffs_null_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc,
462 struct dirent *de, off_t *off, size_t *reslen,
463 const struct puffs_cred *pcred, int *eofflag, off_t *cookies,
464 size_t *ncookies)
466 /* TODO: use original code since we have libc from NetBSD */
467 struct puffs_node *pn = opc;
468 struct dirent *entry;
469 DIR *dp;
470 off_t i;
471 int rv;
473 dp = opendir(PNPATH(pn));
474 if (dp == NULL)
475 return errno;
477 rv = 0;
478 i = *off;
481 * XXX: need to do trickery here, telldir/seekdir would be nice, but
482 * then we'd need to keep state, which I'm too lazy to keep
484 while (i--) {
485 entry = readdir(dp);
486 if (!entry) {
487 *eofflag = 1;
488 goto out;
492 for (;;) {
493 /* FIXME: DIRENT_SIZE macro? For now do calculations here */
494 int namelen;
495 char* cp;
496 int dirent_size;
498 entry = readdir(dp);
500 if (!entry) {
501 *eofflag = 1;
502 goto out;
505 cp = memchr(entry->d_name, '\0', NAME_MAX);
506 if (cp == NULL)
507 namelen = NAME_MAX;
508 else
509 namelen = cp - (entry->d_name);
510 dirent_size = _DIRENT_RECLEN(entry, namelen);
512 if (dirent_size > *reslen)
513 goto out;
515 *de = *entry;
516 strncpy(de->d_name, entry->d_name, namelen);
517 *reslen -= dirent_size;
519 de = _DIRENT_NEXT(de);
520 (*off)++;
523 out:
524 closedir(dp);
525 return 0;
528 /*ARGSUSED*/
530 puffs_null_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
531 uint8_t *buf, off_t offset, size_t *buflen,
532 const struct puffs_cred *pcred, int ioflag)
534 struct puffs_node *pn = opc;
535 ssize_t n;
536 off_t off;
537 int fd, rv;
539 rv = 0;
540 fd = open(PNPATH(pn), O_RDONLY);
541 if (fd == -1)
542 return errno;
543 off = lseek(fd, offset, SEEK_SET);
544 if (off == -1) {
545 rv = errno;
546 goto out;
549 n = read(fd, buf, *buflen);
550 if (n == -1)
551 rv = errno;
552 else
553 *buflen -= n;
555 out:
556 close(fd);
557 return rv;
560 /*ARGSUSED*/
562 puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
563 uint8_t *buf, off_t offset, size_t *buflen,
564 const struct puffs_cred *pcred, int ioflag)
566 struct puffs_node *pn = opc;
567 ssize_t n;
568 off_t off;
569 int fd, rv;
571 rv = 0;
572 fd = writeableopen(PNPATH(pn));
573 if (fd == -1)
574 return errno;
576 off = lseek(fd, offset, SEEK_SET);
577 if (off == -1) {
578 rv = errno;
579 goto out;
582 n = write(fd, buf, *buflen);
583 if (n == -1)
584 rv = errno;
585 else
586 *buflen -= n;
588 out:
589 close(fd);
590 return rv;