ukfs.c revision 1.22 1 /* $NetBSD: ukfs.c,v 1.22 2009/02/11 14:35:58 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 cred = rump_cred_suserget();
273 rv = rump_vfs_sync(fs->ukfs_mp, 1, cred);
274 rump_cred_suserput(cred);
275 rump_vp_recycle_nokidding(ukfs_getrvp(fs));
276 rv |= rump_vfs_unmount(fs->ukfs_mp, 0);
277 assert(rv == 0);
278 }
279
280 rump_vfs_syncwait(fs->ukfs_mp);
281 rump_mnt_destroy(fs->ukfs_mp);
282
283 pthread_spin_destroy(&fs->ukfs_spin);
284 if (fs->ukfs_devfd != -1) {
285 flock(fs->ukfs_devfd, LOCK_UN);
286 close(fs->ukfs_devfd);
287 }
288 free(fs);
289 }
290
291 #define STDCALL(ukfs, thecall) \
292 int rv = 0; \
293 \
294 precall(ukfs); \
295 rv = thecall; \
296 postcall(ukfs); \
297 return rv;
298
299 int
300 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
301 uint8_t *buf, size_t bufsize)
302 {
303 struct uio *uio;
304 struct vnode *vp;
305 size_t resid;
306 kauth_cred_t cred;
307 int rv, eofflag;
308
309 precall(ukfs);
310 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
311 NULL, &vp, NULL);
312 postcall(ukfs);
313 if (rv)
314 goto out;
315
316 uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
317 cred = rump_cred_suserget();
318 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
319 rump_cred_suserput(cred);
320 RUMP_VOP_UNLOCK(vp, 0);
321 *off = rump_uio_getoff(uio);
322 resid = rump_uio_free(uio);
323 rump_vp_rele(vp);
324
325 out:
326 if (rv) {
327 errno = rv;
328 return -1;
329 }
330
331 /* LINTED: not totally correct return type, but follows syscall */
332 return bufsize - resid;
333 }
334
335 ssize_t
336 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
337 uint8_t *buf, size_t bufsize)
338 {
339 int fd;
340 ssize_t xfer = -1; /* XXXgcc */
341
342 precall(ukfs);
343 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
344 if (fd == -1)
345 goto out;
346
347 xfer = rump_sys_pread(fd, buf, bufsize, 0, off);
348 rump_sys_close(fd);
349
350 out:
351 postcall(ukfs);
352 if (fd == -1) {
353 return -1;
354 }
355 return xfer;
356 }
357
358 ssize_t
359 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
360 uint8_t *buf, size_t bufsize)
361 {
362 int fd;
363 ssize_t xfer = -1; /* XXXgcc */
364
365 precall(ukfs);
366 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
367 if (fd == -1)
368 goto out;
369
370 /* write and commit */
371 xfer = rump_sys_pwrite(fd, buf, bufsize, 0, off);
372 if (xfer > 0)
373 rump_sys_fsync(fd);
374
375 rump_sys_close(fd);
376
377 out:
378 postcall(ukfs);
379 if (fd == -1) {
380 return -1;
381 }
382 return xfer;
383 }
384
385 int
386 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
387 {
388 int fd;
389
390 precall(ukfs);
391 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
392 if (fd == -1)
393 return -1;
394 rump_sys_close(fd);
395
396 postcall(ukfs);
397 return 0;
398 }
399
400 int
401 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
402 {
403
404 STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
405 }
406
407 int
408 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
409 {
410
411 STDCALL(ukfs, rump_sys_mkfifo(path, mode));
412 }
413
414 int
415 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
416 {
417
418 STDCALL(ukfs, rump_sys_mkdir(filename, mode));
419 }
420
421 int
422 ukfs_remove(struct ukfs *ukfs, const char *filename)
423 {
424
425 STDCALL(ukfs, rump_sys_unlink(filename));
426 }
427
428 int
429 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
430 {
431
432 STDCALL(ukfs, rump_sys_rmdir(filename));
433 }
434
435 int
436 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
437 {
438
439 STDCALL(ukfs, rump_sys_link(filename, f_create));
440 }
441
442 int
443 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
444 {
445
446 STDCALL(ukfs, rump_sys_symlink(filename, linkname));
447 }
448
449 ssize_t
450 ukfs_readlink(struct ukfs *ukfs, const char *filename,
451 char *linkbuf, size_t buflen)
452 {
453 ssize_t rv;
454
455 precall(ukfs);
456 rv = rump_sys_readlink(filename, linkbuf, buflen);
457 postcall(ukfs);
458 return rv;
459 }
460
461 int
462 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
463 {
464
465 STDCALL(ukfs, rump_sys_rename(from, to));
466 }
467
468 int
469 ukfs_chdir(struct ukfs *ukfs, const char *path)
470 {
471 struct vnode *newvp, *oldvp;
472 int rv;
473
474 precall(ukfs);
475 rv = rump_sys_chdir(path);
476 if (rv == -1)
477 goto out;
478
479 newvp = rump_cdir_get();
480 pthread_spin_lock(&ukfs->ukfs_spin);
481 oldvp = ukfs->ukfs_cdir;
482 ukfs->ukfs_cdir = newvp;
483 pthread_spin_unlock(&ukfs->ukfs_spin);
484 if (oldvp)
485 rump_vp_rele(oldvp);
486
487 out:
488 postcall(ukfs);
489 return rv;
490 }
491
492 int
493 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
494 {
495
496 STDCALL(ukfs, rump_sys_stat(filename, file_stat));
497 }
498
499 int
500 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
501 {
502
503 STDCALL(ukfs, rump_sys_lstat(filename, file_stat));
504 }
505
506 int
507 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
508 {
509
510 STDCALL(ukfs, rump_sys_chmod(filename, mode));
511 }
512
513 int
514 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
515 {
516
517 STDCALL(ukfs, rump_sys_lchmod(filename, mode));
518 }
519
520 int
521 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
522 {
523
524 STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
525 }
526
527 int
528 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
529 {
530
531 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
532 }
533
534 int
535 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
536 {
537
538 STDCALL(ukfs, rump_sys_chflags(filename, flags));
539 }
540
541 int
542 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
543 {
544
545 STDCALL(ukfs, rump_sys_lchflags(filename, flags));
546 }
547
548 int
549 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
550 {
551
552 STDCALL(ukfs, rump_sys_utimes(filename, tptr));
553 }
554
555 int
556 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
557 const struct timeval *tptr)
558 {
559
560 STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
561 }
562
563 /*
564 * Dynamic module support
565 */
566
567 /* load one library */
568
569 /*
570 * XXX: the dlerror stuff isn't really threadsafe, but then again I
571 * can't protect against other threads calling dl*() outside of ukfs,
572 * so just live with it being flimsy
573 */
574 #define UFSLIB "librumpfs_ufs.so"
575 int
576 ukfs_modload(const char *fname)
577 {
578 void *handle, *thesym;
579 struct stat sb;
580 const char *p;
581 int error;
582
583 if (stat(fname, &sb) == -1)
584 return -1;
585
586 handle = dlopen(fname, RTLD_GLOBAL);
587 if (handle == NULL) {
588 const char *dlmsg = dlerror();
589 if (strstr(dlmsg, "Undefined symbol"))
590 return 0;
591 warnx("dlopen %s failed: %s\n", fname, dlmsg);
592 /* XXXerrno */
593 return -1;
594 }
595
596 /*
597 * XXX: the ufs module is not loaded in the same fashion as the
598 * others. But we can't do dlclose() for it, since that would
599 * lead to not being able to load ffs/ext2fs/lfs. Hence hardcode
600 * and kludge around the issue for now. But this should really
601 * be fixed by fixing sys/ufs/ufs to be a kernel module.
602 */
603 if ((p = strrchr(fname, '/')) != NULL)
604 p++;
605 else
606 p = fname;
607 if (strcmp(p, UFSLIB) == 0)
608 return 1;
609
610 thesym = dlsym(handle, "__start_link_set_modules");
611 if (thesym) {
612 error = rump_module_load(thesym);
613 if (error)
614 goto errclose;
615 return 1;
616 }
617 error = EINVAL;
618
619 errclose:
620 dlclose(handle);
621 errno = error;
622 return -1;
623 }
624
625 struct loadfail {
626 char *pname;
627
628 LIST_ENTRY(loadfail) entries;
629 };
630
631 #define RUMPFSMOD_PREFIX "librumpfs_"
632 #define RUMPFSMOD_SUFFIX ".so"
633
634 int
635 ukfs_modload_dir(const char *dir)
636 {
637 char nbuf[MAXPATHLEN+1], *p;
638 struct dirent entry, *result;
639 DIR *libdir;
640 struct loadfail *lf, *nlf;
641 int error, nloaded = 0, redo;
642 LIST_HEAD(, loadfail) lfs;
643
644 libdir = opendir(dir);
645 if (libdir == NULL)
646 return -1;
647
648 LIST_INIT(&lfs);
649 for (;;) {
650 if ((error = readdir_r(libdir, &entry, &result)) != 0)
651 break;
652 if (!result)
653 break;
654 if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
655 strlen(RUMPFSMOD_PREFIX)) != 0)
656 continue;
657 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
658 || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
659 continue;
660 strlcpy(nbuf, dir, sizeof(nbuf));
661 strlcat(nbuf, "/", sizeof(nbuf));
662 strlcat(nbuf, result->d_name, sizeof(nbuf));
663 switch (ukfs_modload(nbuf)) {
664 case 0:
665 lf = malloc(sizeof(*lf));
666 if (lf == NULL) {
667 error = ENOMEM;
668 break;
669 }
670 lf->pname = strdup(nbuf);
671 if (lf->pname == NULL) {
672 free(lf);
673 error = ENOMEM;
674 break;
675 }
676 LIST_INSERT_HEAD(&lfs, lf, entries);
677 break;
678 case 1:
679 nloaded++;
680 break;
681 default:
682 /* ignore errors */
683 break;
684 }
685 }
686 closedir(libdir);
687 if (error && nloaded != 0)
688 error = 0;
689
690 /*
691 * El-cheapo dependency calculator. Just try to load the
692 * modules n times in a loop
693 */
694 for (redo = 1; redo;) {
695 redo = 0;
696 nlf = LIST_FIRST(&lfs);
697 while ((lf = nlf) != NULL) {
698 nlf = LIST_NEXT(lf, entries);
699 if (ukfs_modload(lf->pname) == 1) {
700 nloaded++;
701 redo = 1;
702 LIST_REMOVE(lf, entries);
703 free(lf->pname);
704 free(lf);
705 }
706 }
707 }
708
709 while ((lf = LIST_FIRST(&lfs)) != NULL) {
710 LIST_REMOVE(lf, entries);
711 free(lf->pname);
712 free(lf);
713 }
714
715 if (error && nloaded == 0) {
716 errno = error;
717 return -1;
718 }
719
720 return nloaded;
721 }
722
723 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
724 ssize_t
725 ukfs_vfstypes(char *buf, size_t buflen)
726 {
727 int mib[3];
728 struct sysctlnode q, ans[128];
729 size_t alen;
730 int i;
731
732 mib[0] = CTL_VFS;
733 mib[1] = VFS_GENERIC;
734 mib[2] = CTL_QUERY;
735 alen = sizeof(ans);
736
737 memset(&q, 0, sizeof(q));
738 q.sysctl_flags = SYSCTL_VERSION;
739
740 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
741 return -1;
742 }
743
744 for (i = 0; i < alen/sizeof(ans[0]); i++)
745 if (strcmp("fstypes", ans[i].sysctl_name) == 0)
746 break;
747 if (i == alen/sizeof(ans[0])) {
748 errno = ENXIO;
749 return -1;
750 }
751
752 mib[0] = CTL_VFS;
753 mib[1] = VFS_GENERIC;
754 mib[2] = ans[i].sysctl_num;
755
756 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
757 return -1;
758 }
759
760 return buflen;
761 }
762
763 /*
764 * Utilities
765 */
766 int
767 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
768 {
769 char *f1, *f2;
770 int rv;
771 mode_t mask;
772 bool end;
773
774 /*ukfs_umask((mask = ukfs_umask(0)));*/
775 umask((mask = umask(0)));
776
777 f1 = f2 = strdup(pathname);
778 if (f1 == NULL) {
779 errno = ENOMEM;
780 return -1;
781 }
782
783 end = false;
784 for (;;) {
785 /* find next component */
786 f2 += strspn(f2, "/");
787 f2 += strcspn(f2, "/");
788 if (*f2 == '\0')
789 end = true;
790 else
791 *f2 = '\0';
792
793 rv = ukfs_mkdir(ukfs, f1, mode & ~mask);
794 if (errno == EEXIST)
795 rv = 0;
796
797 if (rv == -1 || *f2 != '\0' || end)
798 break;
799
800 *f2 = '/';
801 }
802
803 free(f1);
804
805 return rv;
806 }
807