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