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