xref: /netbsd-src/lib/libukfs/ukfs.c (revision 10ad5ffa714ce1a679dcc9dd8159648df2d67b5a)
1 /*	$NetBSD: ukfs.c,v 1.32 2009/07/23 01:01:31 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008  Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Finnish Cultural Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
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 /*
32  * This library enables access to files systems directly without
33  * involving system calls.
34  */
35 
36 #ifdef __linux__
37 #define _XOPEN_SOURCE 500
38 #define _BSD_SOURCE
39 #define _FILE_OFFSET_BITS 64
40 #endif
41 
42 #include <sys/param.h>
43 #include <sys/queue.h>
44 #include <sys/stat.h>
45 #include <sys/sysctl.h>
46 #include <sys/mount.h>
47 
48 #include <assert.h>
49 #include <dirent.h>
50 #include <dlfcn.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <pthread.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <stdint.h>
60 
61 #include <rump/ukfs.h>
62 
63 #include <rump/rump.h>
64 #include <rump/rump_syscalls.h>
65 
66 #define UKFS_MODE_DEFAULT 0555
67 
68 struct ukfs {
69 	struct mount *ukfs_mp;
70 	struct vnode *ukfs_rvp;
71 
72 	pthread_spinlock_t ukfs_spin;
73 	pid_t ukfs_nextpid;
74 	struct vnode *ukfs_cdir;
75 	int ukfs_devfd;
76 	char *ukfs_devpath;
77 	char *ukfs_mountpath;
78 };
79 
80 static int builddirs(const char *, mode_t,
81     int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *);
82 
83 struct mount *
84 ukfs_getmp(struct ukfs *ukfs)
85 {
86 
87 	return ukfs->ukfs_mp;
88 }
89 
90 struct vnode *
91 ukfs_getrvp(struct ukfs *ukfs)
92 {
93 	struct vnode *rvp;
94 
95 	rvp = ukfs->ukfs_rvp;
96 	rump_vp_incref(rvp);
97 
98 	return rvp;
99 }
100 
101 #ifdef DONT_WANT_PTHREAD_LINKAGE
102 #define pthread_spin_lock(a)
103 #define pthread_spin_unlock(a)
104 #define pthread_spin_init(a,b)
105 #define pthread_spin_destroy(a)
106 #endif
107 
108 static pid_t
109 nextpid(struct ukfs *ukfs)
110 {
111 	pid_t npid;
112 
113 	pthread_spin_lock(&ukfs->ukfs_spin);
114 	if (ukfs->ukfs_nextpid == 0)
115 		ukfs->ukfs_nextpid++;
116 	npid = ukfs->ukfs_nextpid++;
117 	pthread_spin_unlock(&ukfs->ukfs_spin);
118 
119 	return npid;
120 }
121 
122 static void
123 precall(struct ukfs *ukfs)
124 {
125 	struct vnode *rvp, *cvp;
126 
127 	rump_setup_curlwp(nextpid(ukfs), 1, 1);
128 	rvp = ukfs_getrvp(ukfs);
129 	pthread_spin_lock(&ukfs->ukfs_spin);
130 	cvp = ukfs->ukfs_cdir;
131 	pthread_spin_unlock(&ukfs->ukfs_spin);
132 	rump_rcvp_set(rvp, cvp); /* takes refs */
133 	rump_vp_rele(rvp);
134 }
135 
136 static void
137 postcall(struct ukfs *ukfs)
138 {
139 	struct vnode *rvp;
140 
141 	rvp = ukfs_getrvp(ukfs);
142 	rump_rcvp_set(NULL, rvp);
143 	rump_vp_rele(rvp);
144 	rump_clear_curlwp();
145 }
146 
147 int
148 _ukfs_init(int version)
149 {
150 	int rv;
151 
152 	if (version != UKFS_VERSION) {
153 		printf("incompatible ukfs version, %d vs. %d\n",
154 		    version, UKFS_VERSION);
155 		errno = EPROGMISMATCH;
156 		return -1;
157 	}
158 
159 	if ((rv = rump_init()) != 0) {
160 		errno = rv;
161 		return -1;
162 	}
163 
164 	return 0;
165 }
166 
167 /*ARGSUSED*/
168 static int
169 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode)
170 {
171 
172 	return rump_sys_mkdir(path, mode);
173 }
174 
175 struct ukfs *
176 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath,
177 	int mntflags, void *arg, size_t alen)
178 {
179 	struct stat sb;
180 	struct ukfs *fs = NULL;
181 	int rv = 0, devfd = -1, rdonly;
182 	int mounted = 0;
183 
184 	/*
185 	 * Try open and lock the device.  if we can't open it, assume
186 	 * it's a file system which doesn't use a real device and let
187 	 * it slide.  The mount will fail anyway if the fs requires a
188 	 * device.
189 	 *
190 	 * XXX: strictly speaking this is not 100% correct, as virtual
191 	 * file systems can use a device path which does exist and can
192 	 * be opened.  E.g. tmpfs should be mountable multiple times
193 	 * with "device" path "/swap", but now isn't.  But I think the
194 	 * chances are so low that it's currently acceptable to let
195 	 * this one slip.
196 	 */
197 	rdonly = mntflags & MNT_RDONLY;
198 	devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR);
199 	if (devfd != -1) {
200 		if (fstat(devfd, &sb) == -1) {
201 			close(devfd);
202 			devfd = -1;
203 			rv = errno;
204 			goto out;
205 		}
206 
207 		/*
208 		 * We do this only for non-block device since the
209 		 * (NetBSD) kernel allows block device open only once.
210 		 */
211 		if (!S_ISBLK(sb.st_mode)) {
212 			if (flock(devfd, LOCK_NB | (rdonly ? LOCK_SH:LOCK_EX))
213 			    == -1) {
214 				warnx("ukfs_mount: cannot get %s lock on "
215 				    "device", rdonly ? "shared" : "exclusive");
216 				close(devfd);
217 				devfd = -1;
218 				rv = errno;
219 				goto out;
220 			}
221 		} else {
222 			close(devfd);
223 			devfd = -1;
224 		}
225 	}
226 
227 	fs = malloc(sizeof(struct ukfs));
228 	if (fs == NULL) {
229 		rv = ENOMEM;
230 		goto out;
231 	}
232 	memset(fs, 0, sizeof(struct ukfs));
233 
234 	/* create our mountpoint.  this is never removed. */
235 	if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) {
236 		if (errno != EEXIST) {
237 			rv = errno;
238 			goto out;
239 		}
240 	}
241 
242 	rump_fakeblk_register(devpath);
243 	rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen);
244 	if (rv) {
245 		goto out;
246 	}
247 	mounted = 1;
248 	rv = rump_vfs_getmp(mountpath, &fs->ukfs_mp);
249 	if (rv) {
250 		goto out;
251 	}
252 	rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
253 	if (rv) {
254 		goto out;
255 	}
256 
257 	fs->ukfs_devpath = strdup(devpath);
258 	fs->ukfs_mountpath = strdup(mountpath);
259 	fs->ukfs_cdir = ukfs_getrvp(fs);
260 	pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
261 	fs->ukfs_devfd = devfd;
262 	assert(rv == 0);
263 
264  out:
265 	if (rv) {
266 		int sverrno = errno;
267 		if (fs) {
268 			if (fs->ukfs_rvp)
269 				rump_vp_rele(fs->ukfs_rvp);
270 			free(fs);
271 			fs = NULL;
272 		}
273 		if (mounted)
274 			rump_sys_unmount(mountpath, MNT_FORCE);
275 		if (devfd != -1) {
276 			flock(devfd, LOCK_UN);
277 			close(devfd);
278 		}
279 		errno = sverrno;
280 	}
281 
282 	return fs;
283 }
284 
285 int
286 ukfs_release(struct ukfs *fs, int flags)
287 {
288 
289 	if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
290 		int rv, mntflag;
291 
292 		ukfs_chdir(fs, "/");
293 		mntflag = 0;
294 		if (flags & UKFS_RELFLAG_FORCE)
295 			mntflag = MNT_FORCE;
296 		rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag);
297 		if (rv) {
298 			ukfs_chdir(fs, fs->ukfs_mountpath);
299 			errno = rv;
300 			return -1;
301 		}
302 	}
303 
304 	rump_fakeblk_deregister(fs->ukfs_devpath);
305 	free(fs->ukfs_devpath);
306 	free(fs->ukfs_mountpath);
307 
308 	pthread_spin_destroy(&fs->ukfs_spin);
309 	if (fs->ukfs_devfd != -1) {
310 		flock(fs->ukfs_devfd, LOCK_UN);
311 		close(fs->ukfs_devfd);
312 	}
313 	free(fs);
314 
315 	return 0;
316 }
317 
318 #define STDCALL(ukfs, thecall)						\
319 	int rv = 0;							\
320 									\
321 	precall(ukfs);							\
322 	rv = thecall;							\
323 	postcall(ukfs);							\
324 	return rv;
325 
326 int
327 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c)
328 {
329 	struct vnode *vp;
330 	int rv;
331 
332 	precall(ukfs);
333 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
334 	    NULL, &vp, NULL);
335 	postcall(ukfs);
336 
337 	if (rv == 0) {
338 		RUMP_VOP_UNLOCK(vp, 0);
339 	} else {
340 		errno = rv;
341 		rv = -1;
342 	}
343 
344 	/*LINTED*/
345 	*c = (struct ukfs_dircookie *)vp;
346 	return rv;
347 }
348 
349 static int
350 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize)
351 {
352 	struct uio *uio;
353 	size_t resid;
354 	int rv, eofflag;
355 	kauth_cred_t cred;
356 
357 	uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
358 	cred = rump_cred_suserget();
359 	rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
360 	rump_cred_suserput(cred);
361 	RUMP_VOP_UNLOCK(vp, 0);
362 	*off = rump_uio_getoff(uio);
363 	resid = rump_uio_free(uio);
364 
365 	if (rv) {
366 		errno = rv;
367 		return -1;
368 	}
369 
370 	/* LINTED: not totally correct return type, but follows syscall */
371 	return bufsize - resid;
372 }
373 
374 /*ARGSUSED*/
375 int
376 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off,
377 	uint8_t *buf, size_t bufsize)
378 {
379 	/*LINTED*/
380 	struct vnode *vp = (struct vnode *)c;
381 
382 	RUMP_VOP_LOCK(vp, RUMP_LK_SHARED);
383 	return getmydents(vp, off, buf, bufsize);
384 }
385 
386 int
387 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
388 	uint8_t *buf, size_t bufsize)
389 {
390 	struct vnode *vp;
391 	int rv;
392 
393 	precall(ukfs);
394 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
395 	    NULL, &vp, NULL);
396 	postcall(ukfs);
397 	if (rv) {
398 		errno = rv;
399 		return -1;
400 	}
401 
402 	rv = getmydents(vp, off, buf, bufsize);
403 	rump_vp_rele(vp);
404 	return rv;
405 }
406 
407 /*ARGSUSED*/
408 int
409 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c)
410 {
411 
412 	/*LINTED*/
413 	rump_vp_rele((struct vnode *)c);
414 	return 0;
415 }
416 
417 int
418 ukfs_open(struct ukfs *ukfs, const char *filename, int flags)
419 {
420 	int fd;
421 
422 	precall(ukfs);
423 	fd = rump_sys_open(filename, flags, 0);
424 	postcall(ukfs);
425 	if (fd == -1)
426 		return -1;
427 
428 	return fd;
429 }
430 
431 ssize_t
432 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
433 	uint8_t *buf, size_t bufsize)
434 {
435 	int fd;
436 	ssize_t xfer = -1; /* XXXgcc */
437 
438 	precall(ukfs);
439 	fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
440 	if (fd == -1)
441 		goto out;
442 
443 	xfer = rump_sys_pread(fd, buf, bufsize, off);
444 	rump_sys_close(fd);
445 
446  out:
447 	postcall(ukfs);
448 	if (fd == -1) {
449 		return -1;
450 	}
451 	return xfer;
452 }
453 
454 /*ARGSUSED*/
455 ssize_t
456 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen)
457 {
458 
459 	return rump_sys_pread(fd, buf, buflen, off);
460 }
461 
462 ssize_t
463 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
464 	uint8_t *buf, size_t bufsize)
465 {
466 	int fd;
467 	ssize_t xfer = -1; /* XXXgcc */
468 
469 	precall(ukfs);
470 	fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
471 	if (fd == -1)
472 		goto out;
473 
474 	/* write and commit */
475 	xfer = rump_sys_pwrite(fd, buf, bufsize, off);
476 	if (xfer > 0)
477 		rump_sys_fsync(fd);
478 
479 	rump_sys_close(fd);
480 
481  out:
482 	postcall(ukfs);
483 	if (fd == -1) {
484 		return -1;
485 	}
486 	return xfer;
487 }
488 
489 /*ARGSUSED*/
490 ssize_t
491 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen,
492 	int dosync)
493 {
494 	ssize_t xfer;
495 
496 	xfer = rump_sys_pwrite(fd, buf, buflen, off);
497 	if (xfer > 0 && dosync)
498 		rump_sys_fsync(fd);
499 
500 	return xfer;
501 }
502 
503 /*ARGSUSED*/
504 int
505 ukfs_close(struct ukfs *ukfs, int fd)
506 {
507 
508 	rump_sys_close(fd);
509 	return 0;
510 }
511 
512 int
513 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
514 {
515 	int fd;
516 
517 	precall(ukfs);
518 	fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
519 	if (fd == -1)
520 		return -1;
521 	rump_sys_close(fd);
522 
523 	postcall(ukfs);
524 	return 0;
525 }
526 
527 int
528 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
529 {
530 
531 	STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
532 }
533 
534 int
535 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
536 {
537 
538 	STDCALL(ukfs, rump_sys_mkfifo(path, mode));
539 }
540 
541 int
542 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
543 {
544 
545 	STDCALL(ukfs, rump_sys_mkdir(filename, mode));
546 }
547 
548 int
549 ukfs_remove(struct ukfs *ukfs, const char *filename)
550 {
551 
552 	STDCALL(ukfs, rump_sys_unlink(filename));
553 }
554 
555 int
556 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
557 {
558 
559 	STDCALL(ukfs, rump_sys_rmdir(filename));
560 }
561 
562 int
563 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
564 {
565 
566 	STDCALL(ukfs, rump_sys_link(filename, f_create));
567 }
568 
569 int
570 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
571 {
572 
573 	STDCALL(ukfs, rump_sys_symlink(filename, linkname));
574 }
575 
576 ssize_t
577 ukfs_readlink(struct ukfs *ukfs, const char *filename,
578 	char *linkbuf, size_t buflen)
579 {
580 	ssize_t rv;
581 
582 	precall(ukfs);
583 	rv = rump_sys_readlink(filename, linkbuf, buflen);
584 	postcall(ukfs);
585 	return rv;
586 }
587 
588 int
589 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
590 {
591 
592 	STDCALL(ukfs, rump_sys_rename(from, to));
593 }
594 
595 int
596 ukfs_chdir(struct ukfs *ukfs, const char *path)
597 {
598 	struct vnode *newvp, *oldvp;
599 	int rv;
600 
601 	precall(ukfs);
602 	rv = rump_sys_chdir(path);
603 	if (rv == -1)
604 		goto out;
605 
606 	newvp = rump_cdir_get();
607 	pthread_spin_lock(&ukfs->ukfs_spin);
608 	oldvp = ukfs->ukfs_cdir;
609 	ukfs->ukfs_cdir = newvp;
610 	pthread_spin_unlock(&ukfs->ukfs_spin);
611 	if (oldvp)
612 		rump_vp_rele(oldvp);
613 
614  out:
615 	postcall(ukfs);
616 	return rv;
617 }
618 
619 /*
620  * If we want to use post-time_t file systems on pre-time_t hosts,
621  * we must translate the stat structure.  Since we don't currently
622  * have a general method for making compat calls in rump, special-case
623  * this one.
624  *
625  * Note that this does not allow making system calls to older rump
626  * kernels from newer hosts.
627  */
628 #define VERS_TIMECHANGE 599000700
629 
630 static int
631 needcompat(void)
632 {
633 
634 #ifdef __NetBSD__
635 	/*LINTED*/
636 	return __NetBSD_Version__ < VERS_TIMECHANGE
637 	    && rump_getversion() >= VERS_TIMECHANGE;
638 #else
639 	return 0;
640 #endif
641 }
642 
643 int
644 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
645 {
646 	int rv;
647 
648 	precall(ukfs);
649 	if (needcompat())
650 		rv = rump_sys___stat30(filename, file_stat);
651 	else
652 		rv = rump_sys_stat(filename, file_stat);
653 	postcall(ukfs);
654 
655 	return rv;
656 }
657 
658 int
659 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
660 {
661 	int rv;
662 
663 	precall(ukfs);
664 	if (needcompat())
665 		rv = rump_sys___lstat30(filename, file_stat);
666 	else
667 		rv = rump_sys_lstat(filename, file_stat);
668 	postcall(ukfs);
669 
670 	return rv;
671 }
672 
673 int
674 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
675 {
676 
677 	STDCALL(ukfs, rump_sys_chmod(filename, mode));
678 }
679 
680 int
681 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
682 {
683 
684 	STDCALL(ukfs, rump_sys_lchmod(filename, mode));
685 }
686 
687 int
688 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
689 {
690 
691 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
692 }
693 
694 int
695 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
696 {
697 
698 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
699 }
700 
701 int
702 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
703 {
704 
705 	STDCALL(ukfs, rump_sys_chflags(filename, flags));
706 }
707 
708 int
709 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
710 {
711 
712 	STDCALL(ukfs, rump_sys_lchflags(filename, flags));
713 }
714 
715 int
716 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
717 {
718 
719 	STDCALL(ukfs, rump_sys_utimes(filename, tptr));
720 }
721 
722 int
723 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
724 	      const struct timeval *tptr)
725 {
726 
727 	STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
728 }
729 
730 /*
731  * Dynamic module support
732  */
733 
734 /* load one library */
735 
736 /*
737  * XXX: the dlerror stuff isn't really threadsafe, but then again I
738  * can't protect against other threads calling dl*() outside of ukfs,
739  * so just live with it being flimsy
740  */
741 int
742 ukfs_modload(const char *fname)
743 {
744 	void *handle;
745 	struct modinfo **mi;
746 	int error;
747 
748 	handle = dlopen(fname, RTLD_GLOBAL);
749 	if (handle == NULL) {
750 		const char *dlmsg = dlerror();
751 		if (strstr(dlmsg, "Undefined symbol"))
752 			return 0;
753 		warnx("dlopen %s failed: %s\n", fname, dlmsg);
754 		/* XXXerrno */
755 		return -1;
756 	}
757 
758 	mi = dlsym(handle, "__start_link_set_modules");
759 	if (mi) {
760 		error = rump_module_init(*mi, NULL);
761 		if (error)
762 			goto errclose;
763 		return 1;
764 	}
765 	error = EINVAL;
766 
767  errclose:
768 	dlclose(handle);
769 	errno = error;
770 	return -1;
771 }
772 
773 struct loadfail {
774 	char *pname;
775 
776 	LIST_ENTRY(loadfail) entries;
777 };
778 
779 #define RUMPFSMOD_PREFIX "librumpfs_"
780 #define RUMPFSMOD_SUFFIX ".so"
781 
782 int
783 ukfs_modload_dir(const char *dir)
784 {
785 	char nbuf[MAXPATHLEN+1], *p;
786 	struct dirent entry, *result;
787 	DIR *libdir;
788 	struct loadfail *lf, *nlf;
789 	int error, nloaded = 0, redo;
790 	LIST_HEAD(, loadfail) lfs;
791 
792 	libdir = opendir(dir);
793 	if (libdir == NULL)
794 		return -1;
795 
796 	LIST_INIT(&lfs);
797 	for (;;) {
798 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
799 			break;
800 		if (!result)
801 			break;
802 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
803 		    strlen(RUMPFSMOD_PREFIX)) != 0)
804 			continue;
805 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
806 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
807 			continue;
808 		strlcpy(nbuf, dir, sizeof(nbuf));
809 		strlcat(nbuf, "/", sizeof(nbuf));
810 		strlcat(nbuf, result->d_name, sizeof(nbuf));
811 		switch (ukfs_modload(nbuf)) {
812 		case 0:
813 			lf = malloc(sizeof(*lf));
814 			if (lf == NULL) {
815 				error = ENOMEM;
816 				break;
817 			}
818 			lf->pname = strdup(nbuf);
819 			if (lf->pname == NULL) {
820 				free(lf);
821 				error = ENOMEM;
822 				break;
823 			}
824 			LIST_INSERT_HEAD(&lfs, lf, entries);
825 			break;
826 		case 1:
827 			nloaded++;
828 			break;
829 		default:
830 			/* ignore errors */
831 			break;
832 		}
833 	}
834 	closedir(libdir);
835 	if (error && nloaded != 0)
836 		error = 0;
837 
838 	/*
839 	 * El-cheapo dependency calculator.  Just try to load the
840 	 * modules n times in a loop
841 	 */
842 	for (redo = 1; redo;) {
843 		redo = 0;
844 		nlf = LIST_FIRST(&lfs);
845 		while ((lf = nlf) != NULL) {
846 			nlf = LIST_NEXT(lf, entries);
847 			if (ukfs_modload(lf->pname) == 1) {
848 				nloaded++;
849 				redo = 1;
850 				LIST_REMOVE(lf, entries);
851 				free(lf->pname);
852 				free(lf);
853 			}
854 		}
855 	}
856 
857 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
858 		LIST_REMOVE(lf, entries);
859 		free(lf->pname);
860 		free(lf);
861 	}
862 
863 	if (error && nloaded == 0) {
864 		errno = error;
865 		return -1;
866 	}
867 
868 	return nloaded;
869 }
870 
871 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
872 ssize_t
873 ukfs_vfstypes(char *buf, size_t buflen)
874 {
875 	int mib[3];
876 	struct sysctlnode q, ans[128];
877 	size_t alen;
878 	int i;
879 
880 	mib[0] = CTL_VFS;
881 	mib[1] = VFS_GENERIC;
882 	mib[2] = CTL_QUERY;
883 	alen = sizeof(ans);
884 
885 	memset(&q, 0, sizeof(q));
886 	q.sysctl_flags = SYSCTL_VERSION;
887 
888 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
889 		return -1;
890 	}
891 
892 	for (i = 0; i < alen/sizeof(ans[0]); i++)
893 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
894 			break;
895 	if (i == alen/sizeof(ans[0])) {
896 		errno = ENXIO;
897 		return -1;
898 	}
899 
900 	mib[0] = CTL_VFS;
901 	mib[1] = VFS_GENERIC;
902 	mib[2] = ans[i].sysctl_num;
903 
904 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
905 		return -1;
906 	}
907 
908 	return buflen;
909 }
910 
911 /*
912  * Utilities
913  */
914 static int
915 builddirs(const char *pathname, mode_t mode,
916 	int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs)
917 {
918 	char *f1, *f2;
919 	int rv;
920 	mode_t mask;
921 	bool end;
922 
923 	/*ukfs_umask((mask = ukfs_umask(0)));*/
924 	umask((mask = umask(0)));
925 
926 	f1 = f2 = strdup(pathname);
927 	if (f1 == NULL) {
928 		errno = ENOMEM;
929 		return -1;
930 	}
931 
932 	end = false;
933 	for (;;) {
934 		/* find next component */
935 		f2 += strspn(f2, "/");
936 		f2 += strcspn(f2, "/");
937 		if (*f2 == '\0')
938 			end = true;
939 		else
940 			*f2 = '\0';
941 
942 		rv = mkdirfn(fs, f1, mode & ~mask);
943 		if (errno == EEXIST)
944 			rv = 0;
945 
946 		if (rv == -1 || *f2 != '\0' || end)
947 			break;
948 
949 		*f2 = '/';
950 	}
951 
952 	free(f1);
953 
954 	return rv;
955 }
956 
957 int
958 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
959 {
960 
961 	return builddirs(pathname, mode, ukfs_mkdir, ukfs);
962 }
963