ukfs.c revision 1.32 1 /* $NetBSD: ukfs.c,v 1.32 2009/07/23 01:01:31 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 char *ukfs_devpath;
77 char *ukfs_mountpath;
78 };
79
80 static int builddirs(const char *, mode_t,
81 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *);
82
83 struct mount *
84 ukfs_getmp(struct ukfs *ukfs)
85 {
86
87 return ukfs->ukfs_mp;
88 }
89
90 struct vnode *
91 ukfs_getrvp(struct ukfs *ukfs)
92 {
93 struct vnode *rvp;
94
95 rvp = ukfs->ukfs_rvp;
96 rump_vp_incref(rvp);
97
98 return rvp;
99 }
100
101 #ifdef DONT_WANT_PTHREAD_LINKAGE
102 #define pthread_spin_lock(a)
103 #define pthread_spin_unlock(a)
104 #define pthread_spin_init(a,b)
105 #define pthread_spin_destroy(a)
106 #endif
107
108 static pid_t
109 nextpid(struct ukfs *ukfs)
110 {
111 pid_t npid;
112
113 pthread_spin_lock(&ukfs->ukfs_spin);
114 if (ukfs->ukfs_nextpid == 0)
115 ukfs->ukfs_nextpid++;
116 npid = ukfs->ukfs_nextpid++;
117 pthread_spin_unlock(&ukfs->ukfs_spin);
118
119 return npid;
120 }
121
122 static void
123 precall(struct ukfs *ukfs)
124 {
125 struct vnode *rvp, *cvp;
126
127 rump_setup_curlwp(nextpid(ukfs), 1, 1);
128 rvp = ukfs_getrvp(ukfs);
129 pthread_spin_lock(&ukfs->ukfs_spin);
130 cvp = ukfs->ukfs_cdir;
131 pthread_spin_unlock(&ukfs->ukfs_spin);
132 rump_rcvp_set(rvp, cvp); /* takes refs */
133 rump_vp_rele(rvp);
134 }
135
136 static void
137 postcall(struct ukfs *ukfs)
138 {
139 struct vnode *rvp;
140
141 rvp = ukfs_getrvp(ukfs);
142 rump_rcvp_set(NULL, rvp);
143 rump_vp_rele(rvp);
144 rump_clear_curlwp();
145 }
146
147 int
148 _ukfs_init(int version)
149 {
150 int rv;
151
152 if (version != UKFS_VERSION) {
153 printf("incompatible ukfs version, %d vs. %d\n",
154 version, UKFS_VERSION);
155 errno = EPROGMISMATCH;
156 return -1;
157 }
158
159 if ((rv = rump_init()) != 0) {
160 errno = rv;
161 return -1;
162 }
163
164 return 0;
165 }
166
167 /*ARGSUSED*/
168 static int
169 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode)
170 {
171
172 return rump_sys_mkdir(path, mode);
173 }
174
175 struct ukfs *
176 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath,
177 int mntflags, void *arg, size_t alen)
178 {
179 struct stat sb;
180 struct ukfs *fs = NULL;
181 int rv = 0, devfd = -1, rdonly;
182 int mounted = 0;
183
184 /*
185 * Try open and lock the device. if we can't open it, assume
186 * it's a file system which doesn't use a real device and let
187 * it slide. The mount will fail anyway if the fs requires a
188 * device.
189 *
190 * XXX: strictly speaking this is not 100% correct, as virtual
191 * file systems can use a device path which does exist and can
192 * be opened. E.g. tmpfs should be mountable multiple times
193 * with "device" path "/swap", but now isn't. But I think the
194 * chances are so low that it's currently acceptable to let
195 * this one slip.
196 */
197 rdonly = mntflags & MNT_RDONLY;
198 devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR);
199 if (devfd != -1) {
200 if (fstat(devfd, &sb) == -1) {
201 close(devfd);
202 devfd = -1;
203 rv = errno;
204 goto out;
205 }
206
207 /*
208 * We do this only for non-block device since the
209 * (NetBSD) kernel allows block device open only once.
210 */
211 if (!S_ISBLK(sb.st_mode)) {
212 if (flock(devfd, LOCK_NB | (rdonly ? LOCK_SH:LOCK_EX))
213 == -1) {
214 warnx("ukfs_mount: cannot get %s lock on "
215 "device", rdonly ? "shared" : "exclusive");
216 close(devfd);
217 devfd = -1;
218 rv = errno;
219 goto out;
220 }
221 } else {
222 close(devfd);
223 devfd = -1;
224 }
225 }
226
227 fs = malloc(sizeof(struct ukfs));
228 if (fs == NULL) {
229 rv = ENOMEM;
230 goto out;
231 }
232 memset(fs, 0, sizeof(struct ukfs));
233
234 /* create our mountpoint. this is never removed. */
235 if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) {
236 if (errno != EEXIST) {
237 rv = errno;
238 goto out;
239 }
240 }
241
242 rump_fakeblk_register(devpath);
243 rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen);
244 if (rv) {
245 goto out;
246 }
247 mounted = 1;
248 rv = rump_vfs_getmp(mountpath, &fs->ukfs_mp);
249 if (rv) {
250 goto out;
251 }
252 rv = rump_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
253 if (rv) {
254 goto out;
255 }
256
257 fs->ukfs_devpath = strdup(devpath);
258 fs->ukfs_mountpath = strdup(mountpath);
259 fs->ukfs_cdir = ukfs_getrvp(fs);
260 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
261 fs->ukfs_devfd = devfd;
262 assert(rv == 0);
263
264 out:
265 if (rv) {
266 int sverrno = errno;
267 if (fs) {
268 if (fs->ukfs_rvp)
269 rump_vp_rele(fs->ukfs_rvp);
270 free(fs);
271 fs = NULL;
272 }
273 if (mounted)
274 rump_sys_unmount(mountpath, MNT_FORCE);
275 if (devfd != -1) {
276 flock(devfd, LOCK_UN);
277 close(devfd);
278 }
279 errno = sverrno;
280 }
281
282 return fs;
283 }
284
285 int
286 ukfs_release(struct ukfs *fs, int flags)
287 {
288
289 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
290 int rv, mntflag;
291
292 ukfs_chdir(fs, "/");
293 mntflag = 0;
294 if (flags & UKFS_RELFLAG_FORCE)
295 mntflag = MNT_FORCE;
296 rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag);
297 if (rv) {
298 ukfs_chdir(fs, fs->ukfs_mountpath);
299 errno = rv;
300 return -1;
301 }
302 }
303
304 rump_fakeblk_deregister(fs->ukfs_devpath);
305 free(fs->ukfs_devpath);
306 free(fs->ukfs_mountpath);
307
308 pthread_spin_destroy(&fs->ukfs_spin);
309 if (fs->ukfs_devfd != -1) {
310 flock(fs->ukfs_devfd, LOCK_UN);
311 close(fs->ukfs_devfd);
312 }
313 free(fs);
314
315 return 0;
316 }
317
318 #define STDCALL(ukfs, thecall) \
319 int rv = 0; \
320 \
321 precall(ukfs); \
322 rv = thecall; \
323 postcall(ukfs); \
324 return rv;
325
326 int
327 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c)
328 {
329 struct vnode *vp;
330 int rv;
331
332 precall(ukfs);
333 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
334 NULL, &vp, NULL);
335 postcall(ukfs);
336
337 if (rv == 0) {
338 RUMP_VOP_UNLOCK(vp, 0);
339 } else {
340 errno = rv;
341 rv = -1;
342 }
343
344 /*LINTED*/
345 *c = (struct ukfs_dircookie *)vp;
346 return rv;
347 }
348
349 static int
350 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize)
351 {
352 struct uio *uio;
353 size_t resid;
354 int rv, eofflag;
355 kauth_cred_t cred;
356
357 uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
358 cred = rump_cred_suserget();
359 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
360 rump_cred_suserput(cred);
361 RUMP_VOP_UNLOCK(vp, 0);
362 *off = rump_uio_getoff(uio);
363 resid = rump_uio_free(uio);
364
365 if (rv) {
366 errno = rv;
367 return -1;
368 }
369
370 /* LINTED: not totally correct return type, but follows syscall */
371 return bufsize - resid;
372 }
373
374 /*ARGSUSED*/
375 int
376 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off,
377 uint8_t *buf, size_t bufsize)
378 {
379 /*LINTED*/
380 struct vnode *vp = (struct vnode *)c;
381
382 RUMP_VOP_LOCK(vp, RUMP_LK_SHARED);
383 return getmydents(vp, off, buf, bufsize);
384 }
385
386 int
387 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
388 uint8_t *buf, size_t bufsize)
389 {
390 struct vnode *vp;
391 int rv;
392
393 precall(ukfs);
394 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
395 NULL, &vp, NULL);
396 postcall(ukfs);
397 if (rv) {
398 errno = rv;
399 return -1;
400 }
401
402 rv = getmydents(vp, off, buf, bufsize);
403 rump_vp_rele(vp);
404 return rv;
405 }
406
407 /*ARGSUSED*/
408 int
409 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c)
410 {
411
412 /*LINTED*/
413 rump_vp_rele((struct vnode *)c);
414 return 0;
415 }
416
417 int
418 ukfs_open(struct ukfs *ukfs, const char *filename, int flags)
419 {
420 int fd;
421
422 precall(ukfs);
423 fd = rump_sys_open(filename, flags, 0);
424 postcall(ukfs);
425 if (fd == -1)
426 return -1;
427
428 return fd;
429 }
430
431 ssize_t
432 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
433 uint8_t *buf, size_t bufsize)
434 {
435 int fd;
436 ssize_t xfer = -1; /* XXXgcc */
437
438 precall(ukfs);
439 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
440 if (fd == -1)
441 goto out;
442
443 xfer = rump_sys_pread(fd, buf, bufsize, off);
444 rump_sys_close(fd);
445
446 out:
447 postcall(ukfs);
448 if (fd == -1) {
449 return -1;
450 }
451 return xfer;
452 }
453
454 /*ARGSUSED*/
455 ssize_t
456 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen)
457 {
458
459 return rump_sys_pread(fd, buf, buflen, off);
460 }
461
462 ssize_t
463 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
464 uint8_t *buf, size_t bufsize)
465 {
466 int fd;
467 ssize_t xfer = -1; /* XXXgcc */
468
469 precall(ukfs);
470 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
471 if (fd == -1)
472 goto out;
473
474 /* write and commit */
475 xfer = rump_sys_pwrite(fd, buf, bufsize, off);
476 if (xfer > 0)
477 rump_sys_fsync(fd);
478
479 rump_sys_close(fd);
480
481 out:
482 postcall(ukfs);
483 if (fd == -1) {
484 return -1;
485 }
486 return xfer;
487 }
488
489 /*ARGSUSED*/
490 ssize_t
491 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen,
492 int dosync)
493 {
494 ssize_t xfer;
495
496 xfer = rump_sys_pwrite(fd, buf, buflen, off);
497 if (xfer > 0 && dosync)
498 rump_sys_fsync(fd);
499
500 return xfer;
501 }
502
503 /*ARGSUSED*/
504 int
505 ukfs_close(struct ukfs *ukfs, int fd)
506 {
507
508 rump_sys_close(fd);
509 return 0;
510 }
511
512 int
513 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
514 {
515 int fd;
516
517 precall(ukfs);
518 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
519 if (fd == -1)
520 return -1;
521 rump_sys_close(fd);
522
523 postcall(ukfs);
524 return 0;
525 }
526
527 int
528 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
529 {
530
531 STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
532 }
533
534 int
535 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
536 {
537
538 STDCALL(ukfs, rump_sys_mkfifo(path, mode));
539 }
540
541 int
542 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
543 {
544
545 STDCALL(ukfs, rump_sys_mkdir(filename, mode));
546 }
547
548 int
549 ukfs_remove(struct ukfs *ukfs, const char *filename)
550 {
551
552 STDCALL(ukfs, rump_sys_unlink(filename));
553 }
554
555 int
556 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
557 {
558
559 STDCALL(ukfs, rump_sys_rmdir(filename));
560 }
561
562 int
563 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
564 {
565
566 STDCALL(ukfs, rump_sys_link(filename, f_create));
567 }
568
569 int
570 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
571 {
572
573 STDCALL(ukfs, rump_sys_symlink(filename, linkname));
574 }
575
576 ssize_t
577 ukfs_readlink(struct ukfs *ukfs, const char *filename,
578 char *linkbuf, size_t buflen)
579 {
580 ssize_t rv;
581
582 precall(ukfs);
583 rv = rump_sys_readlink(filename, linkbuf, buflen);
584 postcall(ukfs);
585 return rv;
586 }
587
588 int
589 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
590 {
591
592 STDCALL(ukfs, rump_sys_rename(from, to));
593 }
594
595 int
596 ukfs_chdir(struct ukfs *ukfs, const char *path)
597 {
598 struct vnode *newvp, *oldvp;
599 int rv;
600
601 precall(ukfs);
602 rv = rump_sys_chdir(path);
603 if (rv == -1)
604 goto out;
605
606 newvp = rump_cdir_get();
607 pthread_spin_lock(&ukfs->ukfs_spin);
608 oldvp = ukfs->ukfs_cdir;
609 ukfs->ukfs_cdir = newvp;
610 pthread_spin_unlock(&ukfs->ukfs_spin);
611 if (oldvp)
612 rump_vp_rele(oldvp);
613
614 out:
615 postcall(ukfs);
616 return rv;
617 }
618
619 /*
620 * If we want to use post-time_t file systems on pre-time_t hosts,
621 * we must translate the stat structure. Since we don't currently
622 * have a general method for making compat calls in rump, special-case
623 * this one.
624 *
625 * Note that this does not allow making system calls to older rump
626 * kernels from newer hosts.
627 */
628 #define VERS_TIMECHANGE 599000700
629
630 static int
631 needcompat(void)
632 {
633
634 #ifdef __NetBSD__
635 /*LINTED*/
636 return __NetBSD_Version__ < VERS_TIMECHANGE
637 && rump_getversion() >= VERS_TIMECHANGE;
638 #else
639 return 0;
640 #endif
641 }
642
643 int
644 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
645 {
646 int rv;
647
648 precall(ukfs);
649 if (needcompat())
650 rv = rump_sys___stat30(filename, file_stat);
651 else
652 rv = rump_sys_stat(filename, file_stat);
653 postcall(ukfs);
654
655 return rv;
656 }
657
658 int
659 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
660 {
661 int rv;
662
663 precall(ukfs);
664 if (needcompat())
665 rv = rump_sys___lstat30(filename, file_stat);
666 else
667 rv = rump_sys_lstat(filename, file_stat);
668 postcall(ukfs);
669
670 return rv;
671 }
672
673 int
674 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
675 {
676
677 STDCALL(ukfs, rump_sys_chmod(filename, mode));
678 }
679
680 int
681 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
682 {
683
684 STDCALL(ukfs, rump_sys_lchmod(filename, mode));
685 }
686
687 int
688 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
689 {
690
691 STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
692 }
693
694 int
695 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
696 {
697
698 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
699 }
700
701 int
702 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
703 {
704
705 STDCALL(ukfs, rump_sys_chflags(filename, flags));
706 }
707
708 int
709 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
710 {
711
712 STDCALL(ukfs, rump_sys_lchflags(filename, flags));
713 }
714
715 int
716 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
717 {
718
719 STDCALL(ukfs, rump_sys_utimes(filename, tptr));
720 }
721
722 int
723 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
724 const struct timeval *tptr)
725 {
726
727 STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
728 }
729
730 /*
731 * Dynamic module support
732 */
733
734 /* load one library */
735
736 /*
737 * XXX: the dlerror stuff isn't really threadsafe, but then again I
738 * can't protect against other threads calling dl*() outside of ukfs,
739 * so just live with it being flimsy
740 */
741 int
742 ukfs_modload(const char *fname)
743 {
744 void *handle;
745 struct modinfo **mi;
746 int error;
747
748 handle = dlopen(fname, RTLD_GLOBAL);
749 if (handle == NULL) {
750 const char *dlmsg = dlerror();
751 if (strstr(dlmsg, "Undefined symbol"))
752 return 0;
753 warnx("dlopen %s failed: %s\n", fname, dlmsg);
754 /* XXXerrno */
755 return -1;
756 }
757
758 mi = dlsym(handle, "__start_link_set_modules");
759 if (mi) {
760 error = rump_module_init(*mi, NULL);
761 if (error)
762 goto errclose;
763 return 1;
764 }
765 error = EINVAL;
766
767 errclose:
768 dlclose(handle);
769 errno = error;
770 return -1;
771 }
772
773 struct loadfail {
774 char *pname;
775
776 LIST_ENTRY(loadfail) entries;
777 };
778
779 #define RUMPFSMOD_PREFIX "librumpfs_"
780 #define RUMPFSMOD_SUFFIX ".so"
781
782 int
783 ukfs_modload_dir(const char *dir)
784 {
785 char nbuf[MAXPATHLEN+1], *p;
786 struct dirent entry, *result;
787 DIR *libdir;
788 struct loadfail *lf, *nlf;
789 int error, nloaded = 0, redo;
790 LIST_HEAD(, loadfail) lfs;
791
792 libdir = opendir(dir);
793 if (libdir == NULL)
794 return -1;
795
796 LIST_INIT(&lfs);
797 for (;;) {
798 if ((error = readdir_r(libdir, &entry, &result)) != 0)
799 break;
800 if (!result)
801 break;
802 if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
803 strlen(RUMPFSMOD_PREFIX)) != 0)
804 continue;
805 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
806 || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
807 continue;
808 strlcpy(nbuf, dir, sizeof(nbuf));
809 strlcat(nbuf, "/", sizeof(nbuf));
810 strlcat(nbuf, result->d_name, sizeof(nbuf));
811 switch (ukfs_modload(nbuf)) {
812 case 0:
813 lf = malloc(sizeof(*lf));
814 if (lf == NULL) {
815 error = ENOMEM;
816 break;
817 }
818 lf->pname = strdup(nbuf);
819 if (lf->pname == NULL) {
820 free(lf);
821 error = ENOMEM;
822 break;
823 }
824 LIST_INSERT_HEAD(&lfs, lf, entries);
825 break;
826 case 1:
827 nloaded++;
828 break;
829 default:
830 /* ignore errors */
831 break;
832 }
833 }
834 closedir(libdir);
835 if (error && nloaded != 0)
836 error = 0;
837
838 /*
839 * El-cheapo dependency calculator. Just try to load the
840 * modules n times in a loop
841 */
842 for (redo = 1; redo;) {
843 redo = 0;
844 nlf = LIST_FIRST(&lfs);
845 while ((lf = nlf) != NULL) {
846 nlf = LIST_NEXT(lf, entries);
847 if (ukfs_modload(lf->pname) == 1) {
848 nloaded++;
849 redo = 1;
850 LIST_REMOVE(lf, entries);
851 free(lf->pname);
852 free(lf);
853 }
854 }
855 }
856
857 while ((lf = LIST_FIRST(&lfs)) != NULL) {
858 LIST_REMOVE(lf, entries);
859 free(lf->pname);
860 free(lf);
861 }
862
863 if (error && nloaded == 0) {
864 errno = error;
865 return -1;
866 }
867
868 return nloaded;
869 }
870
871 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
872 ssize_t
873 ukfs_vfstypes(char *buf, size_t buflen)
874 {
875 int mib[3];
876 struct sysctlnode q, ans[128];
877 size_t alen;
878 int i;
879
880 mib[0] = CTL_VFS;
881 mib[1] = VFS_GENERIC;
882 mib[2] = CTL_QUERY;
883 alen = sizeof(ans);
884
885 memset(&q, 0, sizeof(q));
886 q.sysctl_flags = SYSCTL_VERSION;
887
888 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
889 return -1;
890 }
891
892 for (i = 0; i < alen/sizeof(ans[0]); i++)
893 if (strcmp("fstypes", ans[i].sysctl_name) == 0)
894 break;
895 if (i == alen/sizeof(ans[0])) {
896 errno = ENXIO;
897 return -1;
898 }
899
900 mib[0] = CTL_VFS;
901 mib[1] = VFS_GENERIC;
902 mib[2] = ans[i].sysctl_num;
903
904 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
905 return -1;
906 }
907
908 return buflen;
909 }
910
911 /*
912 * Utilities
913 */
914 static int
915 builddirs(const char *pathname, mode_t mode,
916 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs)
917 {
918 char *f1, *f2;
919 int rv;
920 mode_t mask;
921 bool end;
922
923 /*ukfs_umask((mask = ukfs_umask(0)));*/
924 umask((mask = umask(0)));
925
926 f1 = f2 = strdup(pathname);
927 if (f1 == NULL) {
928 errno = ENOMEM;
929 return -1;
930 }
931
932 end = false;
933 for (;;) {
934 /* find next component */
935 f2 += strspn(f2, "/");
936 f2 += strcspn(f2, "/");
937 if (*f2 == '\0')
938 end = true;
939 else
940 *f2 = '\0';
941
942 rv = mkdirfn(fs, f1, mode & ~mask);
943 if (errno == EEXIST)
944 rv = 0;
945
946 if (rv == -1 || *f2 != '\0' || end)
947 break;
948
949 *f2 = '/';
950 }
951
952 free(f1);
953
954 return rv;
955 }
956
957 int
958 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
959 {
960
961 return builddirs(pathname, mode, ukfs_mkdir, ukfs);
962 }
963