xref: /netbsd-src/lib/libukfs/ukfs.c (revision f75f5aae154fcd0572e8889e4fea2a51d67bbf08)
1 /*	$NetBSD: ukfs.c,v 1.39 2009/10/07 20:53:38 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008, 2009  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 #include "ukfs_int_disklabel.h"
67 
68 #define UKFS_MODE_DEFAULT 0555
69 
70 struct ukfs {
71 	struct mount *ukfs_mp;
72 	struct vnode *ukfs_rvp;
73 	void *ukfs_specific;
74 
75 	pthread_spinlock_t ukfs_spin;
76 	pid_t ukfs_nextpid;
77 	struct vnode *ukfs_cdir;
78 	int ukfs_devfd;
79 	char *ukfs_devpath;
80 	char *ukfs_mountpath;
81 };
82 
83 static int builddirs(const char *, mode_t,
84     int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *);
85 
86 struct mount *
87 ukfs_getmp(struct ukfs *ukfs)
88 {
89 
90 	return ukfs->ukfs_mp;
91 }
92 
93 struct vnode *
94 ukfs_getrvp(struct ukfs *ukfs)
95 {
96 	struct vnode *rvp;
97 
98 	rvp = ukfs->ukfs_rvp;
99 	rump_vp_incref(rvp);
100 
101 	return rvp;
102 }
103 
104 void
105 ukfs_setspecific(struct ukfs *ukfs, void *priv)
106 {
107 
108 	ukfs->ukfs_specific = priv;
109 }
110 
111 void *
112 ukfs_getspecific(struct ukfs *ukfs)
113 {
114 
115 	return ukfs->ukfs_specific;
116 }
117 
118 #ifdef DONT_WANT_PTHREAD_LINKAGE
119 #define pthread_spin_lock(a)
120 #define pthread_spin_unlock(a)
121 #define pthread_spin_init(a,b)
122 #define pthread_spin_destroy(a)
123 #endif
124 
125 static pid_t
126 nextpid(struct ukfs *ukfs)
127 {
128 	pid_t npid;
129 
130 	pthread_spin_lock(&ukfs->ukfs_spin);
131 	if (ukfs->ukfs_nextpid == 0)
132 		ukfs->ukfs_nextpid++;
133 	npid = ukfs->ukfs_nextpid++;
134 	pthread_spin_unlock(&ukfs->ukfs_spin);
135 
136 	return npid;
137 }
138 
139 static void
140 precall(struct ukfs *ukfs)
141 {
142 	struct vnode *rvp, *cvp;
143 
144 	rump_setup_curlwp(nextpid(ukfs), 1, 1);
145 	rvp = ukfs_getrvp(ukfs);
146 	pthread_spin_lock(&ukfs->ukfs_spin);
147 	cvp = ukfs->ukfs_cdir;
148 	pthread_spin_unlock(&ukfs->ukfs_spin);
149 	rump_rcvp_set(rvp, cvp); /* takes refs */
150 	rump_vp_rele(rvp);
151 }
152 
153 static void
154 postcall(struct ukfs *ukfs)
155 {
156 	struct vnode *rvp;
157 
158 	rvp = ukfs_getrvp(ukfs);
159 	rump_rcvp_set(NULL, rvp);
160 	rump_vp_rele(rvp);
161 	rump_clear_curlwp();
162 }
163 
164 int
165 _ukfs_init(int version)
166 {
167 	int rv;
168 
169 	if (version != UKFS_VERSION) {
170 		printf("incompatible ukfs version, %d vs. %d\n",
171 		    version, UKFS_VERSION);
172 		errno = EPROGMISMATCH;
173 		return -1;
174 	}
175 
176 	if ((rv = rump_init()) != 0) {
177 		errno = rv;
178 		return -1;
179 	}
180 
181 	return 0;
182 }
183 
184 /*ARGSUSED*/
185 static int
186 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode)
187 {
188 
189 	return rump_sys_mkdir(path, mode);
190 }
191 
192 int
193 ukfs_partition_probe(char *devpath, int *partition)
194 {
195 	char *p;
196 	int rv = 0;
197 
198 	/*
199 	 * Check for disklabel magic in pathname:
200 	 * /regularpath%PART:<char>%\0
201 	 */
202 #define MAGICADJ(p, n) (p+sizeof(UKFS_PARTITION_SCANMAGIC)-1+n)
203 	if ((p = strstr(devpath, UKFS_PARTITION_SCANMAGIC)) != NULL
204 	    && strlen(p) == UKFS_PARTITION_MAGICLEN
205 	    && *(MAGICADJ(p,1)) == '%') {
206 		if (*(MAGICADJ(p,0)) >= 'a' &&
207 		    *(MAGICADJ(p,0)) < 'a' + UKFS_MAXPARTITIONS) {
208 			*partition = *(MAGICADJ(p,0)) - 'a';
209 			*p = '\0';
210 		} else {
211 			rv = EINVAL;
212 		}
213 	} else {
214 		*partition = UKFS_PARTITION_NONE;
215 	}
216 
217 	return rv;
218 }
219 
220 /*
221  * Open the disk file and flock it.  Also, if we are operation on
222  * an embedded partition, find the partition offset and size from
223  * the disklabel.
224  *
225  * We hard-fail only in two cases:
226  *  1) we failed to get the partition info out (don't know what offset
227  *     to mount from)
228  *  2) we failed to flock the source device (i.e. flock() fails,
229  *     not e.g. open() before it)
230  *
231  * Otherwise we let the code proceed to mount and let the file system
232  * throw the proper error.  The only questionable bit is that if we
233  * soft-fail before flock() and mount does succeed...
234  *
235  * Returns: -1 error (errno reports error code)
236  *           0 success
237  *
238  * dfdp: -1  device is not open
239  *        n  device is open
240  */
241 static int
242 process_diskdevice(const char *devpath, int partition, int rdonly,
243 	int *dfdp, uint64_t *devoff, uint64_t *devsize)
244 {
245 	char buf[65536];
246 	struct stat sb;
247 	struct ukfs_disklabel dl;
248 	struct ukfs_partition *pp;
249 	int rv = 0, devfd;
250 
251 	/* defaults */
252 	*devoff = 0;
253 	*devsize = RUMP_ETFS_SIZE_ENDOFF;
254 	*dfdp = -1;
255 
256 	devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR);
257 	if (devfd == -1) {
258 		if (UKFS_USEPARTITION(partition))
259 			rv = errno;
260 		goto out;
261 	}
262 
263 	/*
264 	 * Locate the disklabel and find the partition in question.
265 	 */
266 	if (UKFS_USEPARTITION(partition)) {
267 		if (pread(devfd, buf, sizeof(buf), 0) == -1) {
268 			rv = errno;
269 			goto out;
270 		}
271 
272 		if (ukfs_disklabel_scan(&dl, buf, sizeof(buf)) != 0) {
273 			rv = ENOENT;
274 			goto out;
275 		}
276 
277 		if (dl.d_npartitions < partition) {
278 			rv = ENOENT;
279 			goto out;
280 		}
281 
282 		pp = &dl.d_partitions[partition];
283 		*devoff = pp->p_offset << DEV_BSHIFT;
284 		*devsize = pp->p_size << DEV_BSHIFT;
285 	}
286 
287 	if (fstat(devfd, &sb) == -1) {
288 		rv = errno;
289 		goto out;
290 	}
291 
292 	/*
293 	 * We do this only for non-block device since the
294 	 * (NetBSD) kernel allows block device open only once.
295 	 * We also need to close the device for fairly obvious reasons.
296 	 */
297 	if (!S_ISBLK(sb.st_mode)) {
298 		if (flock(devfd, LOCK_NB | (rdonly ? LOCK_SH:LOCK_EX)) == -1) {
299 			warnx("ukfs_mount: cannot get %s lock on "
300 			    "device", rdonly ? "shared" : "exclusive");
301 			rv = errno;
302 			goto out;
303 		}
304 	} else {
305 		close(devfd);
306 		devfd = -1;
307 	}
308 	*dfdp = devfd;
309 
310  out:
311 	if (rv) {
312 		if (devfd != -1)
313 			close(devfd);
314 		errno = rv;
315 		rv = -1;
316 	}
317 
318 	return rv;
319 }
320 
321 static struct ukfs *
322 doukfsmount(const char *vfsname, const char *devpath, int partition,
323 	const char *mountpath, int mntflags, void *arg, size_t alen)
324 {
325 	struct ukfs *fs = NULL;
326 	int rv = 0, devfd = -1;
327 	uint64_t devoff, devsize;
328 	int mounted = 0;
329 	int regged = 0;
330 
331 	/* XXX: gcc whine */
332 	devoff = 0;
333 	devsize = 0;
334 
335 	if (partition != UKFS_PARTITION_NA)
336 		process_diskdevice(devpath, partition, mntflags & MNT_RDONLY,
337 		    &devfd, &devoff, &devsize);
338 
339 	fs = malloc(sizeof(struct ukfs));
340 	if (fs == NULL) {
341 		rv = ENOMEM;
342 		goto out;
343 	}
344 	memset(fs, 0, sizeof(struct ukfs));
345 
346 	/* create our mountpoint.  this is never removed. */
347 	if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) {
348 		if (errno != EEXIST) {
349 			rv = errno;
350 			goto out;
351 		}
352 	}
353 
354 	if (partition != UKFS_PARTITION_NA) {
355 		rv = rump_etfs_register_withsize(devpath, devpath,
356 		    RUMP_ETFS_BLK, devoff, devsize);
357 		if (rv) {
358 			goto out;
359 		}
360 		regged = 1;
361 	}
362 
363 	rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen);
364 	if (rv) {
365 		rv = errno;
366 		goto out;
367 	}
368 	mounted = 1;
369 	rv = rump_vfs_getmp(mountpath, &fs->ukfs_mp);
370 	if (rv) {
371 		goto out;
372 	}
373 	rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
374 	if (rv) {
375 		goto out;
376 	}
377 
378 	if (regged) {
379 		fs->ukfs_devpath = strdup(devpath);
380 	}
381 	fs->ukfs_mountpath = strdup(mountpath);
382 	fs->ukfs_cdir = ukfs_getrvp(fs);
383 	pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
384 	fs->ukfs_devfd = devfd;
385 	assert(rv == 0);
386 
387  out:
388 	if (rv) {
389 		if (fs) {
390 			if (fs->ukfs_rvp)
391 				rump_vp_rele(fs->ukfs_rvp);
392 			free(fs);
393 			fs = NULL;
394 		}
395 		if (mounted)
396 			rump_sys_unmount(mountpath, MNT_FORCE);
397 		if (regged)
398 			rump_etfs_remove(devpath);
399 		if (devfd != -1) {
400 			flock(devfd, LOCK_UN);
401 			close(devfd);
402 		}
403 		errno = rv;
404 	}
405 
406 	return fs;
407 }
408 
409 struct ukfs *
410 ukfs_mount(const char *vfsname, const char *devpath,
411 	const char *mountpath, int mntflags, void *arg, size_t alen)
412 {
413 
414 	return doukfsmount(vfsname, devpath, UKFS_PARTITION_NA,
415 	    mountpath, mntflags, arg, alen);
416 }
417 
418 struct ukfs *
419 ukfs_mount_disk(const char *vfsname, const char *devpath, int partition,
420 	const char *mountpath, int mntflags, void *arg, size_t alen)
421 {
422 
423 	return doukfsmount(vfsname, devpath, partition,
424 	    mountpath, mntflags, arg, alen);
425 }
426 
427 int
428 ukfs_release(struct ukfs *fs, int flags)
429 {
430 
431 	if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
432 		int rv, mntflag, error;
433 
434 		ukfs_chdir(fs, "/");
435 		mntflag = 0;
436 		if (flags & UKFS_RELFLAG_FORCE)
437 			mntflag = MNT_FORCE;
438 		rump_setup_curlwp(nextpid(fs), 1, 1);
439 		rump_vp_rele(fs->ukfs_rvp);
440 		fs->ukfs_rvp = NULL;
441 		rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag);
442 		if (rv == -1) {
443 			error = errno;
444 			rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
445 			rump_clear_curlwp();
446 			ukfs_chdir(fs, fs->ukfs_mountpath);
447 			errno = error;
448 			return -1;
449 		}
450 		rump_clear_curlwp();
451 	}
452 
453 	if (fs->ukfs_devpath) {
454 		rump_etfs_remove(fs->ukfs_devpath);
455 		free(fs->ukfs_devpath);
456 	}
457 	free(fs->ukfs_mountpath);
458 
459 	pthread_spin_destroy(&fs->ukfs_spin);
460 	if (fs->ukfs_devfd != -1) {
461 		flock(fs->ukfs_devfd, LOCK_UN);
462 		close(fs->ukfs_devfd);
463 	}
464 	free(fs);
465 
466 	return 0;
467 }
468 
469 #define STDCALL(ukfs, thecall)						\
470 	int rv = 0;							\
471 									\
472 	precall(ukfs);							\
473 	rv = thecall;							\
474 	postcall(ukfs);							\
475 	return rv;
476 
477 int
478 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c)
479 {
480 	struct vnode *vp;
481 	int rv;
482 
483 	precall(ukfs);
484 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
485 	    NULL, &vp, NULL);
486 	postcall(ukfs);
487 
488 	if (rv == 0) {
489 		RUMP_VOP_UNLOCK(vp, 0);
490 	} else {
491 		errno = rv;
492 		rv = -1;
493 	}
494 
495 	/*LINTED*/
496 	*c = (struct ukfs_dircookie *)vp;
497 	return rv;
498 }
499 
500 static int
501 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize)
502 {
503 	struct uio *uio;
504 	size_t resid;
505 	int rv, eofflag;
506 	kauth_cred_t cred;
507 
508 	uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
509 	cred = rump_cred_suserget();
510 	rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
511 	rump_cred_suserput(cred);
512 	RUMP_VOP_UNLOCK(vp, 0);
513 	*off = rump_uio_getoff(uio);
514 	resid = rump_uio_free(uio);
515 
516 	if (rv) {
517 		errno = rv;
518 		return -1;
519 	}
520 
521 	/* LINTED: not totally correct return type, but follows syscall */
522 	return bufsize - resid;
523 }
524 
525 /*ARGSUSED*/
526 int
527 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off,
528 	uint8_t *buf, size_t bufsize)
529 {
530 	/*LINTED*/
531 	struct vnode *vp = (struct vnode *)c;
532 
533 	RUMP_VOP_LOCK(vp, RUMP_LK_SHARED);
534 	return getmydents(vp, off, buf, bufsize);
535 }
536 
537 int
538 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
539 	uint8_t *buf, size_t bufsize)
540 {
541 	struct vnode *vp;
542 	int rv;
543 
544 	precall(ukfs);
545 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
546 	    NULL, &vp, NULL);
547 	postcall(ukfs);
548 	if (rv) {
549 		errno = rv;
550 		return -1;
551 	}
552 
553 	rv = getmydents(vp, off, buf, bufsize);
554 	rump_vp_rele(vp);
555 	return rv;
556 }
557 
558 /*ARGSUSED*/
559 int
560 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c)
561 {
562 
563 	/*LINTED*/
564 	rump_vp_rele((struct vnode *)c);
565 	return 0;
566 }
567 
568 int
569 ukfs_open(struct ukfs *ukfs, const char *filename, int flags)
570 {
571 	int fd;
572 
573 	precall(ukfs);
574 	fd = rump_sys_open(filename, flags, 0);
575 	postcall(ukfs);
576 	if (fd == -1)
577 		return -1;
578 
579 	return fd;
580 }
581 
582 ssize_t
583 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
584 	uint8_t *buf, size_t bufsize)
585 {
586 	int fd;
587 	ssize_t xfer = -1; /* XXXgcc */
588 
589 	precall(ukfs);
590 	fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
591 	if (fd == -1)
592 		goto out;
593 
594 	xfer = rump_sys_pread(fd, buf, bufsize, off);
595 	rump_sys_close(fd);
596 
597  out:
598 	postcall(ukfs);
599 	if (fd == -1) {
600 		return -1;
601 	}
602 	return xfer;
603 }
604 
605 /*ARGSUSED*/
606 ssize_t
607 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen)
608 {
609 
610 	return rump_sys_pread(fd, buf, buflen, off);
611 }
612 
613 ssize_t
614 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
615 	uint8_t *buf, size_t bufsize)
616 {
617 	int fd;
618 	ssize_t xfer = -1; /* XXXgcc */
619 
620 	precall(ukfs);
621 	fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
622 	if (fd == -1)
623 		goto out;
624 
625 	/* write and commit */
626 	xfer = rump_sys_pwrite(fd, buf, bufsize, off);
627 	if (xfer > 0)
628 		rump_sys_fsync(fd);
629 
630 	rump_sys_close(fd);
631 
632  out:
633 	postcall(ukfs);
634 	if (fd == -1) {
635 		return -1;
636 	}
637 	return xfer;
638 }
639 
640 /*ARGSUSED*/
641 ssize_t
642 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen,
643 	int dosync)
644 {
645 	ssize_t xfer;
646 
647 	xfer = rump_sys_pwrite(fd, buf, buflen, off);
648 	if (xfer > 0 && dosync)
649 		rump_sys_fsync(fd);
650 
651 	return xfer;
652 }
653 
654 /*ARGSUSED*/
655 int
656 ukfs_close(struct ukfs *ukfs, int fd)
657 {
658 
659 	rump_sys_close(fd);
660 	return 0;
661 }
662 
663 int
664 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
665 {
666 	int fd;
667 
668 	precall(ukfs);
669 	fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
670 	if (fd == -1)
671 		return -1;
672 	rump_sys_close(fd);
673 
674 	postcall(ukfs);
675 	return 0;
676 }
677 
678 int
679 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
680 {
681 
682 	STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
683 }
684 
685 int
686 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
687 {
688 
689 	STDCALL(ukfs, rump_sys_mkfifo(path, mode));
690 }
691 
692 int
693 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
694 {
695 
696 	STDCALL(ukfs, rump_sys_mkdir(filename, mode));
697 }
698 
699 int
700 ukfs_remove(struct ukfs *ukfs, const char *filename)
701 {
702 
703 	STDCALL(ukfs, rump_sys_unlink(filename));
704 }
705 
706 int
707 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
708 {
709 
710 	STDCALL(ukfs, rump_sys_rmdir(filename));
711 }
712 
713 int
714 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
715 {
716 
717 	STDCALL(ukfs, rump_sys_link(filename, f_create));
718 }
719 
720 int
721 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
722 {
723 
724 	STDCALL(ukfs, rump_sys_symlink(filename, linkname));
725 }
726 
727 ssize_t
728 ukfs_readlink(struct ukfs *ukfs, const char *filename,
729 	char *linkbuf, size_t buflen)
730 {
731 	ssize_t rv;
732 
733 	precall(ukfs);
734 	rv = rump_sys_readlink(filename, linkbuf, buflen);
735 	postcall(ukfs);
736 	return rv;
737 }
738 
739 int
740 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
741 {
742 
743 	STDCALL(ukfs, rump_sys_rename(from, to));
744 }
745 
746 int
747 ukfs_chdir(struct ukfs *ukfs, const char *path)
748 {
749 	struct vnode *newvp, *oldvp;
750 	int rv;
751 
752 	precall(ukfs);
753 	rv = rump_sys_chdir(path);
754 	if (rv == -1)
755 		goto out;
756 
757 	newvp = rump_cdir_get();
758 	pthread_spin_lock(&ukfs->ukfs_spin);
759 	oldvp = ukfs->ukfs_cdir;
760 	ukfs->ukfs_cdir = newvp;
761 	pthread_spin_unlock(&ukfs->ukfs_spin);
762 	if (oldvp)
763 		rump_vp_rele(oldvp);
764 
765  out:
766 	postcall(ukfs);
767 	return rv;
768 }
769 
770 /*
771  * If we want to use post-time_t file systems on pre-time_t hosts,
772  * we must translate the stat structure.  Since we don't currently
773  * have a general method for making compat calls in rump, special-case
774  * this one.
775  *
776  * Note that this does not allow making system calls to older rump
777  * kernels from newer hosts.
778  */
779 #define VERS_TIMECHANGE 599000700
780 
781 static int
782 needcompat(void)
783 {
784 
785 #ifdef __NetBSD__
786 	/*LINTED*/
787 	return __NetBSD_Version__ < VERS_TIMECHANGE
788 	    && rump_getversion() >= VERS_TIMECHANGE;
789 #else
790 	return 0;
791 #endif
792 }
793 
794 int
795 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
796 {
797 	int rv;
798 
799 	precall(ukfs);
800 	if (needcompat())
801 		rv = rump_sys___stat30(filename, file_stat);
802 	else
803 		rv = rump_sys_stat(filename, file_stat);
804 	postcall(ukfs);
805 
806 	return rv;
807 }
808 
809 int
810 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
811 {
812 	int rv;
813 
814 	precall(ukfs);
815 	if (needcompat())
816 		rv = rump_sys___lstat30(filename, file_stat);
817 	else
818 		rv = rump_sys_lstat(filename, file_stat);
819 	postcall(ukfs);
820 
821 	return rv;
822 }
823 
824 int
825 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
826 {
827 
828 	STDCALL(ukfs, rump_sys_chmod(filename, mode));
829 }
830 
831 int
832 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
833 {
834 
835 	STDCALL(ukfs, rump_sys_lchmod(filename, mode));
836 }
837 
838 int
839 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
840 {
841 
842 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
843 }
844 
845 int
846 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
847 {
848 
849 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
850 }
851 
852 int
853 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
854 {
855 
856 	STDCALL(ukfs, rump_sys_chflags(filename, flags));
857 }
858 
859 int
860 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
861 {
862 
863 	STDCALL(ukfs, rump_sys_lchflags(filename, flags));
864 }
865 
866 int
867 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
868 {
869 
870 	STDCALL(ukfs, rump_sys_utimes(filename, tptr));
871 }
872 
873 int
874 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
875 	      const struct timeval *tptr)
876 {
877 
878 	STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
879 }
880 
881 /*
882  * Dynamic module support
883  */
884 
885 /* load one library */
886 
887 /*
888  * XXX: the dlerror stuff isn't really threadsafe, but then again I
889  * can't protect against other threads calling dl*() outside of ukfs,
890  * so just live with it being flimsy
891  */
892 int
893 ukfs_modload(const char *fname)
894 {
895 	void *handle;
896 	struct modinfo **mi;
897 	int error;
898 
899 	handle = dlopen(fname, RTLD_GLOBAL);
900 	if (handle == NULL) {
901 		const char *dlmsg = dlerror();
902 		if (strstr(dlmsg, "Undefined symbol"))
903 			return 0;
904 		warnx("dlopen %s failed: %s\n", fname, dlmsg);
905 		/* XXXerrno */
906 		return -1;
907 	}
908 
909 	mi = dlsym(handle, "__start_link_set_modules");
910 	if (mi) {
911 		error = rump_module_init(*mi, NULL);
912 		if (error)
913 			goto errclose;
914 		return 1;
915 	}
916 	error = EINVAL;
917 
918  errclose:
919 	dlclose(handle);
920 	errno = error;
921 	return -1;
922 }
923 
924 struct loadfail {
925 	char *pname;
926 
927 	LIST_ENTRY(loadfail) entries;
928 };
929 
930 #define RUMPFSMOD_PREFIX "librumpfs_"
931 #define RUMPFSMOD_SUFFIX ".so"
932 
933 int
934 ukfs_modload_dir(const char *dir)
935 {
936 	char nbuf[MAXPATHLEN+1], *p;
937 	struct dirent entry, *result;
938 	DIR *libdir;
939 	struct loadfail *lf, *nlf;
940 	int error, nloaded = 0, redo;
941 	LIST_HEAD(, loadfail) lfs;
942 
943 	libdir = opendir(dir);
944 	if (libdir == NULL)
945 		return -1;
946 
947 	LIST_INIT(&lfs);
948 	for (;;) {
949 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
950 			break;
951 		if (!result)
952 			break;
953 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
954 		    strlen(RUMPFSMOD_PREFIX)) != 0)
955 			continue;
956 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
957 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
958 			continue;
959 		strlcpy(nbuf, dir, sizeof(nbuf));
960 		strlcat(nbuf, "/", sizeof(nbuf));
961 		strlcat(nbuf, result->d_name, sizeof(nbuf));
962 		switch (ukfs_modload(nbuf)) {
963 		case 0:
964 			lf = malloc(sizeof(*lf));
965 			if (lf == NULL) {
966 				error = ENOMEM;
967 				break;
968 			}
969 			lf->pname = strdup(nbuf);
970 			if (lf->pname == NULL) {
971 				free(lf);
972 				error = ENOMEM;
973 				break;
974 			}
975 			LIST_INSERT_HEAD(&lfs, lf, entries);
976 			break;
977 		case 1:
978 			nloaded++;
979 			break;
980 		default:
981 			/* ignore errors */
982 			break;
983 		}
984 	}
985 	closedir(libdir);
986 	if (error && nloaded != 0)
987 		error = 0;
988 
989 	/*
990 	 * El-cheapo dependency calculator.  Just try to load the
991 	 * modules n times in a loop
992 	 */
993 	for (redo = 1; redo;) {
994 		redo = 0;
995 		nlf = LIST_FIRST(&lfs);
996 		while ((lf = nlf) != NULL) {
997 			nlf = LIST_NEXT(lf, entries);
998 			if (ukfs_modload(lf->pname) == 1) {
999 				nloaded++;
1000 				redo = 1;
1001 				LIST_REMOVE(lf, entries);
1002 				free(lf->pname);
1003 				free(lf);
1004 			}
1005 		}
1006 	}
1007 
1008 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
1009 		LIST_REMOVE(lf, entries);
1010 		free(lf->pname);
1011 		free(lf);
1012 	}
1013 
1014 	if (error && nloaded == 0) {
1015 		errno = error;
1016 		return -1;
1017 	}
1018 
1019 	return nloaded;
1020 }
1021 
1022 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
1023 ssize_t
1024 ukfs_vfstypes(char *buf, size_t buflen)
1025 {
1026 	int mib[3];
1027 	struct sysctlnode q, ans[128];
1028 	size_t alen;
1029 	int i;
1030 
1031 	mib[0] = CTL_VFS;
1032 	mib[1] = VFS_GENERIC;
1033 	mib[2] = CTL_QUERY;
1034 	alen = sizeof(ans);
1035 
1036 	memset(&q, 0, sizeof(q));
1037 	q.sysctl_flags = SYSCTL_VERSION;
1038 
1039 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
1040 		return -1;
1041 	}
1042 
1043 	for (i = 0; i < alen/sizeof(ans[0]); i++)
1044 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
1045 			break;
1046 	if (i == alen/sizeof(ans[0])) {
1047 		errno = ENXIO;
1048 		return -1;
1049 	}
1050 
1051 	mib[0] = CTL_VFS;
1052 	mib[1] = VFS_GENERIC;
1053 	mib[2] = ans[i].sysctl_num;
1054 
1055 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
1056 		return -1;
1057 	}
1058 
1059 	return buflen;
1060 }
1061 
1062 /*
1063  * Utilities
1064  */
1065 static int
1066 builddirs(const char *pathname, mode_t mode,
1067 	int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs)
1068 {
1069 	char *f1, *f2;
1070 	int rv;
1071 	mode_t mask;
1072 	bool end;
1073 
1074 	/*ukfs_umask((mask = ukfs_umask(0)));*/
1075 	umask((mask = umask(0)));
1076 
1077 	f1 = f2 = strdup(pathname);
1078 	if (f1 == NULL) {
1079 		errno = ENOMEM;
1080 		return -1;
1081 	}
1082 
1083 	end = false;
1084 	for (;;) {
1085 		/* find next component */
1086 		f2 += strspn(f2, "/");
1087 		f2 += strcspn(f2, "/");
1088 		if (*f2 == '\0')
1089 			end = true;
1090 		else
1091 			*f2 = '\0';
1092 
1093 		rv = mkdirfn(fs, f1, mode & ~mask);
1094 		if (errno == EEXIST)
1095 			rv = 0;
1096 
1097 		if (rv == -1 || *f2 != '\0' || end)
1098 			break;
1099 
1100 		*f2 = '/';
1101 	}
1102 
1103 	free(f1);
1104 
1105 	return rv;
1106 }
1107 
1108 int
1109 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
1110 {
1111 
1112 	return builddirs(pathname, mode, ukfs_mkdir, ukfs);
1113 }
1114