xref: /netbsd-src/lib/libukfs/ukfs.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: ukfs.c,v 1.18 2009/01/11 02:47:19 christos 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 <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 #define UKFS_MODE_DEFAULT 0555
67 
68 struct ukfs {
69 	struct mount *ukfs_mp;
70 	struct vnode *ukfs_rvp;
71 
72 	pthread_spinlock_t ukfs_spin;
73 	pid_t ukfs_nextpid;
74 	struct vnode *ukfs_cdir;
75 	int ukfs_devfd;
76 };
77 
78 struct mount *
79 ukfs_getmp(struct ukfs *ukfs)
80 {
81 
82 	return ukfs->ukfs_mp;
83 }
84 
85 struct vnode *
86 ukfs_getrvp(struct ukfs *ukfs)
87 {
88 	struct vnode *rvp;
89 
90 	rvp = ukfs->ukfs_rvp;
91 	rump_vp_incref(rvp);
92 
93 	return rvp;
94 }
95 
96 static pid_t
97 nextpid(struct ukfs *ukfs)
98 {
99 	pid_t npid;
100 
101 	pthread_spin_lock(&ukfs->ukfs_spin);
102 	if (ukfs->ukfs_nextpid == 0)
103 		ukfs->ukfs_nextpid++;
104 	npid = ukfs->ukfs_nextpid++;
105 	pthread_spin_unlock(&ukfs->ukfs_spin);
106 
107 	return npid;
108 }
109 
110 static void
111 precall(struct ukfs *ukfs)
112 {
113 	struct vnode *rvp, *cvp;
114 
115 	rump_setup_curlwp(nextpid(ukfs), 1, 1);
116 	rvp = ukfs_getrvp(ukfs);
117 	pthread_spin_lock(&ukfs->ukfs_spin);
118 	cvp = ukfs->ukfs_cdir;
119 	pthread_spin_unlock(&ukfs->ukfs_spin);
120 	rump_rcvp_set(rvp, cvp); /* takes refs */
121 	rump_vp_rele(rvp);
122 }
123 
124 static void
125 postcall(struct ukfs *ukfs)
126 {
127 	struct vnode *rvp;
128 
129 	rvp = ukfs_getrvp(ukfs);
130 	rump_rcvp_set(NULL, rvp);
131 	rump_vp_rele(rvp);
132 	rump_clear_curlwp();
133 }
134 
135 int
136 _ukfs_init(int version)
137 {
138 	int rv;
139 
140 	if (version != UKFS_VERSION) {
141 		printf("incompatible ukfs version, %d vs. %d\n",
142 		    version, UKFS_VERSION);
143 		errno = EPROGMISMATCH;
144 		return -1;
145 	}
146 
147 	if ((rv = rump_init()) != 0) {
148 		errno = rv;
149 		return -1;
150 	}
151 
152 	return 0;
153 }
154 
155 struct ukfs *
156 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath,
157 	int mntflags, void *arg, size_t alen)
158 {
159 	struct ukfs *fs = NULL;
160 	struct vfsops *vfsops;
161 	struct mount *mp = NULL;
162 	int rv = 0, devfd = -1, rdonly;
163 
164 	vfsops = rump_vfs_getopsbyname(vfsname);
165 	if (vfsops == NULL) {
166 		rv = ENODEV;
167 		goto out;
168 	}
169 
170 	/*
171 	 * Try open and lock the device.  if we can't open it, assume
172 	 * it's a file system which doesn't use a real device and let
173 	 * it slide.  The mount will fail anyway if the fs requires a
174 	 * device.
175 	 *
176 	 * XXX: strictly speaking this is not 100% correct, as virtual
177 	 * file systems can use a device path which does exist and can
178 	 * be opened.  E.g. tmpfs should be mountable multiple times
179 	 * with "device" path "/swap", but now isn't.  But I think the
180 	 * chances are so low that it's currently acceptable to let
181 	 * this one slip.
182 	 */
183 	rdonly = mntflags & MNT_RDONLY;
184 	devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR);
185 	if (devfd != -1) {
186 		if (flock(devfd, LOCK_NB | (rdonly ? LOCK_SH:LOCK_EX)) == -1) {
187 			warnx("ukfs_mount: cannot get %s lock on device",
188 			    rdonly ? "shared" : "exclusive");
189 			close(devfd);
190 			devfd = -1;
191 			rv = errno;
192 			goto out;
193 		}
194 	}
195 
196 	fs = malloc(sizeof(struct ukfs));
197 	if (fs == NULL) {
198 		rv = ENOMEM;
199 		goto out;
200 	}
201 	memset(fs, 0, sizeof(struct ukfs));
202 	mp = rump_mnt_init(vfsops, mntflags);
203 
204 	rump_fakeblk_register(devpath);
205 	rv = rump_mnt_mount(mp, mountpath, arg, &alen);
206 	rump_fakeblk_deregister(devpath);
207 	if (rv) {
208 		goto out;
209 	}
210 	rv = rump_vfs_root(mp, &fs->ukfs_rvp, 0);
211 	if (rv) {
212 		goto out;
213 	}
214 	fs->ukfs_cdir = ukfs_getrvp(fs);
215 
216 	fs->ukfs_mp = mp;
217 	pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
218 	fs->ukfs_devfd = devfd;
219 	assert(rv == 0);
220 
221  out:
222 	if (rv) {
223 		if (mp)
224 			rump_mnt_destroy(mp);
225 		if (fs)
226 			free(fs);
227 		errno = rv;
228 		fs = NULL;
229 		if (devfd != -1) {
230 			flock(devfd, LOCK_UN);
231 			close(devfd);
232 		}
233 	}
234 
235 	return fs;
236 }
237 
238 void
239 ukfs_release(struct ukfs *fs, int flags)
240 {
241 	int rv;
242 
243 	if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
244 		kauth_cred_t cred;
245 
246 		rump_vp_rele(fs->ukfs_cdir);
247 		cred = rump_cred_suserget();
248 		rv = rump_vfs_sync(fs->ukfs_mp, 1, cred);
249 		rump_cred_suserput(cred);
250 		rump_vp_recycle_nokidding(ukfs_getrvp(fs));
251 		rv |= rump_vfs_unmount(fs->ukfs_mp, 0);
252 		assert(rv == 0);
253 	}
254 
255 	rump_vfs_syncwait(fs->ukfs_mp);
256 	rump_mnt_destroy(fs->ukfs_mp);
257 
258 	pthread_spin_destroy(&fs->ukfs_spin);
259 	if (fs->ukfs_devfd != -1) {
260 		flock(fs->ukfs_devfd, LOCK_UN);
261 		close(fs->ukfs_devfd);
262 	}
263 	free(fs);
264 }
265 
266 #define STDCALL(ukfs, thecall)						\
267 	int rv = 0;							\
268 									\
269 	precall(ukfs);							\
270 	thecall;							\
271 	postcall(ukfs);							\
272 	if (rv) {							\
273 		errno = rv;						\
274 		return -1;						\
275 	}								\
276 	return 0;
277 
278 int
279 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
280 	uint8_t *buf, size_t bufsize)
281 {
282 	struct uio *uio;
283 	struct vnode *vp;
284 	size_t resid;
285 	kauth_cred_t cred;
286 	int rv, eofflag;
287 
288 	precall(ukfs);
289 	rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
290 	    NULL, &vp, NULL);
291 	postcall(ukfs);
292 	if (rv)
293 		goto out;
294 
295 	uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
296 	cred = rump_cred_suserget();
297 	rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
298 	rump_cred_suserput(cred);
299 	RUMP_VOP_UNLOCK(vp, 0);
300 	*off = rump_uio_getoff(uio);
301 	resid = rump_uio_free(uio);
302 	rump_vp_rele(vp);
303 
304  out:
305 	if (rv) {
306 		errno = rv;
307 		return -1;
308 	}
309 
310 	/* LINTED: not totally correct return type, but follows syscall */
311 	return bufsize - resid;
312 }
313 
314 ssize_t
315 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
316 	uint8_t *buf, size_t bufsize)
317 {
318 	int fd, rv = 0, dummy;
319 	ssize_t xfer = -1; /* XXXgcc */
320 
321 	precall(ukfs);
322 	fd = rump_sys_open(filename, RUMP_O_RDONLY, 0, &rv);
323 	if (rv)
324 		goto out;
325 
326 	xfer = rump_sys_pread(fd, buf, bufsize, 0, off, &rv);
327 	rump_sys_close(fd, &dummy);
328 
329  out:
330 	postcall(ukfs);
331 	if (rv) {
332 		errno = rv;
333 		return -1;
334 	}
335 	return xfer;
336 }
337 
338 ssize_t
339 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
340 	uint8_t *buf, size_t bufsize)
341 {
342 	int fd, rv = 0, dummy;
343 	ssize_t xfer = -1; /* XXXgcc */
344 
345 	precall(ukfs);
346 	fd = rump_sys_open(filename, RUMP_O_WRONLY, 0, &rv);
347 	if (rv)
348 		goto out;
349 
350 	/* write and commit */
351 	xfer = rump_sys_pwrite(fd, buf, bufsize, 0, off, &rv);
352 	if (rv == 0)
353 		rump_sys_fsync(fd, &dummy);
354 
355 	rump_sys_close(fd, &dummy);
356 
357  out:
358 	postcall(ukfs);
359 	if (rv) {
360 		errno = rv;
361 		return -1;
362 	}
363 	return xfer;
364 }
365 
366 int
367 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
368 {
369 	int rv, fd, dummy;
370 
371 	precall(ukfs);
372 	fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode, &rv);
373 	rump_sys_close(fd, &dummy);
374 
375 	postcall(ukfs);
376 	if (rv) {
377 		errno = rv;
378 		return -1;
379 	}
380 	return 0;
381 }
382 
383 int
384 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
385 {
386 
387 	STDCALL(ukfs, rump_sys___mknod50(path, mode, dev, &rv));
388 }
389 
390 int
391 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
392 {
393 
394 	STDCALL(ukfs, rump_sys_mkfifo(path, mode, &rv));
395 }
396 
397 int
398 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
399 {
400 
401 	STDCALL(ukfs, rump_sys_mkdir(filename, mode, &rv));
402 }
403 
404 int
405 ukfs_remove(struct ukfs *ukfs, const char *filename)
406 {
407 
408 	STDCALL(ukfs, rump_sys_unlink(filename, &rv));
409 }
410 
411 int
412 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
413 {
414 
415 	STDCALL(ukfs, rump_sys_rmdir(filename, &rv));
416 }
417 
418 int
419 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
420 {
421 
422 	STDCALL(ukfs, rump_sys_link(filename, f_create, &rv));
423 }
424 
425 int
426 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
427 {
428 
429 	STDCALL(ukfs, rump_sys_symlink(filename, linkname, &rv));
430 }
431 
432 ssize_t
433 ukfs_readlink(struct ukfs *ukfs, const char *filename,
434 	char *linkbuf, size_t buflen)
435 {
436 	ssize_t rv;
437 	int myerr = 0;
438 
439 	precall(ukfs);
440 	rv = rump_sys_readlink(filename, linkbuf, buflen, &myerr);
441 	postcall(ukfs);
442 	if (myerr) {
443 		errno = myerr;
444 		return -1;
445 	}
446 	return rv;
447 }
448 
449 int
450 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
451 {
452 
453 	STDCALL(ukfs, rump_sys_rename(from, to, &rv));
454 }
455 
456 int
457 ukfs_chdir(struct ukfs *ukfs, const char *path)
458 {
459 	struct vnode *newvp, *oldvp;
460 	int rv;
461 
462 	precall(ukfs);
463 	rump_sys_chdir(path, &rv);
464 	if (rv)
465 		goto out;
466 
467 	newvp = rump_cdir_get();
468 	pthread_spin_lock(&ukfs->ukfs_spin);
469 	oldvp = ukfs->ukfs_cdir;
470 	ukfs->ukfs_cdir = newvp;
471 	pthread_spin_unlock(&ukfs->ukfs_spin);
472 	if (oldvp)
473 		rump_vp_rele(oldvp);
474 
475  out:
476 	postcall(ukfs);
477 	if (rv) {
478 		errno = rv;
479 		return -1;
480 	}
481 	return 0;
482 }
483 
484 int
485 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
486 {
487 
488 	STDCALL(ukfs, rump_sys___stat50(filename, file_stat, &rv));
489 }
490 
491 int
492 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
493 {
494 
495 	STDCALL(ukfs, rump_sys___lstat50(filename, file_stat, &rv));
496 }
497 
498 int
499 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
500 {
501 
502 	STDCALL(ukfs, rump_sys_chmod(filename, mode, &rv));
503 }
504 
505 int
506 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
507 {
508 
509 	STDCALL(ukfs, rump_sys_lchmod(filename, mode, &rv));
510 }
511 
512 int
513 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
514 {
515 
516 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid, &rv));
517 }
518 
519 int
520 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
521 {
522 
523 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid, &rv));
524 }
525 
526 int
527 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
528 {
529 
530 	STDCALL(ukfs, rump_sys_chflags(filename, flags, &rv));
531 }
532 
533 int
534 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
535 {
536 
537 	STDCALL(ukfs, rump_sys_lchflags(filename, flags, &rv));
538 }
539 
540 int
541 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
542 {
543 
544 	STDCALL(ukfs, rump_sys___utimes50(filename, tptr, &rv));
545 }
546 
547 int
548 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
549 	      const struct timeval *tptr)
550 {
551 
552 	STDCALL(ukfs, rump_sys___lutimes50(filename, tptr, &rv));
553 }
554 
555 /*
556  * Dynamic module support
557  */
558 
559 /* load one library */
560 
561 /*
562  * XXX: the dlerror stuff isn't really threadsafe, but then again I
563  * can't protect against other threads calling dl*() outside of ukfs,
564  * so just live with it being flimsy
565  */
566 #define UFSLIB "librumpfs_ufs.so"
567 int
568 ukfs_modload(const char *fname)
569 {
570 	void *handle, *thesym;
571 	struct stat sb;
572 	const char *p;
573 	int error;
574 
575 	if (stat(fname, &sb) == -1)
576 		return -1;
577 
578 	handle = dlopen(fname, RTLD_GLOBAL);
579 	if (handle == NULL) {
580 		const char *dlmsg = dlerror();
581 		if (strstr(dlmsg, "Undefined symbol"))
582 			return 0;
583 		warnx("dlopen %s failed: %s\n", fname, dlmsg);
584 		/* XXXerrno */
585 		return -1;
586 	}
587 
588 	/*
589 	 * XXX: the ufs module is not loaded in the same fashion as the
590 	 * others.  But we can't do dlclose() for it, since that would
591 	 * lead to not being able to load ffs/ext2fs/lfs.  Hence hardcode
592 	 * and kludge around the issue for now.  But this should really
593 	 * be fixed by fixing sys/ufs/ufs to be a kernel module.
594 	 */
595 	if ((p = strrchr(fname, '/')) != NULL)
596 		p++;
597 	else
598 		p = fname;
599 	if (strcmp(p, UFSLIB) == 0)
600 		return 1;
601 
602 	thesym = dlsym(handle, "__start_link_set_modules");
603 	if (thesym) {
604 		error = rump_module_load(thesym);
605 		if (error)
606 			goto errclose;
607 		return 1;
608 	}
609 	error = EINVAL;
610 
611  errclose:
612 	dlclose(handle);
613 	errno = error;
614 	return -1;
615 }
616 
617 struct loadfail {
618 	char *pname;
619 
620 	LIST_ENTRY(loadfail) entries;
621 };
622 
623 #define RUMPFSMOD_PREFIX "librumpfs_"
624 #define RUMPFSMOD_SUFFIX ".so"
625 
626 int
627 ukfs_modload_dir(const char *dir)
628 {
629 	char nbuf[MAXPATHLEN+1], *p;
630 	struct dirent entry, *result;
631 	DIR *libdir;
632 	struct loadfail *lf, *nlf;
633 	int error, nloaded = 0, redo;
634 	LIST_HEAD(, loadfail) lfs;
635 
636 	libdir = opendir(dir);
637 	if (libdir == NULL)
638 		return -1;
639 
640 	LIST_INIT(&lfs);
641 	for (;;) {
642 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
643 			break;
644 		if (!result)
645 			break;
646 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
647 		    strlen(RUMPFSMOD_PREFIX)) != 0)
648 			continue;
649 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
650 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
651 			continue;
652 		strlcpy(nbuf, dir, sizeof(nbuf));
653 		strlcat(nbuf, "/", sizeof(nbuf));
654 		strlcat(nbuf, result->d_name, sizeof(nbuf));
655 		switch (ukfs_modload(nbuf)) {
656 		case 0:
657 			lf = malloc(sizeof(*lf));
658 			if (lf == NULL) {
659 				error = ENOMEM;
660 				break;
661 			}
662 			lf->pname = strdup(nbuf);
663 			if (lf->pname == NULL) {
664 				free(lf);
665 				error = ENOMEM;
666 				break;
667 			}
668 			LIST_INSERT_HEAD(&lfs, lf, entries);
669 			break;
670 		case 1:
671 			nloaded++;
672 			break;
673 		default:
674 			/* ignore errors */
675 			break;
676 		}
677 	}
678 	closedir(libdir);
679 	if (error && nloaded != 0)
680 		error = 0;
681 
682 	/*
683 	 * El-cheapo dependency calculator.  Just try to load the
684 	 * modules n times in a loop
685 	 */
686 	for (redo = 1; redo;) {
687 		redo = 0;
688 		nlf = LIST_FIRST(&lfs);
689 		while ((lf = nlf) != NULL) {
690 			nlf = LIST_NEXT(lf, entries);
691 			if (ukfs_modload(lf->pname) == 1) {
692 				nloaded++;
693 				redo = 1;
694 				LIST_REMOVE(lf, entries);
695 				free(lf->pname);
696 				free(lf);
697 			}
698 		}
699 	}
700 
701 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
702 		LIST_REMOVE(lf, entries);
703 		free(lf->pname);
704 		free(lf);
705 	}
706 
707 	if (error && nloaded == 0) {
708 		errno = error;
709 		return -1;
710 	}
711 
712 	return nloaded;
713 }
714 
715 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
716 ssize_t
717 ukfs_vfstypes(char *buf, size_t buflen)
718 {
719 	int mib[3];
720 	struct sysctlnode q, ans[128];
721 	size_t alen;
722 	int error, i;
723 
724 	mib[0] = CTL_VFS;
725 	mib[1] = VFS_GENERIC;
726 	mib[2] = CTL_QUERY;
727 	alen = sizeof(ans);
728 
729 	memset(&q, 0, sizeof(q));
730 	q.sysctl_flags = SYSCTL_VERSION;
731 
732 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q), &error) == -1){
733 		errno = error;
734 		return -1;
735 	}
736 
737 	for (i = 0; i < alen/sizeof(ans[0]); i++)
738 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
739 			break;
740 	if (i == alen/sizeof(ans[0])) {
741 		errno = ENXIO;
742 		return -1;
743 	}
744 
745 	mib[0] = CTL_VFS;
746 	mib[1] = VFS_GENERIC;
747 	mib[2] = ans[i].sysctl_num;
748 
749 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0, &error) == -1) {
750 		errno = error;
751 		return -1;
752 	}
753 
754 	return buflen;
755 }
756 
757 /*
758  * Utilities
759  */
760 int
761 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
762 {
763 	char *f1, *f2;
764 	int rv;
765 	mode_t mask;
766 	bool end;
767 
768 	/*ukfs_umask((mask = ukfs_umask(0)));*/
769 	umask((mask = umask(0)));
770 
771 	f1 = f2 = strdup(pathname);
772 	if (f1 == NULL) {
773 		errno = ENOMEM;
774 		return -1;
775 	}
776 
777 	end = false;
778 	for (;;) {
779 		/* find next component */
780 		f2 += strspn(f2, "/");
781 		f2 += strcspn(f2, "/");
782 		if (*f2 == '\0')
783 			end = true;
784 		else
785 			*f2 = '\0';
786 
787 		rv = ukfs_mkdir(ukfs, f1, mode & ~mask);
788 		if (errno == EEXIST)
789 			rv = 0;
790 
791 		if (rv == -1 || *f2 != '\0' || end)
792 			break;
793 
794 		*f2 = '/';
795 	}
796 
797 	free(f1);
798 
799 	return rv;
800 }
801