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