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