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