Home | History | Annotate | Line # | Download | only in libukfs
ukfs.c revision 1.41
      1 /*	$NetBSD: ukfs.c,v 1.41 2009/10/15 16:41:08 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_pub_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_pub_lwp_alloc_and_switch(nextpid(ukfs), 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_pub_rcvp_set(rvp, cvp); /* takes refs */
    150 	rump_pub_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_pub_rcvp_set(NULL, rvp);
    160 	rump_pub_vp_rele(rvp);
    161 	rump_pub_lwp_release(rump_pub_lwp_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_pub_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_pub_vfs_getmp(mountpath, &fs->ukfs_mp);
    370 	if (rv) {
    371 		goto out;
    372 	}
    373 	rv = rump_pub_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_pub_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_pub_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_pub_lwp_alloc_and_switch(nextpid(fs), 1);
    439 		rump_pub_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_pub_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
    445 			rump_pub_lwp_release(rump_pub_lwp_curlwp());
    446 			ukfs_chdir(fs, fs->ukfs_mountpath);
    447 			errno = error;
    448 			return -1;
    449 		}
    450 		rump_pub_lwp_release(rump_pub_lwp_curlwp());
    451 	}
    452 
    453 	if (fs->ukfs_devpath) {
    454 		rump_pub_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_pub_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_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
    509 	cred = rump_pub_cred_suserget();
    510 	rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
    511 	rump_pub_cred_put(cred);
    512 	RUMP_VOP_UNLOCK(vp, 0);
    513 	*off = rump_pub_uio_getoff(uio);
    514 	resid = rump_pub_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_pub_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_pub_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_pub_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_pub_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_pub_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_pub_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_pub_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_pub_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_pub_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