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