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