Home | History | Annotate | Line # | Download | only in libukfs
ukfs.c revision 1.38
      1 /*	$NetBSD: ukfs.c,v 1.38 2009/10/07 20:51:00 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;
    327 	uint64_t devoff, devsize;
    328 	int mounted = 0;
    329 	int regged = 0;
    330 
    331 	if (partition != UKFS_PARTITION_NA)
    332 		process_diskdevice(devpath, partition, mntflags & MNT_RDONLY,
    333 		    &devfd, &devoff, &devsize);
    334 
    335 	fs = malloc(sizeof(struct ukfs));
    336 	if (fs == NULL) {
    337 		rv = ENOMEM;
    338 		goto out;
    339 	}
    340 	memset(fs, 0, sizeof(struct ukfs));
    341 
    342 	/* create our mountpoint.  this is never removed. */
    343 	if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) {
    344 		if (errno != EEXIST) {
    345 			rv = errno;
    346 			goto out;
    347 		}
    348 	}
    349 
    350 	if (partition != UKFS_PARTITION_NA) {
    351 		rv = rump_etfs_register_withsize(devpath, devpath,
    352 		    RUMP_ETFS_BLK, devoff, devsize);
    353 		if (rv) {
    354 			goto out;
    355 		}
    356 		regged = 1;
    357 	}
    358 
    359 	rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen);
    360 	if (rv) {
    361 		rv = errno;
    362 		goto out;
    363 	}
    364 	mounted = 1;
    365 	rv = rump_vfs_getmp(mountpath, &fs->ukfs_mp);
    366 	if (rv) {
    367 		goto out;
    368 	}
    369 	rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
    370 	if (rv) {
    371 		goto out;
    372 	}
    373 
    374 	if (regged) {
    375 		fs->ukfs_devpath = strdup(devpath);
    376 	}
    377 	fs->ukfs_mountpath = strdup(mountpath);
    378 	fs->ukfs_cdir = ukfs_getrvp(fs);
    379 	pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
    380 	fs->ukfs_devfd = devfd;
    381 	assert(rv == 0);
    382 
    383  out:
    384 	if (rv) {
    385 		if (fs) {
    386 			if (fs->ukfs_rvp)
    387 				rump_vp_rele(fs->ukfs_rvp);
    388 			free(fs);
    389 			fs = NULL;
    390 		}
    391 		if (mounted)
    392 			rump_sys_unmount(mountpath, MNT_FORCE);
    393 		if (regged)
    394 			rump_etfs_remove(devpath);
    395 		if (devfd != -1) {
    396 			flock(devfd, LOCK_UN);
    397 			close(devfd);
    398 		}
    399 		errno = rv;
    400 	}
    401 
    402 	return fs;
    403 }
    404 
    405 struct ukfs *
    406 ukfs_mount(const char *vfsname, const char *devpath,
    407 	const char *mountpath, int mntflags, void *arg, size_t alen)
    408 {
    409 
    410 	return doukfsmount(vfsname, devpath, UKFS_PARTITION_NA,
    411 	    mountpath, mntflags, arg, alen);
    412 }
    413 
    414 struct ukfs *
    415 ukfs_mount_disk(const char *vfsname, const char *devpath, int partition,
    416 	const char *mountpath, int mntflags, void *arg, size_t alen)
    417 {
    418 
    419 	return doukfsmount(vfsname, devpath, partition,
    420 	    mountpath, mntflags, arg, alen);
    421 }
    422 
    423 int
    424 ukfs_release(struct ukfs *fs, int flags)
    425 {
    426 
    427 	if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
    428 		int rv, mntflag, error;
    429 
    430 		ukfs_chdir(fs, "/");
    431 		mntflag = 0;
    432 		if (flags & UKFS_RELFLAG_FORCE)
    433 			mntflag = MNT_FORCE;
    434 		rump_setup_curlwp(nextpid(fs), 1, 1);
    435 		rump_vp_rele(fs->ukfs_rvp);
    436 		fs->ukfs_rvp = NULL;
    437 		rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag);
    438 		if (rv == -1) {
    439 			error = errno;
    440 			rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
    441 			rump_clear_curlwp();
    442 			ukfs_chdir(fs, fs->ukfs_mountpath);
    443 			errno = error;
    444 			return -1;
    445 		}
    446 		rump_clear_curlwp();
    447 	}
    448 
    449 	if (fs->ukfs_devpath) {
    450 		rump_etfs_remove(fs->ukfs_devpath);
    451 		free(fs->ukfs_devpath);
    452 	}
    453 	free(fs->ukfs_mountpath);
    454 
    455 	pthread_spin_destroy(&fs->ukfs_spin);
    456 	if (fs->ukfs_devfd != -1) {
    457 		flock(fs->ukfs_devfd, LOCK_UN);
    458 		close(fs->ukfs_devfd);
    459 	}
    460 	free(fs);
    461 
    462 	return 0;
    463 }
    464 
    465 #define STDCALL(ukfs, thecall)						\
    466 	int rv = 0;							\
    467 									\
    468 	precall(ukfs);							\
    469 	rv = thecall;							\
    470 	postcall(ukfs);							\
    471 	return rv;
    472 
    473 int
    474 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c)
    475 {
    476 	struct vnode *vp;
    477 	int rv;
    478 
    479 	precall(ukfs);
    480 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
    481 	    NULL, &vp, NULL);
    482 	postcall(ukfs);
    483 
    484 	if (rv == 0) {
    485 		RUMP_VOP_UNLOCK(vp, 0);
    486 	} else {
    487 		errno = rv;
    488 		rv = -1;
    489 	}
    490 
    491 	/*LINTED*/
    492 	*c = (struct ukfs_dircookie *)vp;
    493 	return rv;
    494 }
    495 
    496 static int
    497 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize)
    498 {
    499 	struct uio *uio;
    500 	size_t resid;
    501 	int rv, eofflag;
    502 	kauth_cred_t cred;
    503 
    504 	uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
    505 	cred = rump_cred_suserget();
    506 	rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
    507 	rump_cred_suserput(cred);
    508 	RUMP_VOP_UNLOCK(vp, 0);
    509 	*off = rump_uio_getoff(uio);
    510 	resid = rump_uio_free(uio);
    511 
    512 	if (rv) {
    513 		errno = rv;
    514 		return -1;
    515 	}
    516 
    517 	/* LINTED: not totally correct return type, but follows syscall */
    518 	return bufsize - resid;
    519 }
    520 
    521 /*ARGSUSED*/
    522 int
    523 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off,
    524 	uint8_t *buf, size_t bufsize)
    525 {
    526 	/*LINTED*/
    527 	struct vnode *vp = (struct vnode *)c;
    528 
    529 	RUMP_VOP_LOCK(vp, RUMP_LK_SHARED);
    530 	return getmydents(vp, off, buf, bufsize);
    531 }
    532 
    533 int
    534 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
    535 	uint8_t *buf, size_t bufsize)
    536 {
    537 	struct vnode *vp;
    538 	int rv;
    539 
    540 	precall(ukfs);
    541 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
    542 	    NULL, &vp, NULL);
    543 	postcall(ukfs);
    544 	if (rv) {
    545 		errno = rv;
    546 		return -1;
    547 	}
    548 
    549 	rv = getmydents(vp, off, buf, bufsize);
    550 	rump_vp_rele(vp);
    551 	return rv;
    552 }
    553 
    554 /*ARGSUSED*/
    555 int
    556 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c)
    557 {
    558 
    559 	/*LINTED*/
    560 	rump_vp_rele((struct vnode *)c);
    561 	return 0;
    562 }
    563 
    564 int
    565 ukfs_open(struct ukfs *ukfs, const char *filename, int flags)
    566 {
    567 	int fd;
    568 
    569 	precall(ukfs);
    570 	fd = rump_sys_open(filename, flags, 0);
    571 	postcall(ukfs);
    572 	if (fd == -1)
    573 		return -1;
    574 
    575 	return fd;
    576 }
    577 
    578 ssize_t
    579 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
    580 	uint8_t *buf, size_t bufsize)
    581 {
    582 	int fd;
    583 	ssize_t xfer = -1; /* XXXgcc */
    584 
    585 	precall(ukfs);
    586 	fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
    587 	if (fd == -1)
    588 		goto out;
    589 
    590 	xfer = rump_sys_pread(fd, buf, bufsize, off);
    591 	rump_sys_close(fd);
    592 
    593  out:
    594 	postcall(ukfs);
    595 	if (fd == -1) {
    596 		return -1;
    597 	}
    598 	return xfer;
    599 }
    600 
    601 /*ARGSUSED*/
    602 ssize_t
    603 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen)
    604 {
    605 
    606 	return rump_sys_pread(fd, buf, buflen, off);
    607 }
    608 
    609 ssize_t
    610 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
    611 	uint8_t *buf, size_t bufsize)
    612 {
    613 	int fd;
    614 	ssize_t xfer = -1; /* XXXgcc */
    615 
    616 	precall(ukfs);
    617 	fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
    618 	if (fd == -1)
    619 		goto out;
    620 
    621 	/* write and commit */
    622 	xfer = rump_sys_pwrite(fd, buf, bufsize, off);
    623 	if (xfer > 0)
    624 		rump_sys_fsync(fd);
    625 
    626 	rump_sys_close(fd);
    627 
    628  out:
    629 	postcall(ukfs);
    630 	if (fd == -1) {
    631 		return -1;
    632 	}
    633 	return xfer;
    634 }
    635 
    636 /*ARGSUSED*/
    637 ssize_t
    638 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen,
    639 	int dosync)
    640 {
    641 	ssize_t xfer;
    642 
    643 	xfer = rump_sys_pwrite(fd, buf, buflen, off);
    644 	if (xfer > 0 && dosync)
    645 		rump_sys_fsync(fd);
    646 
    647 	return xfer;
    648 }
    649 
    650 /*ARGSUSED*/
    651 int
    652 ukfs_close(struct ukfs *ukfs, int fd)
    653 {
    654 
    655 	rump_sys_close(fd);
    656 	return 0;
    657 }
    658 
    659 int
    660 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
    661 {
    662 	int fd;
    663 
    664 	precall(ukfs);
    665 	fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
    666 	if (fd == -1)
    667 		return -1;
    668 	rump_sys_close(fd);
    669 
    670 	postcall(ukfs);
    671 	return 0;
    672 }
    673 
    674 int
    675 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
    676 {
    677 
    678 	STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
    679 }
    680 
    681 int
    682 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
    683 {
    684 
    685 	STDCALL(ukfs, rump_sys_mkfifo(path, mode));
    686 }
    687 
    688 int
    689 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
    690 {
    691 
    692 	STDCALL(ukfs, rump_sys_mkdir(filename, mode));
    693 }
    694 
    695 int
    696 ukfs_remove(struct ukfs *ukfs, const char *filename)
    697 {
    698 
    699 	STDCALL(ukfs, rump_sys_unlink(filename));
    700 }
    701 
    702 int
    703 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
    704 {
    705 
    706 	STDCALL(ukfs, rump_sys_rmdir(filename));
    707 }
    708 
    709 int
    710 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
    711 {
    712 
    713 	STDCALL(ukfs, rump_sys_link(filename, f_create));
    714 }
    715 
    716 int
    717 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
    718 {
    719 
    720 	STDCALL(ukfs, rump_sys_symlink(filename, linkname));
    721 }
    722 
    723 ssize_t
    724 ukfs_readlink(struct ukfs *ukfs, const char *filename,
    725 	char *linkbuf, size_t buflen)
    726 {
    727 	ssize_t rv;
    728 
    729 	precall(ukfs);
    730 	rv = rump_sys_readlink(filename, linkbuf, buflen);
    731 	postcall(ukfs);
    732 	return rv;
    733 }
    734 
    735 int
    736 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
    737 {
    738 
    739 	STDCALL(ukfs, rump_sys_rename(from, to));
    740 }
    741 
    742 int
    743 ukfs_chdir(struct ukfs *ukfs, const char *path)
    744 {
    745 	struct vnode *newvp, *oldvp;
    746 	int rv;
    747 
    748 	precall(ukfs);
    749 	rv = rump_sys_chdir(path);
    750 	if (rv == -1)
    751 		goto out;
    752 
    753 	newvp = rump_cdir_get();
    754 	pthread_spin_lock(&ukfs->ukfs_spin);
    755 	oldvp = ukfs->ukfs_cdir;
    756 	ukfs->ukfs_cdir = newvp;
    757 	pthread_spin_unlock(&ukfs->ukfs_spin);
    758 	if (oldvp)
    759 		rump_vp_rele(oldvp);
    760 
    761  out:
    762 	postcall(ukfs);
    763 	return rv;
    764 }
    765 
    766 /*
    767  * If we want to use post-time_t file systems on pre-time_t hosts,
    768  * we must translate the stat structure.  Since we don't currently
    769  * have a general method for making compat calls in rump, special-case
    770  * this one.
    771  *
    772  * Note that this does not allow making system calls to older rump
    773  * kernels from newer hosts.
    774  */
    775 #define VERS_TIMECHANGE 599000700
    776 
    777 static int
    778 needcompat(void)
    779 {
    780 
    781 #ifdef __NetBSD__
    782 	/*LINTED*/
    783 	return __NetBSD_Version__ < VERS_TIMECHANGE
    784 	    && rump_getversion() >= VERS_TIMECHANGE;
    785 #else
    786 	return 0;
    787 #endif
    788 }
    789 
    790 int
    791 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
    792 {
    793 	int rv;
    794 
    795 	precall(ukfs);
    796 	if (needcompat())
    797 		rv = rump_sys___stat30(filename, file_stat);
    798 	else
    799 		rv = rump_sys_stat(filename, file_stat);
    800 	postcall(ukfs);
    801 
    802 	return rv;
    803 }
    804 
    805 int
    806 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
    807 {
    808 	int rv;
    809 
    810 	precall(ukfs);
    811 	if (needcompat())
    812 		rv = rump_sys___lstat30(filename, file_stat);
    813 	else
    814 		rv = rump_sys_lstat(filename, file_stat);
    815 	postcall(ukfs);
    816 
    817 	return rv;
    818 }
    819 
    820 int
    821 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
    822 {
    823 
    824 	STDCALL(ukfs, rump_sys_chmod(filename, mode));
    825 }
    826 
    827 int
    828 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
    829 {
    830 
    831 	STDCALL(ukfs, rump_sys_lchmod(filename, mode));
    832 }
    833 
    834 int
    835 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
    836 {
    837 
    838 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
    839 }
    840 
    841 int
    842 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
    843 {
    844 
    845 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
    846 }
    847 
    848 int
    849 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
    850 {
    851 
    852 	STDCALL(ukfs, rump_sys_chflags(filename, flags));
    853 }
    854 
    855 int
    856 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
    857 {
    858 
    859 	STDCALL(ukfs, rump_sys_lchflags(filename, flags));
    860 }
    861 
    862 int
    863 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
    864 {
    865 
    866 	STDCALL(ukfs, rump_sys_utimes(filename, tptr));
    867 }
    868 
    869 int
    870 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
    871 	      const struct timeval *tptr)
    872 {
    873 
    874 	STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
    875 }
    876 
    877 /*
    878  * Dynamic module support
    879  */
    880 
    881 /* load one library */
    882 
    883 /*
    884  * XXX: the dlerror stuff isn't really threadsafe, but then again I
    885  * can't protect against other threads calling dl*() outside of ukfs,
    886  * so just live with it being flimsy
    887  */
    888 int
    889 ukfs_modload(const char *fname)
    890 {
    891 	void *handle;
    892 	struct modinfo **mi;
    893 	int error;
    894 
    895 	handle = dlopen(fname, RTLD_GLOBAL);
    896 	if (handle == NULL) {
    897 		const char *dlmsg = dlerror();
    898 		if (strstr(dlmsg, "Undefined symbol"))
    899 			return 0;
    900 		warnx("dlopen %s failed: %s\n", fname, dlmsg);
    901 		/* XXXerrno */
    902 		return -1;
    903 	}
    904 
    905 	mi = dlsym(handle, "__start_link_set_modules");
    906 	if (mi) {
    907 		error = rump_module_init(*mi, NULL);
    908 		if (error)
    909 			goto errclose;
    910 		return 1;
    911 	}
    912 	error = EINVAL;
    913 
    914  errclose:
    915 	dlclose(handle);
    916 	errno = error;
    917 	return -1;
    918 }
    919 
    920 struct loadfail {
    921 	char *pname;
    922 
    923 	LIST_ENTRY(loadfail) entries;
    924 };
    925 
    926 #define RUMPFSMOD_PREFIX "librumpfs_"
    927 #define RUMPFSMOD_SUFFIX ".so"
    928 
    929 int
    930 ukfs_modload_dir(const char *dir)
    931 {
    932 	char nbuf[MAXPATHLEN+1], *p;
    933 	struct dirent entry, *result;
    934 	DIR *libdir;
    935 	struct loadfail *lf, *nlf;
    936 	int error, nloaded = 0, redo;
    937 	LIST_HEAD(, loadfail) lfs;
    938 
    939 	libdir = opendir(dir);
    940 	if (libdir == NULL)
    941 		return -1;
    942 
    943 	LIST_INIT(&lfs);
    944 	for (;;) {
    945 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
    946 			break;
    947 		if (!result)
    948 			break;
    949 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
    950 		    strlen(RUMPFSMOD_PREFIX)) != 0)
    951 			continue;
    952 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
    953 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
    954 			continue;
    955 		strlcpy(nbuf, dir, sizeof(nbuf));
    956 		strlcat(nbuf, "/", sizeof(nbuf));
    957 		strlcat(nbuf, result->d_name, sizeof(nbuf));
    958 		switch (ukfs_modload(nbuf)) {
    959 		case 0:
    960 			lf = malloc(sizeof(*lf));
    961 			if (lf == NULL) {
    962 				error = ENOMEM;
    963 				break;
    964 			}
    965 			lf->pname = strdup(nbuf);
    966 			if (lf->pname == NULL) {
    967 				free(lf);
    968 				error = ENOMEM;
    969 				break;
    970 			}
    971 			LIST_INSERT_HEAD(&lfs, lf, entries);
    972 			break;
    973 		case 1:
    974 			nloaded++;
    975 			break;
    976 		default:
    977 			/* ignore errors */
    978 			break;
    979 		}
    980 	}
    981 	closedir(libdir);
    982 	if (error && nloaded != 0)
    983 		error = 0;
    984 
    985 	/*
    986 	 * El-cheapo dependency calculator.  Just try to load the
    987 	 * modules n times in a loop
    988 	 */
    989 	for (redo = 1; redo;) {
    990 		redo = 0;
    991 		nlf = LIST_FIRST(&lfs);
    992 		while ((lf = nlf) != NULL) {
    993 			nlf = LIST_NEXT(lf, entries);
    994 			if (ukfs_modload(lf->pname) == 1) {
    995 				nloaded++;
    996 				redo = 1;
    997 				LIST_REMOVE(lf, entries);
    998 				free(lf->pname);
    999 				free(lf);
   1000 			}
   1001 		}
   1002 	}
   1003 
   1004 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
   1005 		LIST_REMOVE(lf, entries);
   1006 		free(lf->pname);
   1007 		free(lf);
   1008 	}
   1009 
   1010 	if (error && nloaded == 0) {
   1011 		errno = error;
   1012 		return -1;
   1013 	}
   1014 
   1015 	return nloaded;
   1016 }
   1017 
   1018 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
   1019 ssize_t
   1020 ukfs_vfstypes(char *buf, size_t buflen)
   1021 {
   1022 	int mib[3];
   1023 	struct sysctlnode q, ans[128];
   1024 	size_t alen;
   1025 	int i;
   1026 
   1027 	mib[0] = CTL_VFS;
   1028 	mib[1] = VFS_GENERIC;
   1029 	mib[2] = CTL_QUERY;
   1030 	alen = sizeof(ans);
   1031 
   1032 	memset(&q, 0, sizeof(q));
   1033 	q.sysctl_flags = SYSCTL_VERSION;
   1034 
   1035 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
   1036 		return -1;
   1037 	}
   1038 
   1039 	for (i = 0; i < alen/sizeof(ans[0]); i++)
   1040 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
   1041 			break;
   1042 	if (i == alen/sizeof(ans[0])) {
   1043 		errno = ENXIO;
   1044 		return -1;
   1045 	}
   1046 
   1047 	mib[0] = CTL_VFS;
   1048 	mib[1] = VFS_GENERIC;
   1049 	mib[2] = ans[i].sysctl_num;
   1050 
   1051 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
   1052 		return -1;
   1053 	}
   1054 
   1055 	return buflen;
   1056 }
   1057 
   1058 /*
   1059  * Utilities
   1060  */
   1061 static int
   1062 builddirs(const char *pathname, mode_t mode,
   1063 	int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs)
   1064 {
   1065 	char *f1, *f2;
   1066 	int rv;
   1067 	mode_t mask;
   1068 	bool end;
   1069 
   1070 	/*ukfs_umask((mask = ukfs_umask(0)));*/
   1071 	umask((mask = umask(0)));
   1072 
   1073 	f1 = f2 = strdup(pathname);
   1074 	if (f1 == NULL) {
   1075 		errno = ENOMEM;
   1076 		return -1;
   1077 	}
   1078 
   1079 	end = false;
   1080 	for (;;) {
   1081 		/* find next component */
   1082 		f2 += strspn(f2, "/");
   1083 		f2 += strcspn(f2, "/");
   1084 		if (*f2 == '\0')
   1085 			end = true;
   1086 		else
   1087 			*f2 = '\0';
   1088 
   1089 		rv = mkdirfn(fs, f1, mode & ~mask);
   1090 		if (errno == EEXIST)
   1091 			rv = 0;
   1092 
   1093 		if (rv == -1 || *f2 != '\0' || end)
   1094 			break;
   1095 
   1096 		*f2 = '/';
   1097 	}
   1098 
   1099 	free(f1);
   1100 
   1101 	return rv;
   1102 }
   1103 
   1104 int
   1105 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
   1106 {
   1107 
   1108 	return builddirs(pathname, mode, ukfs_mkdir, ukfs);
   1109 }
   1110