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