Home | History | Annotate | Line # | Download | only in libukfs
ukfs.c revision 1.5
      1 /*	$NetBSD: ukfs.c,v 1.5 2008/08/08 14:16:24 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2007, 2008  Antti Kantee.  All Rights Reserved.
      5  *
      6  * Development of this software was supported by the
      7  * Finnish Cultural Foundation.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  * SUCH DAMAGE.
     29  */
     30 
     31 /*
     32  * This library enables access to files systems directly without
     33  * involving system calls.
     34  */
     35 
     36 #ifdef __linux__
     37 #define _XOPEN_SOURCE 500
     38 #define _BSD_SOURCE
     39 #define _FILE_OFFSET_BITS 64
     40 #endif
     41 
     42 #include <sys/param.h>
     43 #include <sys/queue.h>
     44 #include <sys/stat.h>
     45 #include <sys/sysctl.h>
     46 #include <sys/mount.h>
     47 
     48 #include <assert.h>
     49 #include <dirent.h>
     50 #include <dlfcn.h>
     51 #include <err.h>
     52 #include <errno.h>
     53 #include <pthread.h>
     54 #include <stdio.h>
     55 #include <stdlib.h>
     56 #include <string.h>
     57 #include <unistd.h>
     58 #include <stdint.h>
     59 
     60 #include <rump/ukfs.h>
     61 
     62 #include <rump/rump.h>
     63 #include <rump/rump_syscalls.h>
     64 
     65 #define UKFS_MODE_DEFAULT 0555
     66 
     67 struct ukfs {
     68 	struct mount *ukfs_mp;
     69 	struct vnode *ukfs_rvp;
     70 
     71 	pthread_spinlock_t ukfs_spin;
     72 	pid_t ukfs_nextpid;
     73 	struct vnode *ukfs_cdir;
     74 };
     75 
     76 struct mount *
     77 ukfs_getmp(struct ukfs *ukfs)
     78 {
     79 
     80 	return ukfs->ukfs_mp;
     81 }
     82 
     83 struct vnode *
     84 ukfs_getrvp(struct ukfs *ukfs)
     85 {
     86 	struct vnode *rvp;
     87 
     88 	rvp = ukfs->ukfs_rvp;
     89 	rump_vp_incref(rvp);
     90 
     91 	return rvp;
     92 }
     93 
     94 static pid_t
     95 nextpid(struct ukfs *ukfs)
     96 {
     97 	pid_t npid;
     98 
     99 	pthread_spin_lock(&ukfs->ukfs_spin);
    100 	if (ukfs->ukfs_nextpid == 0)
    101 		ukfs->ukfs_nextpid++;
    102 	npid = ukfs->ukfs_nextpid++;
    103 	pthread_spin_unlock(&ukfs->ukfs_spin);
    104 
    105 	return npid;
    106 }
    107 
    108 static void
    109 precall(struct ukfs *ukfs)
    110 {
    111 	struct vnode *rvp, *cvp;
    112 
    113 	rump_setup_curlwp(nextpid(ukfs), 1, 1);
    114 	rvp = ukfs_getrvp(ukfs);
    115 	pthread_spin_lock(&ukfs->ukfs_spin);
    116 	cvp = ukfs->ukfs_cdir;
    117 	pthread_spin_unlock(&ukfs->ukfs_spin);
    118 	rump_rcvp_set(rvp, cvp); /* takes refs */
    119 	rump_vp_rele(rvp);
    120 }
    121 
    122 static void
    123 postcall(struct ukfs *ukfs)
    124 {
    125 	struct vnode *rvp;
    126 
    127 	rvp = ukfs_getrvp(ukfs);
    128 	rump_rcvp_set(NULL, rvp);
    129 	rump_vp_rele(rvp);
    130 	rump_clear_curlwp();
    131 }
    132 
    133 int
    134 ukfs_init()
    135 {
    136 
    137 	rump_init();
    138 
    139 	return 0;
    140 }
    141 
    142 struct ukfs *
    143 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath,
    144 	int mntflags, void *arg, size_t alen)
    145 {
    146 	struct ukfs *fs = NULL;
    147 	struct vfsops *vfsops;
    148 	struct mount *mp;
    149 	int rv = 0;
    150 
    151 	vfsops = rump_vfs_getopsbyname(vfsname);
    152 	if (vfsops == NULL) {
    153 		rv = ENOENT;
    154 		goto out;
    155 	}
    156 
    157 	mp = rump_mnt_init(vfsops, mntflags);
    158 
    159 	fs = malloc(sizeof(struct ukfs));
    160 	if (fs == NULL) {
    161 		rv = ENOMEM;
    162 		goto out;
    163 	}
    164 	memset(fs, 0, sizeof(struct ukfs));
    165 	pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
    166 
    167 	rump_fakeblk_register(devpath);
    168 	rv = rump_mnt_mount(mp, mountpath, arg, &alen);
    169 	rump_fakeblk_deregister(devpath);
    170 	if (rv) {
    171 		warnx("VFS_MOUNT %d", rv);
    172 		goto out;
    173 	}
    174 	fs->ukfs_mp = mp;
    175 
    176 	rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
    177 	fs->ukfs_cdir = ukfs_getrvp(fs);
    178 
    179  out:
    180 	if (rv) {
    181 		if (fs && fs->ukfs_mp)
    182 			rump_mnt_destroy(fs->ukfs_mp);
    183 		if (fs)
    184 			free(fs);
    185 		errno = rv;
    186 		fs = NULL;
    187 	}
    188 
    189 	return fs;
    190 }
    191 
    192 void
    193 ukfs_release(struct ukfs *fs, int flags)
    194 {
    195 	int rv;
    196 
    197 	if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
    198 		rump_vp_rele(fs->ukfs_cdir);
    199 		rv = rump_vfs_sync(fs->ukfs_mp, 1, NULL);
    200 		rump_vp_recycle_nokidding(ukfs_getrvp(fs));
    201 		rv |= rump_vfs_unmount(fs->ukfs_mp, 0);
    202 		assert(rv == 0);
    203 	}
    204 
    205 	rump_vfs_syncwait(fs->ukfs_mp);
    206 	rump_mnt_destroy(fs->ukfs_mp);
    207 
    208 	pthread_spin_destroy(&fs->ukfs_spin);
    209 	free(fs);
    210 }
    211 
    212 /* don't need vn_lock(), since we don't have VXLOCK */
    213 #define VLE(a) rump_vp_lock_exclusive(a)
    214 #define VLS(a) rump_vp_lock_shared(a)
    215 #define VUL(a) rump_vp_unlock(a)
    216 #define AUL(a) assert(rump_vp_islocked(a) == 0)
    217 
    218 #define STDCALL(ukfs, thecall)						\
    219 	int rv = 0;							\
    220 									\
    221 	precall(ukfs);							\
    222 	thecall;							\
    223 	postcall(ukfs);							\
    224 	if (rv) {							\
    225 		errno = rv;						\
    226 		return -1;						\
    227 	}								\
    228 	return 0;
    229 
    230 int
    231 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
    232 	uint8_t *buf, size_t bufsize)
    233 {
    234 	struct uio *uio;
    235 	struct vnode *vp;
    236 	size_t resid;
    237 	int rv, eofflag;
    238 
    239 	precall(ukfs);
    240 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
    241 	    NULL, &vp, NULL);
    242 	postcall(ukfs);
    243 	if (rv)
    244 		goto out;
    245 
    246 	uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
    247 	rv = RUMP_VOP_READDIR(vp, uio, NULL, &eofflag, NULL, NULL);
    248 	VUL(vp);
    249 	*off = rump_uio_getoff(uio);
    250 	resid = rump_uio_free(uio);
    251 	rump_vp_rele(vp);
    252 
    253  out:
    254 	if (rv) {
    255 		errno = rv;
    256 		return -1;
    257 	}
    258 
    259 	/* LINTED: not totally correct return type, but follows syscall */
    260 	return bufsize - resid;
    261 }
    262 
    263 ssize_t
    264 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
    265 	uint8_t *buf, size_t bufsize)
    266 {
    267 	int fd, rv = 0, dummy;
    268 	ssize_t xfer = -1; /* XXXgcc */
    269 
    270 	precall(ukfs);
    271 	fd = rump_sys_open(filename, RUMP_O_RDONLY, 0, &rv);
    272 	if (rv)
    273 		goto out;
    274 
    275 	xfer = rump_sys_pread(fd, buf, bufsize, 0, off, &rv);
    276 	rump_sys_close(fd, &dummy);
    277 
    278  out:
    279 	postcall(ukfs);
    280 	if (rv) {
    281 		errno = rv;
    282 		return -1;
    283 	}
    284 	return xfer;
    285 }
    286 
    287 ssize_t
    288 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
    289 	uint8_t *buf, size_t bufsize)
    290 {
    291 	int fd, rv = 0, dummy;
    292 	ssize_t xfer = -1; /* XXXgcc */
    293 
    294 	precall(ukfs);
    295 	fd = rump_sys_open(filename, RUMP_O_WRONLY, 0, &rv);
    296 	if (rv)
    297 		goto out;
    298 
    299 	/* write and commit */
    300 	xfer = rump_sys_pwrite(fd, buf, bufsize, 0, off, &rv);
    301 	if (rv == 0)
    302 		rump_sys_fsync(fd, &dummy);
    303 
    304 	rump_sys_close(fd, &dummy);
    305 
    306  out:
    307 	postcall(ukfs);
    308 	if (rv) {
    309 		errno = rv;
    310 		return -1;
    311 	}
    312 	return xfer;
    313 }
    314 
    315 int
    316 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
    317 {
    318 	int rv, fd, dummy;
    319 
    320 	precall(ukfs);
    321 	fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode, &rv);
    322 	rump_sys_close(fd, &dummy);
    323 
    324 	postcall(ukfs);
    325 	if (rv) {
    326 		errno = rv;
    327 		return -1;
    328 	}
    329 	return 0;
    330 }
    331 
    332 int
    333 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
    334 {
    335 
    336 	STDCALL(ukfs, rump_sys_mknod(path, mode, dev, &rv));
    337 }
    338 
    339 int
    340 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
    341 {
    342 
    343 	STDCALL(ukfs, rump_sys_mkfifo(path, mode, &rv));
    344 }
    345 
    346 int
    347 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
    348 {
    349 
    350 	STDCALL(ukfs, rump_sys_mkdir(filename, mode, &rv));
    351 }
    352 
    353 int
    354 ukfs_remove(struct ukfs *ukfs, const char *filename)
    355 {
    356 
    357 	STDCALL(ukfs, rump_sys_unlink(filename, &rv));
    358 }
    359 
    360 int
    361 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
    362 {
    363 
    364 	STDCALL(ukfs, rump_sys_rmdir(filename, &rv));
    365 }
    366 
    367 int
    368 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
    369 {
    370 
    371 	STDCALL(ukfs, rump_sys_link(filename, f_create, &rv));
    372 }
    373 
    374 int
    375 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
    376 {
    377 
    378 	STDCALL(ukfs, rump_sys_symlink(filename, linkname, &rv));
    379 }
    380 
    381 ssize_t
    382 ukfs_readlink(struct ukfs *ukfs, const char *filename,
    383 	char *linkbuf, size_t buflen)
    384 {
    385 	ssize_t rv;
    386 	int myerr = 0;
    387 
    388 	precall(ukfs);
    389 	rv = rump_sys_readlink(filename, linkbuf, buflen, &myerr);
    390 	postcall(ukfs);
    391 	if (myerr) {
    392 		errno = myerr;
    393 		return -1;
    394 	}
    395 	return rv;
    396 }
    397 
    398 int
    399 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
    400 {
    401 
    402 	STDCALL(ukfs, rump_sys_rename(from, to, &rv));
    403 }
    404 
    405 int
    406 ukfs_chdir(struct ukfs *ukfs, const char *path)
    407 {
    408 	struct vnode *newvp, *oldvp;
    409 	int rv;
    410 
    411 	precall(ukfs);
    412 	rump_sys_chdir(path, &rv);
    413 	if (rv)
    414 		goto out;
    415 
    416 	newvp = rump_cdir_get();
    417 	pthread_spin_lock(&ukfs->ukfs_spin);
    418 	oldvp = ukfs->ukfs_cdir;
    419 	ukfs->ukfs_cdir = newvp;
    420 	pthread_spin_unlock(&ukfs->ukfs_spin);
    421 	if (oldvp)
    422 		rump_vp_rele(oldvp);
    423 
    424  out:
    425 	postcall(ukfs);
    426 	if (rv) {
    427 		errno = rv;
    428 		return -1;
    429 	}
    430 	return 0;
    431 }
    432 
    433 int
    434 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
    435 {
    436 
    437 	STDCALL(ukfs, rump_sys___stat30(filename, file_stat, &rv));
    438 }
    439 
    440 int
    441 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
    442 {
    443 
    444 	STDCALL(ukfs, rump_sys___lstat30(filename, file_stat, &rv));
    445 }
    446 
    447 int
    448 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
    449 {
    450 
    451 	STDCALL(ukfs, rump_sys_chmod(filename, mode, &rv));
    452 }
    453 
    454 int
    455 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
    456 {
    457 
    458 	STDCALL(ukfs, rump_sys_lchmod(filename, mode, &rv));
    459 }
    460 
    461 int
    462 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
    463 {
    464 
    465 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid, &rv));
    466 }
    467 
    468 int
    469 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
    470 {
    471 
    472 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid, &rv));
    473 }
    474 
    475 int
    476 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
    477 {
    478 
    479 	STDCALL(ukfs, rump_sys_chflags(filename, flags, &rv));
    480 }
    481 
    482 int
    483 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
    484 {
    485 
    486 	STDCALL(ukfs, rump_sys_lchflags(filename, flags, &rv));
    487 }
    488 
    489 int
    490 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
    491 {
    492 
    493 	STDCALL(ukfs, rump_sys_utimes(filename, tptr, &rv));
    494 }
    495 
    496 int
    497 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
    498 	      const struct timeval *tptr)
    499 {
    500 
    501 	STDCALL(ukfs, rump_sys_lutimes(filename, tptr, &rv));
    502 }
    503 
    504 /*
    505  * Dynamic module support
    506  */
    507 
    508 /* load one library */
    509 
    510 /*
    511  * XXX: the dlerror stuff isn't really threadsafe, but then again I
    512  * can't protect against other threads calling dl*() outside of ukfs,
    513  * so just live with it being flimsy
    514  */
    515 #define UFSLIB "librumpfs_ufs.so"
    516 int
    517 ukfs_modload(const char *fname)
    518 {
    519 	void *handle, *thesym;
    520 	struct stat sb;
    521 	const char *p;
    522 	int error;
    523 
    524 	if (stat(fname, &sb) == -1)
    525 		return -1;
    526 
    527 	handle = dlopen(fname, RTLD_GLOBAL);
    528 	if (handle == NULL) {
    529 		if (strstr(dlerror(), "Undefined symbol"))
    530 			return 0;
    531 		warnx("dlopen %s failed: %s\n", fname, dlerror());
    532 		/* XXXerrno */
    533 		return -1;
    534 	}
    535 
    536 	/*
    537 	 * XXX: the ufs module is not loaded in the same fashion as the
    538 	 * others.  But we can't do dlclose() for it, since that would
    539 	 * lead to not being able to load ffs/ext2fs/lfs.  Hence hardcode
    540 	 * and kludge around the issue for now.  But this should really
    541 	 * be fixed by fixing sys/ufs/ufs to be a kernel module.
    542 	 */
    543 	if ((p = strrchr(fname, '/')) != NULL)
    544 		p++;
    545 	else
    546 		p = fname;
    547 	if (strcmp(p, UFSLIB) == 0)
    548 		return 1;
    549 
    550 	thesym = dlsym(handle, "__start_link_set_modules");
    551 	if (thesym) {
    552 		error = rump_vfs_load(thesym);
    553 		if (error)
    554 			goto errclose;
    555 		return 1;
    556 	}
    557 	error = EINVAL;
    558 
    559  errclose:
    560 	dlclose(handle);
    561 	errno = error;
    562 	return -1;
    563 }
    564 
    565 struct loadfail {
    566 	char *pname;
    567 
    568 	LIST_ENTRY(loadfail) entries;
    569 };
    570 
    571 #define RUMPFSMOD_PREFIX "librumpfs_"
    572 #define RUMPFSMOD_SUFFIX ".so"
    573 
    574 int
    575 ukfs_modload_dir(const char *dir)
    576 {
    577 	char nbuf[MAXPATHLEN+1], *p;
    578 	struct dirent entry, *result;
    579 	DIR *libdir;
    580 	struct loadfail *lf, *nlf;
    581 	int error, nloaded = 0, redo;
    582 	LIST_HEAD(, loadfail) lfs;
    583 
    584 	libdir = opendir(dir);
    585 	if (libdir == NULL)
    586 		return -1;
    587 
    588 	LIST_INIT(&lfs);
    589 	for (;;) {
    590 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
    591 			break;
    592 		if (!result)
    593 			break;
    594 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
    595 		    strlen(RUMPFSMOD_PREFIX)) != 0)
    596 			continue;
    597 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
    598 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
    599 			continue;
    600 		strlcpy(nbuf, dir, sizeof(nbuf));
    601 		strlcat(nbuf, "/", sizeof(nbuf));
    602 		strlcat(nbuf, result->d_name, sizeof(nbuf));
    603 		switch (ukfs_modload(nbuf)) {
    604 		case 0:
    605 			lf = malloc(sizeof(*lf));
    606 			if (lf == NULL) {
    607 				error = ENOMEM;
    608 				break;
    609 			}
    610 			lf->pname = strdup(nbuf);
    611 			if (lf->pname == NULL) {
    612 				free(lf);
    613 				error = ENOMEM;
    614 				break;
    615 			}
    616 			LIST_INSERT_HEAD(&lfs, lf, entries);
    617 			break;
    618 		case 1:
    619 			nloaded++;
    620 			break;
    621 		default:
    622 			/* ignore errors */
    623 			break;
    624 		}
    625 	}
    626 	closedir(libdir);
    627 	if (error && nloaded != 0)
    628 		error = 0;
    629 
    630 	/*
    631 	 * El-cheapo dependency calculator.  Just try to load the
    632 	 * modules n times in a loop
    633 	 */
    634 	for (redo = 1; redo;) {
    635 		redo = 0;
    636 		nlf = LIST_FIRST(&lfs);
    637 		while ((lf = nlf) != NULL) {
    638 			nlf = LIST_NEXT(lf, entries);
    639 			if (ukfs_modload(lf->pname) == 1) {
    640 				nloaded++;
    641 				redo = 1;
    642 				LIST_REMOVE(lf, entries);
    643 				free(lf->pname);
    644 				free(lf);
    645 			}
    646 		}
    647 	}
    648 
    649 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
    650 		LIST_REMOVE(lf, entries);
    651 		free(lf->pname);
    652 		free(lf);
    653 	}
    654 
    655 	if (error && nloaded == 0) {
    656 		errno = error;
    657 		return -1;
    658 	}
    659 
    660 	return nloaded;
    661 }
    662 
    663 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
    664 ssize_t
    665 ukfs_vfstypes(char *buf, size_t buflen)
    666 {
    667 	int mib[3];
    668 	struct sysctlnode q, ans[128];
    669 	size_t alen;
    670 	int error, i;
    671 
    672 	mib[0] = CTL_VFS;
    673 	mib[1] = VFS_GENERIC;
    674 	mib[2] = CTL_QUERY;
    675 	alen = sizeof(ans);
    676 
    677 	memset(&q, 0, sizeof(q));
    678 	q.sysctl_flags = SYSCTL_VERSION;
    679 
    680 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q), &error) == -1){
    681 		errno = error;
    682 		return -1;
    683 	}
    684 
    685 	for (i = 0; i < alen/sizeof(ans[0]); i++)
    686 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
    687 			break;
    688 	if (i == alen/sizeof(ans[0])) {
    689 		errno = ENXIO;
    690 		return -1;
    691 	}
    692 
    693 	mib[0] = CTL_VFS;
    694 	mib[1] = VFS_GENERIC;
    695 	mib[2] = ans[i].sysctl_num;
    696 
    697 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0, &error) == -1) {
    698 		errno = error;
    699 		return -1;
    700 	}
    701 
    702 	return buflen;
    703 }
    704 
    705 /*
    706  * Utilities
    707  */
    708 int
    709 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
    710 {
    711 	char *f1, *f2;
    712 	int rv;
    713 	mode_t mask;
    714 	bool end;
    715 
    716 	/*ukfs_umask((mask = ukfs_umask(0)));*/
    717 	umask((mask = umask(0)));
    718 
    719 	f1 = f2 = strdup(pathname);
    720 	if (f1 == NULL) {
    721 		errno = ENOMEM;
    722 		return -1;
    723 	}
    724 
    725 	end = false;
    726 	for (;;) {
    727 		/* find next component */
    728 		f2 += strspn(f2, "/");
    729 		f2 += strcspn(f2, "/");
    730 		if (*f2 == '\0')
    731 			end = true;
    732 		else
    733 			*f2 = '\0';
    734 
    735 		rv = ukfs_mkdir(ukfs, f1, mode & ~mask);
    736 		if (errno == EEXIST)
    737 			rv = 0;
    738 
    739 		if (rv == -1 || *f2 != '\0' || end)
    740 			break;
    741 
    742 		*f2 = '/';
    743 	}
    744 
    745 	free(f1);
    746 
    747 	return rv;
    748 }
    749