Home | History | Annotate | Line # | Download | only in libukfs
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