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