ukfs.c revision 1.41 1 /* $NetBSD: ukfs.c,v 1.41 2009/10/15 16:41:08 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_pub_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_pub_lwp_alloc_and_switch(nextpid(ukfs), 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_pub_rcvp_set(rvp, cvp); /* takes refs */
150 rump_pub_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_pub_rcvp_set(NULL, rvp);
160 rump_pub_vp_rele(rvp);
161 rump_pub_lwp_release(rump_pub_lwp_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 = -1;
327 uint64_t devoff, devsize;
328 int mounted = 0;
329 int regged = 0;
330
331 /* XXX: gcc whine */
332 devoff = 0;
333 devsize = 0;
334
335 if (partition != UKFS_PARTITION_NA)
336 process_diskdevice(devpath, partition, mntflags & MNT_RDONLY,
337 &devfd, &devoff, &devsize);
338
339 fs = malloc(sizeof(struct ukfs));
340 if (fs == NULL) {
341 rv = ENOMEM;
342 goto out;
343 }
344 memset(fs, 0, sizeof(struct ukfs));
345
346 /* create our mountpoint. this is never removed. */
347 if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) {
348 if (errno != EEXIST) {
349 rv = errno;
350 goto out;
351 }
352 }
353
354 if (partition != UKFS_PARTITION_NA) {
355 rv = rump_pub_etfs_register_withsize(devpath, devpath,
356 RUMP_ETFS_BLK, devoff, devsize);
357 if (rv) {
358 goto out;
359 }
360 regged = 1;
361 }
362
363 rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen);
364 if (rv) {
365 rv = errno;
366 goto out;
367 }
368 mounted = 1;
369 rv = rump_pub_vfs_getmp(mountpath, &fs->ukfs_mp);
370 if (rv) {
371 goto out;
372 }
373 rv = rump_pub_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
374 if (rv) {
375 goto out;
376 }
377
378 if (regged) {
379 fs->ukfs_devpath = strdup(devpath);
380 }
381 fs->ukfs_mountpath = strdup(mountpath);
382 fs->ukfs_cdir = ukfs_getrvp(fs);
383 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
384 fs->ukfs_devfd = devfd;
385 assert(rv == 0);
386
387 out:
388 if (rv) {
389 if (fs) {
390 if (fs->ukfs_rvp)
391 rump_pub_vp_rele(fs->ukfs_rvp);
392 free(fs);
393 fs = NULL;
394 }
395 if (mounted)
396 rump_sys_unmount(mountpath, MNT_FORCE);
397 if (regged)
398 rump_pub_etfs_remove(devpath);
399 if (devfd != -1) {
400 flock(devfd, LOCK_UN);
401 close(devfd);
402 }
403 errno = rv;
404 }
405
406 return fs;
407 }
408
409 struct ukfs *
410 ukfs_mount(const char *vfsname, const char *devpath,
411 const char *mountpath, int mntflags, void *arg, size_t alen)
412 {
413
414 return doukfsmount(vfsname, devpath, UKFS_PARTITION_NA,
415 mountpath, mntflags, arg, alen);
416 }
417
418 struct ukfs *
419 ukfs_mount_disk(const char *vfsname, const char *devpath, int partition,
420 const char *mountpath, int mntflags, void *arg, size_t alen)
421 {
422
423 return doukfsmount(vfsname, devpath, partition,
424 mountpath, mntflags, arg, alen);
425 }
426
427 int
428 ukfs_release(struct ukfs *fs, int flags)
429 {
430
431 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
432 int rv, mntflag, error;
433
434 ukfs_chdir(fs, "/");
435 mntflag = 0;
436 if (flags & UKFS_RELFLAG_FORCE)
437 mntflag = MNT_FORCE;
438 rump_pub_lwp_alloc_and_switch(nextpid(fs), 1);
439 rump_pub_vp_rele(fs->ukfs_rvp);
440 fs->ukfs_rvp = NULL;
441 rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag);
442 if (rv == -1) {
443 error = errno;
444 rump_pub_vfs_root(fs->ukfs_mp, &fs->ukfs_rvp, 0);
445 rump_pub_lwp_release(rump_pub_lwp_curlwp());
446 ukfs_chdir(fs, fs->ukfs_mountpath);
447 errno = error;
448 return -1;
449 }
450 rump_pub_lwp_release(rump_pub_lwp_curlwp());
451 }
452
453 if (fs->ukfs_devpath) {
454 rump_pub_etfs_remove(fs->ukfs_devpath);
455 free(fs->ukfs_devpath);
456 }
457 free(fs->ukfs_mountpath);
458
459 pthread_spin_destroy(&fs->ukfs_spin);
460 if (fs->ukfs_devfd != -1) {
461 flock(fs->ukfs_devfd, LOCK_UN);
462 close(fs->ukfs_devfd);
463 }
464 free(fs);
465
466 return 0;
467 }
468
469 #define STDCALL(ukfs, thecall) \
470 int rv = 0; \
471 \
472 precall(ukfs); \
473 rv = thecall; \
474 postcall(ukfs); \
475 return rv;
476
477 int
478 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c)
479 {
480 struct vnode *vp;
481 int rv;
482
483 precall(ukfs);
484 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
485 NULL, &vp, NULL);
486 postcall(ukfs);
487
488 if (rv == 0) {
489 RUMP_VOP_UNLOCK(vp, 0);
490 } else {
491 errno = rv;
492 rv = -1;
493 }
494
495 /*LINTED*/
496 *c = (struct ukfs_dircookie *)vp;
497 return rv;
498 }
499
500 static int
501 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize)
502 {
503 struct uio *uio;
504 size_t resid;
505 int rv, eofflag;
506 kauth_cred_t cred;
507
508 uio = rump_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
509 cred = rump_pub_cred_suserget();
510 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
511 rump_pub_cred_put(cred);
512 RUMP_VOP_UNLOCK(vp, 0);
513 *off = rump_pub_uio_getoff(uio);
514 resid = rump_pub_uio_free(uio);
515
516 if (rv) {
517 errno = rv;
518 return -1;
519 }
520
521 /* LINTED: not totally correct return type, but follows syscall */
522 return bufsize - resid;
523 }
524
525 /*ARGSUSED*/
526 int
527 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off,
528 uint8_t *buf, size_t bufsize)
529 {
530 /*LINTED*/
531 struct vnode *vp = (struct vnode *)c;
532
533 RUMP_VOP_LOCK(vp, RUMP_LK_SHARED);
534 return getmydents(vp, off, buf, bufsize);
535 }
536
537 int
538 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
539 uint8_t *buf, size_t bufsize)
540 {
541 struct vnode *vp;
542 int rv;
543
544 precall(ukfs);
545 rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
546 NULL, &vp, NULL);
547 postcall(ukfs);
548 if (rv) {
549 errno = rv;
550 return -1;
551 }
552
553 rv = getmydents(vp, off, buf, bufsize);
554 rump_pub_vp_rele(vp);
555 return rv;
556 }
557
558 /*ARGSUSED*/
559 int
560 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c)
561 {
562
563 /*LINTED*/
564 rump_pub_vp_rele((struct vnode *)c);
565 return 0;
566 }
567
568 int
569 ukfs_open(struct ukfs *ukfs, const char *filename, int flags)
570 {
571 int fd;
572
573 precall(ukfs);
574 fd = rump_sys_open(filename, flags, 0);
575 postcall(ukfs);
576 if (fd == -1)
577 return -1;
578
579 return fd;
580 }
581
582 ssize_t
583 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
584 uint8_t *buf, size_t bufsize)
585 {
586 int fd;
587 ssize_t xfer = -1; /* XXXgcc */
588
589 precall(ukfs);
590 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
591 if (fd == -1)
592 goto out;
593
594 xfer = rump_sys_pread(fd, buf, bufsize, off);
595 rump_sys_close(fd);
596
597 out:
598 postcall(ukfs);
599 if (fd == -1) {
600 return -1;
601 }
602 return xfer;
603 }
604
605 /*ARGSUSED*/
606 ssize_t
607 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen)
608 {
609
610 return rump_sys_pread(fd, buf, buflen, off);
611 }
612
613 ssize_t
614 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
615 uint8_t *buf, size_t bufsize)
616 {
617 int fd;
618 ssize_t xfer = -1; /* XXXgcc */
619
620 precall(ukfs);
621 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
622 if (fd == -1)
623 goto out;
624
625 /* write and commit */
626 xfer = rump_sys_pwrite(fd, buf, bufsize, off);
627 if (xfer > 0)
628 rump_sys_fsync(fd);
629
630 rump_sys_close(fd);
631
632 out:
633 postcall(ukfs);
634 if (fd == -1) {
635 return -1;
636 }
637 return xfer;
638 }
639
640 /*ARGSUSED*/
641 ssize_t
642 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen,
643 int dosync)
644 {
645 ssize_t xfer;
646
647 xfer = rump_sys_pwrite(fd, buf, buflen, off);
648 if (xfer > 0 && dosync)
649 rump_sys_fsync(fd);
650
651 return xfer;
652 }
653
654 /*ARGSUSED*/
655 int
656 ukfs_close(struct ukfs *ukfs, int fd)
657 {
658
659 rump_sys_close(fd);
660 return 0;
661 }
662
663 int
664 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
665 {
666 int fd;
667
668 precall(ukfs);
669 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
670 if (fd == -1)
671 return -1;
672 rump_sys_close(fd);
673
674 postcall(ukfs);
675 return 0;
676 }
677
678 int
679 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
680 {
681
682 STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
683 }
684
685 int
686 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
687 {
688
689 STDCALL(ukfs, rump_sys_mkfifo(path, mode));
690 }
691
692 int
693 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
694 {
695
696 STDCALL(ukfs, rump_sys_mkdir(filename, mode));
697 }
698
699 int
700 ukfs_remove(struct ukfs *ukfs, const char *filename)
701 {
702
703 STDCALL(ukfs, rump_sys_unlink(filename));
704 }
705
706 int
707 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
708 {
709
710 STDCALL(ukfs, rump_sys_rmdir(filename));
711 }
712
713 int
714 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
715 {
716
717 STDCALL(ukfs, rump_sys_link(filename, f_create));
718 }
719
720 int
721 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
722 {
723
724 STDCALL(ukfs, rump_sys_symlink(filename, linkname));
725 }
726
727 ssize_t
728 ukfs_readlink(struct ukfs *ukfs, const char *filename,
729 char *linkbuf, size_t buflen)
730 {
731 ssize_t rv;
732
733 precall(ukfs);
734 rv = rump_sys_readlink(filename, linkbuf, buflen);
735 postcall(ukfs);
736 return rv;
737 }
738
739 int
740 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
741 {
742
743 STDCALL(ukfs, rump_sys_rename(from, to));
744 }
745
746 int
747 ukfs_chdir(struct ukfs *ukfs, const char *path)
748 {
749 struct vnode *newvp, *oldvp;
750 int rv;
751
752 precall(ukfs);
753 rv = rump_sys_chdir(path);
754 if (rv == -1)
755 goto out;
756
757 newvp = rump_pub_cdir_get();
758 pthread_spin_lock(&ukfs->ukfs_spin);
759 oldvp = ukfs->ukfs_cdir;
760 ukfs->ukfs_cdir = newvp;
761 pthread_spin_unlock(&ukfs->ukfs_spin);
762 if (oldvp)
763 rump_pub_vp_rele(oldvp);
764
765 out:
766 postcall(ukfs);
767 return rv;
768 }
769
770 /*
771 * If we want to use post-time_t file systems on pre-time_t hosts,
772 * we must translate the stat structure. Since we don't currently
773 * have a general method for making compat calls in rump, special-case
774 * this one.
775 *
776 * Note that this does not allow making system calls to older rump
777 * kernels from newer hosts.
778 */
779 #define VERS_TIMECHANGE 599000700
780
781 static int
782 needcompat(void)
783 {
784
785 #ifdef __NetBSD__
786 /*LINTED*/
787 return __NetBSD_Version__ < VERS_TIMECHANGE
788 && rump_pub_getversion() >= VERS_TIMECHANGE;
789 #else
790 return 0;
791 #endif
792 }
793
794 int
795 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
796 {
797 int rv;
798
799 precall(ukfs);
800 if (needcompat())
801 rv = rump_pub_sys___stat30(filename, file_stat);
802 else
803 rv = rump_sys_stat(filename, file_stat);
804 postcall(ukfs);
805
806 return rv;
807 }
808
809 int
810 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
811 {
812 int rv;
813
814 precall(ukfs);
815 if (needcompat())
816 rv = rump_pub_sys___lstat30(filename, file_stat);
817 else
818 rv = rump_sys_lstat(filename, file_stat);
819 postcall(ukfs);
820
821 return rv;
822 }
823
824 int
825 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
826 {
827
828 STDCALL(ukfs, rump_sys_chmod(filename, mode));
829 }
830
831 int
832 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
833 {
834
835 STDCALL(ukfs, rump_sys_lchmod(filename, mode));
836 }
837
838 int
839 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
840 {
841
842 STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
843 }
844
845 int
846 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
847 {
848
849 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
850 }
851
852 int
853 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
854 {
855
856 STDCALL(ukfs, rump_sys_chflags(filename, flags));
857 }
858
859 int
860 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
861 {
862
863 STDCALL(ukfs, rump_sys_lchflags(filename, flags));
864 }
865
866 int
867 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
868 {
869
870 STDCALL(ukfs, rump_sys_utimes(filename, tptr));
871 }
872
873 int
874 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
875 const struct timeval *tptr)
876 {
877
878 STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
879 }
880
881 /*
882 * Dynamic module support
883 */
884
885 /* load one library */
886
887 /*
888 * XXX: the dlerror stuff isn't really threadsafe, but then again I
889 * can't protect against other threads calling dl*() outside of ukfs,
890 * so just live with it being flimsy
891 */
892 int
893 ukfs_modload(const char *fname)
894 {
895 void *handle;
896 struct modinfo **mi;
897 int error;
898
899 handle = dlopen(fname, RTLD_GLOBAL);
900 if (handle == NULL) {
901 const char *dlmsg = dlerror();
902 if (strstr(dlmsg, "Undefined symbol"))
903 return 0;
904 warnx("dlopen %s failed: %s\n", fname, dlmsg);
905 /* XXXerrno */
906 return -1;
907 }
908
909 mi = dlsym(handle, "__start_link_set_modules");
910 if (mi) {
911 error = rump_pub_module_init(*mi, NULL);
912 if (error)
913 goto errclose;
914 return 1;
915 }
916 error = EINVAL;
917
918 errclose:
919 dlclose(handle);
920 errno = error;
921 return -1;
922 }
923
924 struct loadfail {
925 char *pname;
926
927 LIST_ENTRY(loadfail) entries;
928 };
929
930 #define RUMPFSMOD_PREFIX "librumpfs_"
931 #define RUMPFSMOD_SUFFIX ".so"
932
933 int
934 ukfs_modload_dir(const char *dir)
935 {
936 char nbuf[MAXPATHLEN+1], *p;
937 struct dirent entry, *result;
938 DIR *libdir;
939 struct loadfail *lf, *nlf;
940 int error, nloaded = 0, redo;
941 LIST_HEAD(, loadfail) lfs;
942
943 libdir = opendir(dir);
944 if (libdir == NULL)
945 return -1;
946
947 LIST_INIT(&lfs);
948 for (;;) {
949 if ((error = readdir_r(libdir, &entry, &result)) != 0)
950 break;
951 if (!result)
952 break;
953 if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
954 strlen(RUMPFSMOD_PREFIX)) != 0)
955 continue;
956 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
957 || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
958 continue;
959 strlcpy(nbuf, dir, sizeof(nbuf));
960 strlcat(nbuf, "/", sizeof(nbuf));
961 strlcat(nbuf, result->d_name, sizeof(nbuf));
962 switch (ukfs_modload(nbuf)) {
963 case 0:
964 lf = malloc(sizeof(*lf));
965 if (lf == NULL) {
966 error = ENOMEM;
967 break;
968 }
969 lf->pname = strdup(nbuf);
970 if (lf->pname == NULL) {
971 free(lf);
972 error = ENOMEM;
973 break;
974 }
975 LIST_INSERT_HEAD(&lfs, lf, entries);
976 break;
977 case 1:
978 nloaded++;
979 break;
980 default:
981 /* ignore errors */
982 break;
983 }
984 }
985 closedir(libdir);
986 if (error && nloaded != 0)
987 error = 0;
988
989 /*
990 * El-cheapo dependency calculator. Just try to load the
991 * modules n times in a loop
992 */
993 for (redo = 1; redo;) {
994 redo = 0;
995 nlf = LIST_FIRST(&lfs);
996 while ((lf = nlf) != NULL) {
997 nlf = LIST_NEXT(lf, entries);
998 if (ukfs_modload(lf->pname) == 1) {
999 nloaded++;
1000 redo = 1;
1001 LIST_REMOVE(lf, entries);
1002 free(lf->pname);
1003 free(lf);
1004 }
1005 }
1006 }
1007
1008 while ((lf = LIST_FIRST(&lfs)) != NULL) {
1009 LIST_REMOVE(lf, entries);
1010 free(lf->pname);
1011 free(lf);
1012 }
1013
1014 if (error && nloaded == 0) {
1015 errno = error;
1016 return -1;
1017 }
1018
1019 return nloaded;
1020 }
1021
1022 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
1023 ssize_t
1024 ukfs_vfstypes(char *buf, size_t buflen)
1025 {
1026 int mib[3];
1027 struct sysctlnode q, ans[128];
1028 size_t alen;
1029 int i;
1030
1031 mib[0] = CTL_VFS;
1032 mib[1] = VFS_GENERIC;
1033 mib[2] = CTL_QUERY;
1034 alen = sizeof(ans);
1035
1036 memset(&q, 0, sizeof(q));
1037 q.sysctl_flags = SYSCTL_VERSION;
1038
1039 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
1040 return -1;
1041 }
1042
1043 for (i = 0; i < alen/sizeof(ans[0]); i++)
1044 if (strcmp("fstypes", ans[i].sysctl_name) == 0)
1045 break;
1046 if (i == alen/sizeof(ans[0])) {
1047 errno = ENXIO;
1048 return -1;
1049 }
1050
1051 mib[0] = CTL_VFS;
1052 mib[1] = VFS_GENERIC;
1053 mib[2] = ans[i].sysctl_num;
1054
1055 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
1056 return -1;
1057 }
1058
1059 return buflen;
1060 }
1061
1062 /*
1063 * Utilities
1064 */
1065 static int
1066 builddirs(const char *pathname, mode_t mode,
1067 int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs)
1068 {
1069 char *f1, *f2;
1070 int rv;
1071 mode_t mask;
1072 bool end;
1073
1074 /*ukfs_umask((mask = ukfs_umask(0)));*/
1075 umask((mask = umask(0)));
1076
1077 f1 = f2 = strdup(pathname);
1078 if (f1 == NULL) {
1079 errno = ENOMEM;
1080 return -1;
1081 }
1082
1083 end = false;
1084 for (;;) {
1085 /* find next component */
1086 f2 += strspn(f2, "/");
1087 f2 += strcspn(f2, "/");
1088 if (*f2 == '\0')
1089 end = true;
1090 else
1091 *f2 = '\0';
1092
1093 rv = mkdirfn(fs, f1, mode & ~mask);
1094 if (errno == EEXIST)
1095 rv = 0;
1096
1097 if (rv == -1 || *f2 != '\0' || end)
1098 break;
1099
1100 *f2 = '/';
1101 }
1102
1103 free(f1);
1104
1105 return rv;
1106 }
1107
1108 int
1109 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
1110 {
1111
1112 return builddirs(pathname, mode, ukfs_mkdir, ukfs);
1113 }
1114