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