Home | History | Annotate | Line # | Download | only in libukfs
      1 /*	$NetBSD: ukfs.c,v 1.61 2025/12/18 23:15:54 nia 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/endian.h>
     44 #include <sys/queue.h>
     45 #include <sys/stat.h>
     46 #include <sys/sysctl.h>
     47 #include <sys/mount.h>
     48 
     49 #include <assert.h>
     50 #include <dirent.h>
     51 #include <dlfcn.h>
     52 #include <err.h>
     53 #include <errno.h>
     54 #include <fcntl.h>
     55 #include <pthread.h>
     56 #include <stdio.h>
     57 #include <stdlib.h>
     58 #include <string.h>
     59 #include <unistd.h>
     60 #include <stdint.h>
     61 
     62 #include <rump/ukfs.h>
     63 
     64 #include <rump/rump.h>
     65 #include <rump/rumpvnode_if.h>
     66 #include <rump/rump_syscalls.h>
     67 
     68 #include "ukfs_int_disklabel.h"
     69 
     70 #define UKFS_MODE_DEFAULT 0555
     71 
     72 struct ukfs {
     73 	pthread_spinlock_t ukfs_spin;
     74 
     75 	struct mount *ukfs_mp;
     76 	struct lwp *ukfs_lwp;
     77 	void *ukfs_specific;
     78 
     79 	int ukfs_devfd;
     80 
     81 	char *ukfs_devpath;
     82 	char *ukfs_mountpath;
     83 	char *ukfs_cwd;
     84 
     85 	struct ukfs_part *ukfs_part;
     86 };
     87 
     88 static int builddirs(const char *, mode_t,
     89     int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *);
     90 
     91 struct mount *
     92 ukfs_getmp(struct ukfs *ukfs)
     93 {
     94 
     95 	return ukfs->ukfs_mp;
     96 }
     97 
     98 void
     99 ukfs_setspecific(struct ukfs *ukfs, void *priv)
    100 {
    101 
    102 	ukfs->ukfs_specific = priv;
    103 }
    104 
    105 void *
    106 ukfs_getspecific(struct ukfs *ukfs)
    107 {
    108 
    109 	return ukfs->ukfs_specific;
    110 }
    111 
    112 #ifdef DONT_WANT_PTHREAD_LINKAGE
    113 #define pthread_spin_lock(a)
    114 #define pthread_spin_unlock(a)
    115 #define pthread_spin_init(a,b)
    116 #define pthread_spin_destroy(a)
    117 #endif
    118 
    119 static int
    120 precall(struct ukfs *ukfs, struct lwp **curlwp)
    121 {
    122 
    123 	/* save previous.  ensure start from pristine context */
    124 	*curlwp = rump_pub_lwproc_curlwp();
    125 	if (*curlwp)
    126 		rump_pub_lwproc_switch(ukfs->ukfs_lwp);
    127 	rump_pub_lwproc_rfork(RUMP_RFCFDG);
    128 
    129 	if (rump_sys_chroot(ukfs->ukfs_mountpath) == -1)
    130 		return errno;
    131 	if (rump_sys_chdir(ukfs->ukfs_cwd) == -1)
    132 		return errno;
    133 
    134 	return 0;
    135 }
    136 
    137 static void
    138 postcall(struct lwp *curlwp)
    139 {
    140 
    141 	rump_pub_lwproc_releaselwp();
    142 	if (curlwp)
    143 		rump_pub_lwproc_switch(curlwp);
    144 }
    145 
    146 #define PRECALL()							\
    147 struct lwp *ukfs_curlwp;						\
    148 do {									\
    149 	int ukfs_rv;							\
    150 	if ((ukfs_rv = precall(ukfs, &ukfs_curlwp)) != 0) {		\
    151 		errno = ukfs_rv;					\
    152 		return -1;						\
    153 	}								\
    154 } while (0)
    155 
    156 #define POSTCALL() postcall(ukfs_curlwp);
    157 
    158 struct ukfs_part {
    159 	pthread_spinlock_t part_lck;
    160 	int part_refcount;
    161 
    162 	int part_type;
    163 	char part_labelchar;
    164 	off_t part_devoff;
    165 	off_t part_devsize;
    166 };
    167 
    168 enum ukfs_parttype { UKFS_PART_NONE, UKFS_PART_DISKLABEL, UKFS_PART_OFFSET };
    169 
    170 static struct ukfs_part ukfs__part_none = {
    171 	.part_type = UKFS_PART_NONE,
    172 	.part_devoff = 0,
    173 	.part_devsize = RUMP_ETFS_SIZE_ENDOFF,
    174 };
    175 static struct ukfs_part ukfs__part_na;
    176 struct ukfs_part *ukfs_part_none = &ukfs__part_none;
    177 struct ukfs_part *ukfs_part_na = &ukfs__part_na;
    178 
    179 #define PART2LOCKSIZE(len) ((len) == RUMP_ETFS_SIZE_ENDOFF ? 0 : (len))
    180 
    181 int
    182 _ukfs_init(int version)
    183 {
    184 	int rv;
    185 
    186 	if (version != UKFS_VERSION) {
    187 		errno = EPROGMISMATCH;
    188 		warn("incompatible ukfs version, %d vs. %d",
    189 		    version, UKFS_VERSION);
    190 		return -1;
    191 	}
    192 
    193 	if ((rv = rump_init()) != 0) {
    194 		errno = rv;
    195 		return -1;
    196 	}
    197 
    198 	return 0;
    199 }
    200 
    201 /*ARGSUSED*/
    202 static int
    203 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode)
    204 {
    205 
    206 	return rump_sys_mkdir(path, mode);
    207 }
    208 
    209 int
    210 ukfs_part_probe(char *devpath, struct ukfs_part **partp)
    211 {
    212 	struct ukfs_part *part;
    213 	char *p;
    214 	int error = 0;
    215 	int devfd = -1;
    216 
    217 	if ((p = strstr(devpath, UKFS_PARTITION_SCANMAGIC)) != NULL) {
    218 		warnx("ukfs: %%PART is deprecated.  use "
    219 		    "%%DISKLABEL instead");
    220 		errno = ENODEV;
    221 		return -1;
    222 	}
    223 
    224 	part = malloc(sizeof(*part));
    225 	if (part == NULL) {
    226 		errno = ENOMEM;
    227 		return -1;
    228 	}
    229 	if (pthread_spin_init(&part->part_lck, PTHREAD_PROCESS_PRIVATE) == -1) {
    230 		error = errno;
    231 		free(part);
    232 		errno = error;
    233 		return -1;
    234 	}
    235 	part->part_type = UKFS_PART_NONE;
    236 	part->part_refcount = 1;
    237 
    238 	/*
    239 	 * Check for magic in pathname:
    240 	 *   disklabel: /regularpath%DISKLABEL:labelchar%\0
    241 	 *     offsets: /regularpath%OFFSET:start,end%\0
    242 	 */
    243 #define MAGICADJ_DISKLABEL(p, n) (p+sizeof(UKFS_DISKLABEL_SCANMAGIC)-1+n)
    244 	if ((p = strstr(devpath, UKFS_DISKLABEL_SCANMAGIC)) != NULL
    245 	    && strlen(p) == UKFS_DISKLABEL_MAGICLEN
    246 	    && *(MAGICADJ_DISKLABEL(p,1)) == '%') {
    247 		if (*(MAGICADJ_DISKLABEL(p,0)) >= 'a' &&
    248 		    *(MAGICADJ_DISKLABEL(p,0)) < 'a' + UKFS_MAXPARTITIONS) {
    249 			struct ukfs__disklabel dl;
    250 			struct ukfs__partition *pp;
    251 			int imswapped;
    252 			char buf[65536];
    253 			char labelchar = *(MAGICADJ_DISKLABEL(p,0));
    254 			int partition = labelchar - 'a';
    255 			uint32_t poffset, psize;
    256 
    257 			*p = '\0';
    258 			devfd = open(devpath, O_RDONLY);
    259 			if (devfd == -1) {
    260 				error = errno;
    261 				goto out;
    262 			}
    263 
    264 			/* Locate the disklabel and find the partition. */
    265 			if (pread(devfd, buf, sizeof(buf), 0) == -1) {
    266 				error = errno;
    267 				goto out;
    268 			}
    269 
    270 			if (ukfs__disklabel_scan(&dl, &imswapped,
    271 			    buf, sizeof(buf)) != 0) {
    272 				error = ENOENT;
    273 				goto out;
    274 			}
    275 
    276 			if (dl.d_npartitions < partition) {
    277 				error = ENOENT;
    278 				goto out;
    279 			}
    280 
    281 			pp = &dl.d_partitions[partition];
    282 			part->part_type = UKFS_PART_DISKLABEL;
    283 			part->part_labelchar = labelchar;
    284 			if (imswapped) {
    285 				poffset = bswap32(pp->p_offset);
    286 				psize = bswap32(pp->p_size);
    287 			} else {
    288 				poffset = pp->p_offset;
    289 				psize = pp->p_size;
    290 			}
    291 			part->part_devoff = poffset << DEV_BSHIFT;
    292 			part->part_devsize = psize << DEV_BSHIFT;
    293 		} else {
    294 			error = EINVAL;
    295 		}
    296 #define MAGICADJ_OFFSET(p, n) (p+sizeof(UKFS_OFFSET_SCANMAGIC)-1+n)
    297 	} else if (((p = strstr(devpath, UKFS_OFFSET_SCANMAGIC)) != NULL)
    298 	    && (strlen(p) >= UKFS_OFFSET_MINLEN)) {
    299 		char *comma, *pers, *ep, *nptr;
    300 		u_quad_t val;
    301 
    302 		comma = strchr(p, ',');
    303 		if (comma == NULL) {
    304 			error = EINVAL;
    305 			goto out;
    306 		}
    307 		pers = strchr(comma, '%');
    308 		if (pers == NULL) {
    309 			error = EINVAL;
    310 			goto out;
    311 		}
    312 		*comma = '\0';
    313 		*pers = '\0';
    314 		*p = '\0';
    315 
    316 		nptr = MAGICADJ_OFFSET(p,0);
    317 		/* check if string is negative */
    318 		if (*nptr == '-') {
    319 			error = ERANGE;
    320 			goto out;
    321 		}
    322 		val = strtouq(nptr, &ep, 10);
    323 		if (val == UQUAD_MAX) {
    324 			error = ERANGE;
    325 			goto out;
    326 		}
    327 		if (*ep != '\0') {
    328 			error = EADDRNOTAVAIL; /* creative ;) */
    329 			goto out;
    330 		}
    331 		part->part_devoff = val;
    332 
    333 		/* omstart */
    334 
    335 		nptr = comma+1;
    336 		/* check if string is negative */
    337 		if (*nptr == '-') {
    338 			error = ERANGE;
    339 			goto out;
    340 		}
    341 		val = strtouq(nptr, &ep, 10);
    342 		if (val == UQUAD_MAX) {
    343 			error = ERANGE;
    344 			goto out;
    345 		}
    346 		if (*ep != '\0') {
    347 			error = EADDRNOTAVAIL; /* creative ;) */
    348 			goto out;
    349 		}
    350 		part->part_devsize = val;
    351 		part->part_type = UKFS_PART_OFFSET;
    352 	} else {
    353 		ukfs_part_release(part);
    354 		part = ukfs_part_none;
    355 	}
    356 
    357  out:
    358 	if (devfd != -1)
    359 		close(devfd);
    360 	if (error) {
    361 		free(part);
    362 		errno = error;
    363 	} else {
    364 		*partp = part;
    365 	}
    366 
    367 	return error ? -1 : 0;
    368 }
    369 
    370 int
    371 ukfs_part_tostring(struct ukfs_part *part, char *str, size_t strsize)
    372 {
    373 	int rv;
    374 
    375 	*str = '\0';
    376 	/* "pseudo" values */
    377 	if (part == ukfs_part_na) {
    378 		errno = EINVAL;
    379 		return -1;
    380 	}
    381 	if (part == ukfs_part_none)
    382 		return 0;
    383 
    384 	rv = 0;
    385 	switch (part->part_type) {
    386 	case UKFS_PART_NONE:
    387 		break;
    388 
    389 	case UKFS_PART_DISKLABEL:
    390 		snprintf(str, strsize, "%%DISKLABEL:%c%%",part->part_labelchar);
    391 		rv = 1;
    392 		break;
    393 
    394 	case UKFS_PART_OFFSET:
    395 		snprintf(str, strsize, "[%llu,%llu]",
    396 		    (unsigned long long)part->part_devoff,
    397 		    (unsigned long long)(part->part_devoff+part->part_devsize));
    398 		rv = 1;
    399 		break;
    400 	}
    401 
    402 	return rv;
    403 }
    404 
    405 static void
    406 unlockdev(int fd, struct ukfs_part *part)
    407 {
    408 	struct flock flarg;
    409 
    410 	if (part == ukfs_part_na)
    411 		return;
    412 
    413 	memset(&flarg, 0, sizeof(flarg));
    414 	flarg.l_type = F_UNLCK;
    415 	flarg.l_whence = SEEK_SET;
    416 	flarg.l_start = part->part_devoff;
    417 	flarg.l_len = PART2LOCKSIZE(part->part_devsize);
    418 	if (fcntl(fd, F_SETLK, &flarg) == -1)
    419 		warn("ukfs: cannot unlock device file");
    420 }
    421 
    422 /*
    423  * Open the disk file and flock it.  Also, if we are operation on
    424  * an embedded partition, find the partition offset and size from
    425  * the disklabel.
    426  *
    427  * We hard-fail only in two cases:
    428  *  1) we failed to get the partition info out (don't know what offset
    429  *     to mount from)
    430  *  2) we failed to flock the source device (i.e. fcntl() fails,
    431  *     not e.g. open() before it)
    432  *
    433  * Otherwise we let the code proceed to mount and let the file system
    434  * throw the proper error.  The only questionable bit is that if we
    435  * soft-fail before flock and mount does succeed...
    436  *
    437  * Returns: -1 error (errno reports error code)
    438  *           0 success
    439  *
    440  * dfdp: -1  device is not open
    441  *        n  device is open
    442  */
    443 static int
    444 process_diskdevice(const char *devpath, struct ukfs_part *part, int rdonly,
    445 	int *dfdp)
    446 {
    447 	struct stat sb;
    448 	int rv = 0, devfd;
    449 
    450 	/* defaults */
    451 	*dfdp = -1;
    452 
    453 	devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR);
    454 	if (devfd == -1) {
    455 		rv = errno;
    456 		goto out;
    457 	}
    458 
    459 	if (fstat(devfd, &sb) == -1) {
    460 		rv = errno;
    461 		goto out;
    462 	}
    463 
    464 	/*
    465 	 * We do this only for non-block device since the
    466 	 * (NetBSD) kernel allows block device open only once.
    467 	 * We also need to close the device for fairly obvious reasons.
    468 	 */
    469 	if (!S_ISBLK(sb.st_mode)) {
    470 		struct flock flarg;
    471 
    472 		memset(&flarg, 0, sizeof(flarg));
    473 		flarg.l_type = rdonly ? F_RDLCK : F_WRLCK;
    474 		flarg.l_whence = SEEK_SET;
    475 		flarg.l_start = part->part_devoff;
    476 		flarg.l_len = PART2LOCKSIZE(part->part_devsize);
    477 		if (fcntl(devfd, F_SETLK, &flarg) == -1) {
    478 			pid_t holder;
    479 			int sverrno;
    480 
    481 			sverrno = errno;
    482 			if (fcntl(devfd, F_GETLK, &flarg) != 1)
    483 				holder = flarg.l_pid;
    484 			else
    485 				holder = -1;
    486 			warnx("ukfs_mount: cannot lock device.  held by pid %d",
    487 			    holder);
    488 			rv = sverrno;
    489 			goto out;
    490 		}
    491 	} else {
    492 		close(devfd);
    493 		devfd = -1;
    494 	}
    495 	*dfdp = devfd;
    496 
    497  out:
    498 	if (rv) {
    499 		if (devfd != -1)
    500 			close(devfd);
    501 	}
    502 
    503 	return rv;
    504 }
    505 
    506 struct mountinfo {
    507 	const char *mi_vfsname;
    508 	const char *mi_mountpath;
    509 	int mi_mntflags;
    510 	void *mi_arg;
    511 	size_t mi_alen;
    512 	int *mi_error;
    513 };
    514 static void *
    515 mfs_mounter(void *arg)
    516 {
    517 	struct mountinfo *mi = arg;
    518 	int rv;
    519 
    520 	rv = rump_sys_mount(mi->mi_vfsname, mi->mi_mountpath, mi->mi_mntflags,
    521 	    mi->mi_arg, mi->mi_alen);
    522 	if (rv) {
    523 		warn("mfs mount failed.  fix me.");
    524 		abort(); /* XXX */
    525 	}
    526 
    527 	return NULL;
    528 }
    529 
    530 static struct ukfs *
    531 doukfsmount(const char *vfsname, const char *devpath, struct ukfs_part *part,
    532 	const char *mountpath, int mntflags, void *arg, size_t alen)
    533 {
    534 	struct ukfs *fs = NULL;
    535 	struct lwp *curlwp;
    536 	int rv = 0, devfd = -1;
    537 	int mounted = 0;
    538 	int regged = 0;
    539 
    540 	pthread_spin_lock(&part->part_lck);
    541 	part->part_refcount++;
    542 	pthread_spin_unlock(&part->part_lck);
    543 	if (part != ukfs_part_na) {
    544 		if ((rv = process_diskdevice(devpath, part,
    545 		    mntflags & MNT_RDONLY, &devfd)) != 0)
    546 			goto out;
    547 	}
    548 
    549 	fs = malloc(sizeof(struct ukfs));
    550 	if (fs == NULL) {
    551 		rv = ENOMEM;
    552 		goto out;
    553 	}
    554 	memset(fs, 0, sizeof(struct ukfs));
    555 
    556 	/* create our mountpoint.  this is never removed. */
    557 	if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) {
    558 		if (errno != EEXIST) {
    559 			rv = errno;
    560 			goto out;
    561 		}
    562 	}
    563 
    564 	if (part != ukfs_part_na) {
    565 		/* LINTED */
    566 		rv = rump_pub_etfs_register_withsize(devpath, devpath,
    567 		    RUMP_ETFS_BLK, part->part_devoff, part->part_devsize);
    568 		if (rv) {
    569 			goto out;
    570 		}
    571 		regged = 1;
    572 	}
    573 
    574 	/*
    575 	 * MFS is special since mount(2) doesn't return.  Hence, we
    576 	 * create a thread here.  Could fix mfs to return, but there's
    577 	 * too much history for me to bother.
    578 	 */
    579 	if (strcmp(vfsname, MOUNT_MFS) == 0) {
    580 		pthread_t pt;
    581 		struct mountinfo mi;
    582 		int i;
    583 
    584 		mi.mi_vfsname = vfsname;
    585 		mi.mi_mountpath = mountpath;
    586 		mi.mi_mntflags = mntflags;
    587 		mi.mi_arg = arg;
    588 		mi.mi_alen = alen;
    589 
    590 		if (pthread_create(&pt, NULL, mfs_mounter, &mi) == -1) {
    591 			rv = errno;
    592 			goto out;
    593 		}
    594 
    595 		for (i = 0;i < 100000; i++) {
    596 			struct statvfs svfsb;
    597 
    598 			rv = rump_sys_statvfs1(mountpath, &svfsb, ST_WAIT);
    599 			if (rv == -1) {
    600 				rv = errno;
    601 				goto out;
    602 			}
    603 
    604 			if (strcmp(svfsb.f_mntonname, mountpath) == 0 &&
    605 			    strcmp(svfsb.f_fstypename, MOUNT_MFS) == 0) {
    606 				break;
    607 			}
    608 			usleep(1);
    609 		}
    610 	} else {
    611 		rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen);
    612 		if (rv) {
    613 			rv = errno;
    614 			goto out;
    615 		}
    616 	}
    617 
    618 	mounted = 1;
    619 	rv = rump_pub_vfs_getmp(mountpath, &fs->ukfs_mp);
    620 	if (rv) {
    621 		goto out;
    622 	}
    623 
    624 	if (regged) {
    625 		fs->ukfs_devpath = strdup(devpath);
    626 	}
    627 	fs->ukfs_mountpath = strdup(mountpath);
    628 	pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
    629 	fs->ukfs_devfd = devfd;
    630 	fs->ukfs_part = part;
    631 	assert(rv == 0);
    632 
    633 	curlwp = rump_pub_lwproc_curlwp();
    634 	rump_pub_lwproc_newlwp(0);
    635 	fs->ukfs_lwp = rump_pub_lwproc_curlwp();
    636 	fs->ukfs_cwd = strdup("/");
    637 	rump_pub_lwproc_switch(curlwp);
    638 
    639  out:
    640 	if (rv) {
    641 		if (fs) {
    642 			free(fs);
    643 			fs = NULL;
    644 		}
    645 		if (mounted)
    646 			rump_sys_unmount(mountpath, MNT_FORCE);
    647 		if (regged)
    648 			rump_pub_etfs_remove(devpath);
    649 		if (devfd != -1) {
    650 			unlockdev(devfd, part);
    651 			close(devfd);
    652 		}
    653 		ukfs_part_release(part);
    654 		errno = rv;
    655 	}
    656 
    657 	return fs;
    658 }
    659 
    660 struct ukfs *
    661 ukfs_mount(const char *vfsname, const char *devpath,
    662 	const char *mountpath, int mntflags, void *arg, size_t alen)
    663 {
    664 
    665 	return doukfsmount(vfsname, devpath, ukfs_part_na,
    666 	    mountpath, mntflags, arg, alen);
    667 }
    668 
    669 struct ukfs *
    670 ukfs_mount_disk(const char *vfsname, const char *devpath,
    671 	struct ukfs_part *part, const char *mountpath, int mntflags,
    672 	void *arg, size_t alen)
    673 {
    674 
    675 	return doukfsmount(vfsname, devpath, part,
    676 	    mountpath, mntflags, arg, alen);
    677 }
    678 
    679 int
    680 ukfs_release(struct ukfs *fs, int flags)
    681 {
    682 	struct lwp *curlwp = rump_pub_lwproc_curlwp();
    683 
    684 	/* get root lwp */
    685 	rump_pub_lwproc_switch(fs->ukfs_lwp);
    686 	rump_pub_lwproc_rfork(RUMP_RFCFDG);
    687 
    688 	if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
    689 		int rv, mntflag, error;
    690 
    691 		mntflag = 0;
    692 		if (flags & UKFS_RELFLAG_FORCE)
    693 			mntflag = MNT_FORCE;
    694 
    695 		rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag);
    696 		if (rv == -1) {
    697 			error = errno;
    698 			rump_pub_lwproc_releaselwp();
    699 			if (curlwp)
    700 				rump_pub_lwproc_switch(curlwp);
    701 			errno = error;
    702 			return -1;
    703 		}
    704 	}
    705 
    706 	if (fs->ukfs_devpath) {
    707 		rump_pub_etfs_remove(fs->ukfs_devpath);
    708 		free(fs->ukfs_devpath);
    709 	}
    710 	free(fs->ukfs_mountpath);
    711 	free(fs->ukfs_cwd);
    712 
    713 	/* release this routine's lwp and ukfs base lwp */
    714 	rump_pub_lwproc_releaselwp();
    715 	rump_pub_lwproc_switch(fs->ukfs_lwp);
    716 	rump_pub_lwproc_releaselwp();
    717 
    718 	pthread_spin_destroy(&fs->ukfs_spin);
    719 	if (fs->ukfs_devfd != -1) {
    720 		unlockdev(fs->ukfs_devfd, fs->ukfs_part);
    721 		close(fs->ukfs_devfd);
    722 	}
    723 	ukfs_part_release(fs->ukfs_part);
    724 	free(fs);
    725 
    726 	if (curlwp)
    727 		rump_pub_lwproc_switch(curlwp);
    728 
    729 	return 0;
    730 }
    731 
    732 void
    733 ukfs_part_release(struct ukfs_part *part)
    734 {
    735 	int release;
    736 
    737 	if (part != ukfs_part_none && part != ukfs_part_na) {
    738 		pthread_spin_lock(&part->part_lck);
    739 		release = --part->part_refcount == 0;
    740 		pthread_spin_unlock(&part->part_lck);
    741 		if (release) {
    742 			pthread_spin_destroy(&part->part_lck);
    743 			free(part);
    744 		}
    745 	}
    746 }
    747 
    748 #define STDCALL(ukfs, thecall)						\
    749 	int rv = 0;							\
    750 									\
    751 	PRECALL();							\
    752 	rv = thecall;							\
    753 	POSTCALL();							\
    754 	return rv;
    755 
    756 int
    757 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c)
    758 {
    759 	struct vnode *vp;
    760 	int rv;
    761 
    762 	PRECALL();
    763 	rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
    764 	    NULL, &vp, NULL);
    765 	POSTCALL();
    766 
    767 	if (rv == 0) {
    768 		RUMP_VOP_UNLOCK(vp);
    769 	} else {
    770 		errno = rv;
    771 		rv = -1;
    772 	}
    773 
    774 	/*LINTED*/
    775 	*c = (struct ukfs_dircookie *)vp;
    776 	return rv;
    777 }
    778 
    779 static int
    780 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize)
    781 {
    782 	struct uio *uio;
    783 	size_t resid;
    784 	int rv, eofflag;
    785 	struct kauth_cred *cred;
    786 
    787 	uio = rump_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
    788 	cred = rump_pub_cred_create(0, 0, 0, NULL);
    789 	rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
    790 	rump_pub_cred_put(cred);
    791 	RUMP_VOP_UNLOCK(vp);
    792 	*off = rump_pub_uio_getoff(uio);
    793 	resid = rump_pub_uio_free(uio);
    794 
    795 	if (rv) {
    796 		errno = rv;
    797 		return -1;
    798 	}
    799 
    800 	/* LINTED: not totally correct return type, but follows syscall */
    801 	return bufsize - resid;
    802 }
    803 
    804 /*ARGSUSED*/
    805 int
    806 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off,
    807 	uint8_t *buf, size_t bufsize)
    808 {
    809 	/*LINTED*/
    810 	struct vnode *vp = (struct vnode *)c;
    811 
    812 	RUMP_VOP_LOCK(vp, RUMP_LK_SHARED);
    813 	return getmydents(vp, off, buf, bufsize);
    814 }
    815 
    816 int
    817 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
    818 	uint8_t *buf, size_t bufsize)
    819 {
    820 	struct vnode *vp;
    821 	int rv;
    822 
    823 	PRECALL();
    824 	rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
    825 	    NULL, &vp, NULL);
    826 	if (rv) {
    827 		POSTCALL();
    828 		errno = rv;
    829 		return -1;
    830 	}
    831 
    832 	rv = getmydents(vp, off, buf, bufsize);
    833 	rump_pub_vp_rele(vp);
    834 	POSTCALL();
    835 	return rv;
    836 }
    837 
    838 /*ARGSUSED*/
    839 int
    840 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c)
    841 {
    842 
    843 	/*LINTED*/
    844 	rump_pub_vp_rele((struct vnode *)c);
    845 	return 0;
    846 }
    847 
    848 int
    849 ukfs_open(struct ukfs *ukfs, const char *filename, int flags)
    850 {
    851 	int fd;
    852 
    853 	PRECALL();
    854 	fd = rump_sys_open(filename, flags, 0);
    855 	POSTCALL();
    856 	if (fd == -1)
    857 		return -1;
    858 
    859 	return fd;
    860 }
    861 
    862 ssize_t
    863 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
    864 	uint8_t *buf, size_t bufsize)
    865 {
    866 	int fd;
    867 	ssize_t xfer = -1; /* XXXgcc */
    868 
    869 	PRECALL();
    870 	fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
    871 	if (fd == -1)
    872 		goto out;
    873 
    874 	xfer = rump_sys_pread(fd, buf, bufsize, off);
    875 	rump_sys_close(fd);
    876 
    877  out:
    878 	POSTCALL();
    879 	if (fd == -1) {
    880 		return -1;
    881 	}
    882 	return xfer;
    883 }
    884 
    885 /*ARGSUSED*/
    886 ssize_t
    887 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen)
    888 {
    889 
    890 	return rump_sys_pread(fd, buf, buflen, off);
    891 }
    892 
    893 ssize_t
    894 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
    895 	uint8_t *buf, size_t bufsize)
    896 {
    897 	int fd;
    898 	ssize_t xfer = -1; /* XXXgcc */
    899 
    900 	PRECALL();
    901 	fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
    902 	if (fd == -1)
    903 		goto out;
    904 
    905 	/* write and commit */
    906 	xfer = rump_sys_pwrite(fd, buf, bufsize, off);
    907 	if (xfer > 0)
    908 		rump_sys_fsync(fd);
    909 
    910 	rump_sys_close(fd);
    911 
    912  out:
    913 	POSTCALL();
    914 	if (fd == -1) {
    915 		return -1;
    916 	}
    917 	return xfer;
    918 }
    919 
    920 /*ARGSUSED*/
    921 ssize_t
    922 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen,
    923 	int dosync)
    924 {
    925 	ssize_t xfer;
    926 
    927 	xfer = rump_sys_pwrite(fd, buf, buflen, off);
    928 	if (xfer > 0 && dosync)
    929 		rump_sys_fsync(fd);
    930 
    931 	return xfer;
    932 }
    933 
    934 /*ARGSUSED*/
    935 int
    936 ukfs_close(struct ukfs *ukfs, int fd)
    937 {
    938 
    939 	rump_sys_close(fd);
    940 	return 0;
    941 }
    942 
    943 int
    944 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
    945 {
    946 	int fd;
    947 
    948 	PRECALL();
    949 	fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
    950 	if (fd == -1)
    951 		return -1;
    952 	rump_sys_close(fd);
    953 
    954 	POSTCALL();
    955 	return 0;
    956 }
    957 
    958 int
    959 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
    960 {
    961 
    962 	STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
    963 }
    964 
    965 int
    966 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
    967 {
    968 
    969 	STDCALL(ukfs, rump_sys_mkfifo(path, mode));
    970 }
    971 
    972 int
    973 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
    974 {
    975 
    976 	STDCALL(ukfs, rump_sys_mkdir(filename, mode));
    977 }
    978 
    979 int
    980 ukfs_remove(struct ukfs *ukfs, const char *filename)
    981 {
    982 
    983 	STDCALL(ukfs, rump_sys_unlink(filename));
    984 }
    985 
    986 int
    987 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
    988 {
    989 
    990 	STDCALL(ukfs, rump_sys_rmdir(filename));
    991 }
    992 
    993 int
    994 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
    995 {
    996 
    997 	STDCALL(ukfs, rump_sys_link(filename, f_create));
    998 }
    999 
   1000 int
   1001 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
   1002 {
   1003 
   1004 	STDCALL(ukfs, rump_sys_symlink(filename, linkname));
   1005 }
   1006 
   1007 ssize_t
   1008 ukfs_readlink(struct ukfs *ukfs, const char *filename,
   1009 	char *linkbuf, size_t buflen)
   1010 {
   1011 	ssize_t rv;
   1012 
   1013 	PRECALL();
   1014 	rv = rump_sys_readlink(filename, linkbuf, buflen);
   1015 	POSTCALL();
   1016 	return rv;
   1017 }
   1018 
   1019 int
   1020 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
   1021 {
   1022 
   1023 	STDCALL(ukfs, rump_sys_rename(from, to));
   1024 }
   1025 
   1026 int
   1027 ukfs_chdir(struct ukfs *ukfs, const char *path)
   1028 {
   1029 	char *newpath, *oldpath;
   1030 	int rv;
   1031 
   1032 	PRECALL();
   1033 	rv = rump_sys_chdir(path);
   1034 	if (rv == -1)
   1035 		goto out;
   1036 
   1037 	newpath = malloc(MAXPATHLEN);
   1038 	if (rump_sys___getcwd(newpath, MAXPATHLEN) == -1) {
   1039 		goto out;
   1040 	}
   1041 
   1042 	pthread_spin_lock(&ukfs->ukfs_spin);
   1043 	oldpath = ukfs->ukfs_cwd;
   1044 	ukfs->ukfs_cwd = newpath;
   1045 	pthread_spin_unlock(&ukfs->ukfs_spin);
   1046 	free(oldpath);
   1047 
   1048  out:
   1049 	POSTCALL();
   1050 	return rv;
   1051 }
   1052 
   1053 int
   1054 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
   1055 {
   1056 	int rv;
   1057 
   1058 	PRECALL();
   1059 	rv = rump_sys_stat(filename, file_stat);
   1060 	POSTCALL();
   1061 
   1062 	return rv;
   1063 }
   1064 
   1065 int
   1066 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
   1067 {
   1068 	int rv;
   1069 
   1070 	PRECALL();
   1071 	rv = rump_sys_lstat(filename, file_stat);
   1072 	POSTCALL();
   1073 
   1074 	return rv;
   1075 }
   1076 
   1077 int
   1078 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
   1079 {
   1080 
   1081 	STDCALL(ukfs, rump_sys_chmod(filename, mode));
   1082 }
   1083 
   1084 int
   1085 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
   1086 {
   1087 
   1088 	STDCALL(ukfs, rump_sys_lchmod(filename, mode));
   1089 }
   1090 
   1091 int
   1092 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
   1093 {
   1094 
   1095 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
   1096 }
   1097 
   1098 int
   1099 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
   1100 {
   1101 
   1102 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
   1103 }
   1104 
   1105 int
   1106 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
   1107 {
   1108 
   1109 	STDCALL(ukfs, rump_sys_chflags(filename, flags));
   1110 }
   1111 
   1112 int
   1113 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
   1114 {
   1115 
   1116 	STDCALL(ukfs, rump_sys_lchflags(filename, flags));
   1117 }
   1118 
   1119 int
   1120 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
   1121 {
   1122 
   1123 	STDCALL(ukfs, rump_sys_utimes(filename, tptr));
   1124 }
   1125 
   1126 int
   1127 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
   1128 	      const struct timeval *tptr)
   1129 {
   1130 
   1131 	STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
   1132 }
   1133 
   1134 /*
   1135  * Dynamic module support
   1136  */
   1137 
   1138 /* load one library */
   1139 
   1140 /*
   1141  * XXX: the dlerror stuff isn't really threadsafe, but then again I
   1142  * can't protect against other threads calling dl*() outside of ukfs,
   1143  * so just live with it being flimsy
   1144  */
   1145 int
   1146 ukfs_modload(const char *fname)
   1147 {
   1148 	void *handle;
   1149 	const struct modinfo *const *mi_start, *const *mi_end;
   1150 	int error;
   1151 
   1152 	handle = dlopen(fname, RTLD_LAZY|RTLD_GLOBAL);
   1153 	if (handle == NULL) {
   1154 		const char *dlmsg = dlerror();
   1155 		if (strstr(dlmsg, "Undefined symbol"))
   1156 			return 0;
   1157 		warnx("dlopen %s failed: %s", fname, dlmsg);
   1158 		/* XXXerrno */
   1159 		return -1;
   1160 	}
   1161 
   1162 	mi_start = dlsym(handle, "__start_link_set_modules");
   1163 	mi_end = dlsym(handle, "__stop_link_set_modules");
   1164 	if (mi_start && mi_end) {
   1165 		error = rump_pub_module_init(mi_start,
   1166 		    (size_t)(mi_end-mi_start));
   1167 		if (error)
   1168 			goto errclose;
   1169 		return 1;
   1170 	}
   1171 	error = EINVAL;
   1172 
   1173  errclose:
   1174 	dlclose(handle);
   1175 	errno = error;
   1176 	return -1;
   1177 }
   1178 
   1179 struct loadfail {
   1180 	char *pname;
   1181 
   1182 	LIST_ENTRY(loadfail) entries;
   1183 };
   1184 
   1185 #define RUMPFSMOD_PREFIX "librumpfs_"
   1186 #define RUMPFSMOD_SUFFIX ".so"
   1187 
   1188 int
   1189 ukfs_modload_dir(const char *dir)
   1190 {
   1191 	char nbuf[MAXPATHLEN+1], *p;
   1192 	struct dirent entry, *result;
   1193 	DIR *libdir;
   1194 	struct loadfail *lf, *nlf;
   1195 	int error, nloaded = 0, redo;
   1196 	LIST_HEAD(, loadfail) lfs;
   1197 
   1198 	libdir = opendir(dir);
   1199 	if (libdir == NULL)
   1200 		return -1;
   1201 
   1202 	LIST_INIT(&lfs);
   1203 	for (;;) {
   1204 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
   1205 			break;
   1206 		if (!result)
   1207 			break;
   1208 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
   1209 		    strlen(RUMPFSMOD_PREFIX)) != 0)
   1210 			continue;
   1211 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
   1212 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
   1213 			continue;
   1214 		strlcpy(nbuf, dir, sizeof(nbuf));
   1215 		strlcat(nbuf, "/", sizeof(nbuf));
   1216 		strlcat(nbuf, result->d_name, sizeof(nbuf));
   1217 		switch (ukfs_modload(nbuf)) {
   1218 		case 0:
   1219 			lf = malloc(sizeof(*lf));
   1220 			if (lf == NULL) {
   1221 				error = ENOMEM;
   1222 				break;
   1223 			}
   1224 			lf->pname = strdup(nbuf);
   1225 			if (lf->pname == NULL) {
   1226 				free(lf);
   1227 				error = ENOMEM;
   1228 				break;
   1229 			}
   1230 			LIST_INSERT_HEAD(&lfs, lf, entries);
   1231 			break;
   1232 		case 1:
   1233 			nloaded++;
   1234 			break;
   1235 		default:
   1236 			/* ignore errors */
   1237 			break;
   1238 		}
   1239 	}
   1240 	closedir(libdir);
   1241 	if (error && nloaded != 0)
   1242 		error = 0;
   1243 
   1244 	/*
   1245 	 * El-cheapo dependency calculator.  Just try to load the
   1246 	 * modules n times in a loop
   1247 	 */
   1248 	for (redo = 1; redo;) {
   1249 		redo = 0;
   1250 		nlf = LIST_FIRST(&lfs);
   1251 		while ((lf = nlf) != NULL) {
   1252 			nlf = LIST_NEXT(lf, entries);
   1253 			if (ukfs_modload(lf->pname) == 1) {
   1254 				nloaded++;
   1255 				redo = 1;
   1256 				LIST_REMOVE(lf, entries);
   1257 				free(lf->pname);
   1258 				free(lf);
   1259 			}
   1260 		}
   1261 	}
   1262 
   1263 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
   1264 		LIST_REMOVE(lf, entries);
   1265 		free(lf->pname);
   1266 		free(lf);
   1267 	}
   1268 
   1269 	if (error && nloaded == 0) {
   1270 		errno = error;
   1271 		return -1;
   1272 	}
   1273 
   1274 	return nloaded;
   1275 }
   1276 
   1277 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
   1278 ssize_t
   1279 ukfs_vfstypes(char *buf, size_t buflen)
   1280 {
   1281 	int mib[3];
   1282 	struct sysctlnode q, ans[128];
   1283 	size_t alen;
   1284 	int i;
   1285 
   1286 	mib[0] = CTL_VFS;
   1287 	mib[1] = VFS_GENERIC;
   1288 	mib[2] = CTL_QUERY;
   1289 	alen = sizeof(ans);
   1290 
   1291 	memset(&q, 0, sizeof(q));
   1292 	q.sysctl_flags = SYSCTL_VERSION;
   1293 
   1294 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
   1295 		return -1;
   1296 	}
   1297 
   1298 	for (i = 0; i < alen/sizeof(ans[0]); i++)
   1299 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
   1300 			break;
   1301 	if (i == alen/sizeof(ans[0])) {
   1302 		errno = ENXIO;
   1303 		return -1;
   1304 	}
   1305 
   1306 	mib[0] = CTL_VFS;
   1307 	mib[1] = VFS_GENERIC;
   1308 	mib[2] = ans[i].sysctl_num;
   1309 
   1310 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
   1311 		return -1;
   1312 	}
   1313 
   1314 	return buflen;
   1315 }
   1316 
   1317 /*
   1318  * Utilities
   1319  */
   1320 static int
   1321 builddirs(const char *pathname, mode_t mode,
   1322 	int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs)
   1323 {
   1324 	char *f1, *f2;
   1325 	int rv;
   1326 	mode_t mask;
   1327 	bool end;
   1328 
   1329 	/*ukfs_umask((mask = ukfs_umask(0)));*/
   1330 	umask((mask = umask(0)));
   1331 
   1332 	f1 = f2 = strdup(pathname);
   1333 	if (f1 == NULL) {
   1334 		errno = ENOMEM;
   1335 		return -1;
   1336 	}
   1337 
   1338 	end = false;
   1339 	for (;;) {
   1340 		/* find next component */
   1341 		f2 += strspn(f2, "/");
   1342 		f2 += strcspn(f2, "/");
   1343 		if (*f2 == '\0')
   1344 			end = true;
   1345 		else
   1346 			*f2 = '\0';
   1347 
   1348 		rv = mkdirfn(fs, f1, mode & ~mask);
   1349 		if (errno == EEXIST)
   1350 			rv = 0;
   1351 
   1352 		if (rv == -1 || *f2 != '\0' || end)
   1353 			break;
   1354 
   1355 		*f2 = '/';
   1356 	}
   1357 
   1358 	free(f1);
   1359 
   1360 	return rv;
   1361 }
   1362 
   1363 int
   1364 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
   1365 {
   1366 
   1367 	return builddirs(pathname, mode, ukfs_mkdir, ukfs);
   1368 }
   1369