xref: /netbsd-src/lib/libukfs/ukfs.c (revision cac8e449158efc7261bebc8657cbb0125a2cfdde)
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