xref: /netbsd-src/lib/libukfs/ukfs.c (revision 9ee9e0d7de4c59c936a17df52be682915dc66f43)
1 /*	$NetBSD: ukfs.c,v 1.26 2009/05/02 15:20:08 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, 0, 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, 0, 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, 0, 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, 0, 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 int
594 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
595 {
596 
597 	STDCALL(ukfs, rump_sys_stat(filename, file_stat));
598 }
599 
600 int
601 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
602 {
603 
604 	STDCALL(ukfs, rump_sys_lstat(filename, file_stat));
605 }
606 
607 int
608 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
609 {
610 
611 	STDCALL(ukfs, rump_sys_chmod(filename, mode));
612 }
613 
614 int
615 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
616 {
617 
618 	STDCALL(ukfs, rump_sys_lchmod(filename, mode));
619 }
620 
621 int
622 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
623 {
624 
625 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
626 }
627 
628 int
629 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
630 {
631 
632 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
633 }
634 
635 int
636 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
637 {
638 
639 	STDCALL(ukfs, rump_sys_chflags(filename, flags));
640 }
641 
642 int
643 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
644 {
645 
646 	STDCALL(ukfs, rump_sys_lchflags(filename, flags));
647 }
648 
649 int
650 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
651 {
652 
653 	STDCALL(ukfs, rump_sys_utimes(filename, tptr));
654 }
655 
656 int
657 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
658 	      const struct timeval *tptr)
659 {
660 
661 	STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
662 }
663 
664 /*
665  * Dynamic module support
666  */
667 
668 /* load one library */
669 
670 /*
671  * XXX: the dlerror stuff isn't really threadsafe, but then again I
672  * can't protect against other threads calling dl*() outside of ukfs,
673  * so just live with it being flimsy
674  */
675 int
676 ukfs_modload(const char *fname)
677 {
678 	void *handle;
679 	struct modinfo **mi;
680 	struct stat sb;
681 	int error;
682 
683 	if (stat(fname, &sb) == -1)
684 		return -1;
685 
686 	handle = dlopen(fname, RTLD_GLOBAL);
687 	if (handle == NULL) {
688 		const char *dlmsg = dlerror();
689 		if (strstr(dlmsg, "Undefined symbol"))
690 			return 0;
691 		warnx("dlopen %s failed: %s\n", fname, dlmsg);
692 		/* XXXerrno */
693 		return -1;
694 	}
695 
696 	mi = dlsym(handle, "__start_link_set_modules");
697 	if (mi) {
698 		error = rump_module_init(*mi, NULL);
699 		if (error)
700 			goto errclose;
701 		return 1;
702 	}
703 	error = EINVAL;
704 
705  errclose:
706 	dlclose(handle);
707 	errno = error;
708 	return -1;
709 }
710 
711 struct loadfail {
712 	char *pname;
713 
714 	LIST_ENTRY(loadfail) entries;
715 };
716 
717 #define RUMPFSMOD_PREFIX "librumpfs_"
718 #define RUMPFSMOD_SUFFIX ".so"
719 
720 int
721 ukfs_modload_dir(const char *dir)
722 {
723 	char nbuf[MAXPATHLEN+1], *p;
724 	struct dirent entry, *result;
725 	DIR *libdir;
726 	struct loadfail *lf, *nlf;
727 	int error, nloaded = 0, redo;
728 	LIST_HEAD(, loadfail) lfs;
729 
730 	libdir = opendir(dir);
731 	if (libdir == NULL)
732 		return -1;
733 
734 	LIST_INIT(&lfs);
735 	for (;;) {
736 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
737 			break;
738 		if (!result)
739 			break;
740 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
741 		    strlen(RUMPFSMOD_PREFIX)) != 0)
742 			continue;
743 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
744 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
745 			continue;
746 		strlcpy(nbuf, dir, sizeof(nbuf));
747 		strlcat(nbuf, "/", sizeof(nbuf));
748 		strlcat(nbuf, result->d_name, sizeof(nbuf));
749 		switch (ukfs_modload(nbuf)) {
750 		case 0:
751 			lf = malloc(sizeof(*lf));
752 			if (lf == NULL) {
753 				error = ENOMEM;
754 				break;
755 			}
756 			lf->pname = strdup(nbuf);
757 			if (lf->pname == NULL) {
758 				free(lf);
759 				error = ENOMEM;
760 				break;
761 			}
762 			LIST_INSERT_HEAD(&lfs, lf, entries);
763 			break;
764 		case 1:
765 			nloaded++;
766 			break;
767 		default:
768 			/* ignore errors */
769 			break;
770 		}
771 	}
772 	closedir(libdir);
773 	if (error && nloaded != 0)
774 		error = 0;
775 
776 	/*
777 	 * El-cheapo dependency calculator.  Just try to load the
778 	 * modules n times in a loop
779 	 */
780 	for (redo = 1; redo;) {
781 		redo = 0;
782 		nlf = LIST_FIRST(&lfs);
783 		while ((lf = nlf) != NULL) {
784 			nlf = LIST_NEXT(lf, entries);
785 			if (ukfs_modload(lf->pname) == 1) {
786 				nloaded++;
787 				redo = 1;
788 				LIST_REMOVE(lf, entries);
789 				free(lf->pname);
790 				free(lf);
791 			}
792 		}
793 	}
794 
795 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
796 		LIST_REMOVE(lf, entries);
797 		free(lf->pname);
798 		free(lf);
799 	}
800 
801 	if (error && nloaded == 0) {
802 		errno = error;
803 		return -1;
804 	}
805 
806 	return nloaded;
807 }
808 
809 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
810 ssize_t
811 ukfs_vfstypes(char *buf, size_t buflen)
812 {
813 	int mib[3];
814 	struct sysctlnode q, ans[128];
815 	size_t alen;
816 	int i;
817 
818 	mib[0] = CTL_VFS;
819 	mib[1] = VFS_GENERIC;
820 	mib[2] = CTL_QUERY;
821 	alen = sizeof(ans);
822 
823 	memset(&q, 0, sizeof(q));
824 	q.sysctl_flags = SYSCTL_VERSION;
825 
826 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
827 		return -1;
828 	}
829 
830 	for (i = 0; i < alen/sizeof(ans[0]); i++)
831 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
832 			break;
833 	if (i == alen/sizeof(ans[0])) {
834 		errno = ENXIO;
835 		return -1;
836 	}
837 
838 	mib[0] = CTL_VFS;
839 	mib[1] = VFS_GENERIC;
840 	mib[2] = ans[i].sysctl_num;
841 
842 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
843 		return -1;
844 	}
845 
846 	return buflen;
847 }
848 
849 /*
850  * Utilities
851  */
852 int
853 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
854 {
855 	char *f1, *f2;
856 	int rv;
857 	mode_t mask;
858 	bool end;
859 
860 	/*ukfs_umask((mask = ukfs_umask(0)));*/
861 	umask((mask = umask(0)));
862 
863 	f1 = f2 = strdup(pathname);
864 	if (f1 == NULL) {
865 		errno = ENOMEM;
866 		return -1;
867 	}
868 
869 	end = false;
870 	for (;;) {
871 		/* find next component */
872 		f2 += strspn(f2, "/");
873 		f2 += strcspn(f2, "/");
874 		if (*f2 == '\0')
875 			end = true;
876 		else
877 			*f2 = '\0';
878 
879 		rv = ukfs_mkdir(ukfs, f1, mode & ~mask);
880 		if (errno == EEXIST)
881 			rv = 0;
882 
883 		if (rv == -1 || *f2 != '\0' || end)
884 			break;
885 
886 		*f2 = '/';
887 	}
888 
889 	free(f1);
890 
891 	return rv;
892 }
893