Home | History | Annotate | Line # | Download | only in libpuffs
null.c revision 1.5
      1 /*	$NetBSD: null.c,v 1.5 2007/01/27 11:49:44 agc Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2007  Antti Kantee.  All Rights Reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the company nor the name of the author may be used to
     15  *    endorse or promote products derived from this software without specific
     16  *    prior written permission.
     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 #include <sys/cdefs.h>
     32 #if !defined(lint)
     33 __RCSID("$NetBSD: null.c,v 1.5 2007/01/27 11:49:44 agc Exp $");
     34 #endif /* !lint */
     35 
     36 /*
     37  * A "nullfs" using puffs, i.e. maps one location in the hierarchy
     38  * to another using standard system calls.
     39  */
     40 
     41 #include <sys/types.h>
     42 #include <sys/time.h>
     43 
     44 #include <assert.h>
     45 #include <dirent.h>
     46 #include <errno.h>
     47 #include <fcntl.h>
     48 #include <puffs.h>
     49 #include <stdio.h>
     50 #include <unistd.h>
     51 
     52 PUFFSOP_PROTOS(puffs_null)
     53 
     54 /*ARGSUSED*/
     55 static void *
     56 pathcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
     57 {
     58 	struct puffs_pathobj *po = arg;
     59 
     60 	if (strcmp(po->po_path, PNPATH(pn)) == 0)
     61 		return pn;
     62 	return NULL;
     63 }
     64 
     65 /*
     66  * set attributes to what is specified.  XXX: no rollback in case of failure
     67  */
     68 static int
     69 processvattr(const char *path, const struct vattr *va, int regular)
     70 {
     71 	struct timeval tv[2];
     72 
     73 	/* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */
     74 	if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1)
     75 		if (lchown(path, va->va_uid, va->va_gid) == -1)
     76 			return errno;
     77 
     78 	if (va->va_mode != (unsigned)PUFFS_VNOVAL)
     79 		if (lchmod(path, va->va_mode) == -1)
     80 			return errno;
     81 
     82 	/* sloppy */
     83 	if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL
     84 	    || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) {
     85 		TIMESPEC_TO_TIMEVAL(&tv[0], &va->va_atime);
     86 		TIMESPEC_TO_TIMEVAL(&tv[1], &va->va_mtime);
     87 
     88 		if (lutimes(path, tv) == -1)
     89 			return errno;
     90 	}
     91 
     92 	if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL)
     93 		if (truncate(path, (off_t)va->va_size) == -1)
     94 			return errno;
     95 
     96 	return 0;
     97 }
     98 
     99 /*
    100  * Kludge to open files which aren't writable *any longer*.  This kinda
    101  * works because the vfs layer does validation checks based on the file's
    102  * permissions to allow writable opening before opening them.  However,
    103  * the problem arises if we want to create a file, write to it (cache),
    104  * adjust permissions and then flush the file.
    105  */
    106 static int
    107 writeableopen(const char *path)
    108 {
    109 	struct stat sb;
    110 	mode_t origmode;
    111 	int sverr = 0;
    112 	int fd;
    113 
    114 	fd = open(path, O_WRONLY);
    115 	if (fd == -1) {
    116 		if (errno == EACCES) {
    117 			if (stat(path, &sb) == -1)
    118 				return errno;
    119 			origmode = sb.st_mode & ALLPERMS;
    120 
    121 			if (chmod(path, 0200) == -1)
    122 				return errno;
    123 
    124 			fd = open(path, O_WRONLY);
    125 			if (fd == -1)
    126 				sverr = errno;
    127 
    128 			chmod(path, origmode);
    129 			if (sverr)
    130 				errno = sverr;
    131 		} else
    132 			return errno;
    133 	}
    134 
    135 	return fd;
    136 }
    137 
    138 /*ARGSUSED*/
    139 int
    140 puffs_null_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
    141 {
    142 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    143 
    144 	if (statvfs(PNPATH(pu->pu_pn_root), svfsb) == -1)
    145 		return errno;
    146 
    147 	return 0;
    148 }
    149 
    150 /*
    151  * Wow, this kinda looks like a real lookup since it bounces all
    152  * over the place
    153  */
    154 int
    155 puffs_null_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
    156 	enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
    157 	const struct puffs_cn *pcn)
    158 {
    159 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    160 	struct puffs_node *pn = opc, *pn_res;
    161 	struct dirent entry, *result;
    162 	char dpath[MAXPATHLEN+1];
    163 	char *npath;
    164 	struct stat sb;
    165 	DIR *dp;
    166 	int rv;
    167 
    168 	assert(pn->pn_va.va_type == VDIR);
    169 
    170 	/* XXX: push this out of here */
    171 	if (pcn->pcn_flags & PUFFS_ISDOTDOT) {
    172 		struct puffs_pathobj mypo;
    173 		char *p;
    174 
    175 		strcpy(dpath, PNPATH(pn));
    176 		p = strrchr(dpath, '/');
    177 		assert(p != NULL);
    178 		*p = '\0';
    179 
    180 		mypo.po_path = dpath;
    181 		mypo.po_len = strlen(dpath);
    182 		pn_res = puffs_pn_nodewalk(pu, pathcmp, &mypo);
    183 		if (pn_res)
    184 			goto success;
    185 		npath = dpath;
    186 		goto getnew;
    187 	}
    188 
    189 	dp = opendir(PNPATH(pn));
    190 	if (dp == NULL)
    191 		return errno;
    192 
    193 	/* more readable than for(), IMHO */
    194 	rv = readdir_r(dp, &entry, &result);
    195 	while (rv == 0 && result) {
    196 		if (strcmp(result->d_name, pcn->pcn_name) == 0)
    197 			break;
    198 		rv = readdir_r(dp, &entry, &result);
    199 	}
    200 	closedir(dp);
    201 
    202 	if (rv)
    203 		return rv;
    204 	if (!result)
    205 		return ENOENT;
    206 
    207 	/* XXX: UNCONST is wrong, fix the interface */
    208 	pn_res = puffs_pn_nodewalk(pu, pathcmp, __UNCONST(&pcn->pcn_po_full));
    209 	if (pn_res)
    210 		goto success;
    211 	npath = PCNPATH(pcn);
    212 
    213  getnew:
    214 	rv = lstat(npath, &sb);
    215 	if (rv)
    216 		return rv;
    217 	pn_res = puffs_pn_new(pu, NULL);
    218 	if (pn_res == NULL)
    219 		return ENOMEM;
    220 	puffs_stat2vattr(&pn_res->pn_va, &sb);
    221 
    222  success:
    223 	*newnode = pn_res;
    224 	*newtype = pn_res->pn_va.va_type;
    225 	*newsize = pn_res->pn_va.va_size;
    226 	*newrdev = pn_res->pn_va.va_rdev;
    227 
    228 	return 0;
    229 }
    230 
    231 /*ARGSUSED*/
    232 int
    233 puffs_null_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
    234 	const struct puffs_cn *pcn, const struct vattr *va)
    235 {
    236 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    237 	struct puffs_node *pn;
    238 	int fd, rv;
    239 
    240 	fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC);
    241 	if (fd == -1)
    242 		return errno;
    243 	close(fd);
    244 	if ((rv = processvattr(PCNPATH(pcn), va, 1)) != 0) {
    245 		unlink(PCNPATH(pcn));
    246 		return rv;
    247 	}
    248 
    249 	pn = puffs_pn_new(pu, NULL);
    250 	if (!pn) {
    251 		unlink(PCNPATH(pcn));
    252 		return ENOMEM;
    253 	}
    254 	puffs_setvattr(&pn->pn_va, va);
    255 
    256 	*newnode = pn;
    257 	return 0;
    258 }
    259 
    260 /*ARGSUSED*/
    261 int
    262 puffs_null_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
    263 	const struct puffs_cn *pcn, const struct vattr *va)
    264 {
    265 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    266 	struct puffs_node *pn;
    267 	int rv;
    268 
    269 	if (mknod(PCNPATH(pcn), va->va_mode, va->va_rdev) == -1)
    270 		return errno;
    271 
    272 	if ((rv = processvattr(PCNPATH(pcn), va, 0)) != 0) {
    273 		unlink(PCNPATH(pcn));
    274 		return rv;
    275 	}
    276 
    277 	pn = puffs_pn_new(pu, NULL);
    278 	if (!pn) {
    279 		unlink(PCNPATH(pcn));
    280 		return ENOMEM;
    281 	}
    282 	puffs_setvattr(&pn->pn_va, va);
    283 
    284 	*newnode = pn;
    285 	return 0;
    286 }
    287 
    288 /*ARGSUSED*/
    289 int
    290 puffs_null_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
    291 	const struct puffs_cred *pcred, pid_t pid)
    292 {
    293 	struct puffs_node *pn = opc;
    294 	struct stat sb;
    295 
    296 	if (lstat(PNPATH(pn), &sb) == -1)
    297 		return errno;
    298 	puffs_stat2vattr(va, &sb);
    299 
    300 	return 0;
    301 }
    302 
    303 /*ARGSUSED*/
    304 int
    305 puffs_null_node_setattr(struct puffs_cc *pcc, void *opc,
    306 	const struct vattr *va, const struct puffs_cred *pcred, pid_t pid)
    307 {
    308 	struct puffs_node *pn = opc;
    309 	int rv;
    310 
    311 	rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG);
    312 	if (rv)
    313 		return rv;
    314 
    315 	puffs_setvattr(&pn->pn_va, va);
    316 
    317 	return 0;
    318 }
    319 
    320 /*ARGSUSED*/
    321 int
    322 puffs_null_node_fsync(struct puffs_cc *pcc, void *opc,
    323 	const struct puffs_cred *pcred, int how,
    324 	off_t offlo, off_t offhi, pid_t pid)
    325 {
    326 	struct puffs_node *pn = opc;
    327 	int fd, rv;
    328 	int fflags;
    329 
    330 	rv = 0;
    331 	fd = writeableopen(PNPATH(pn));
    332 	if (fd == -1)
    333 		return errno;
    334 
    335 	if (how & PUFFS_FSYNC_DATAONLY)
    336 		fflags = FDATASYNC;
    337 	else
    338 		fflags = FFILESYNC;
    339 	if (how & PUFFS_FSYNC_CACHE)
    340 		fflags |= FDISKSYNC;
    341 
    342 	if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1)
    343 		rv = errno;
    344 
    345 	close(fd);
    346 
    347 	return rv;
    348 }
    349 
    350 /*ARGSUSED*/
    351 int
    352 puffs_null_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
    353 	const struct puffs_cn *pcn)
    354 {
    355 	struct puffs_node *pn_targ = targ;
    356 
    357 	if (unlink(PNPATH(pn_targ)) == -1)
    358 		return errno;
    359 
    360 	return 0;
    361 }
    362 
    363 /*ARGSUSED*/
    364 int
    365 puffs_null_node_link(struct puffs_cc *pcc, void *opc, void *targ,
    366 	const struct puffs_cn *pcn)
    367 {
    368 	struct puffs_node *pn_targ = targ;
    369 
    370 	if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1)
    371 		return errno;
    372 
    373 	return 0;
    374 }
    375 
    376 /*ARGSUSED*/
    377 int
    378 puffs_null_node_rename(struct puffs_cc *pcc, void *opc, void *src,
    379 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    380 	const struct puffs_cn *pcn_targ)
    381 {
    382 
    383 	if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1)
    384 		return errno;
    385 
    386 	return 0;
    387 }
    388 
    389 /*ARGSUSED*/
    390 int
    391 puffs_null_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
    392 	const struct puffs_cn *pcn, const struct vattr *va)
    393 {
    394 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    395 	struct puffs_node *pn;
    396 	int rv;
    397 
    398 	if (mkdir(PCNPATH(pcn), va->va_mode) == -1)
    399 		return errno;
    400 
    401 	if ((rv = processvattr(PCNPATH(pcn), va, 0)) != 0) {
    402 		rmdir(PCNPATH(pcn));
    403 		return rv;
    404 	}
    405 
    406 	pn = puffs_pn_new(pu, NULL);
    407 	if (pn == NULL) {
    408 		rmdir(PCNPATH(pcn));
    409 		return ENOMEM;
    410 	}
    411 	puffs_setvattr(&pn->pn_va, va);
    412 
    413 	*newnode = pn;
    414 	return 0;
    415 }
    416 
    417 /*ARGSUSED*/
    418 int
    419 puffs_null_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
    420 	const struct puffs_cn *pcn)
    421 {
    422 	struct puffs_node *pn_targ = targ;
    423 
    424 	if (rmdir(PNPATH(pn_targ)) == -1)
    425 		return errno;
    426 
    427 	return 0;
    428 }
    429 
    430 /*ARGSUSED*/
    431 int
    432 puffs_null_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
    433 	const struct puffs_cn *pcn, const struct vattr *va,
    434 	const char *linkname)
    435 {
    436 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    437 	struct puffs_node *pn;
    438 	int rv;
    439 
    440 	if (symlink(linkname, PCNPATH(pcn)) == -1)
    441 		return errno;
    442 
    443 	if ((rv = processvattr(PCNPATH(pcn), va, 0)) != 0) {
    444 		unlink(PCNPATH(pcn));
    445 		return rv;
    446 	}
    447 
    448 	pn = puffs_pn_new(pu, NULL);
    449 	if (pn == NULL) {
    450 		rmdir(PCNPATH(pcn));
    451 		return ENOMEM;
    452 	}
    453 	puffs_setvattr(&pn->pn_va, va);
    454 
    455 	*newnode = pn;
    456 	return 0;
    457 }
    458 
    459 /*ARGSUSED*/
    460 int
    461 puffs_null_node_readlink(struct puffs_cc *pcc, void *opc,
    462 	const struct puffs_cred *pcred, char *linkname, size_t *linklen)
    463 {
    464 	struct puffs_node *pn = opc;
    465 	ssize_t rv;
    466 
    467 	rv = readlink(PNPATH(pn), linkname, *linklen);
    468 	if (rv == -1)
    469 		return errno;
    470 
    471 	*linklen -= rv;
    472 	return 0;
    473 }
    474 
    475 /*ARGSUSED*/
    476 int
    477 puffs_null_node_readdir(struct puffs_cc *pcc, void *opc, struct dirent *de,
    478 	const struct puffs_cred *pcred, off_t *off, size_t *reslen)
    479 {
    480 	struct puffs_node *pn = opc;
    481 	struct dirent entry, *result;
    482 	DIR *dp;
    483 	off_t i;
    484 	int rv;
    485 
    486 	dp = opendir(PNPATH(pn));
    487 	if (dp == NULL)
    488 		return errno;
    489 
    490 	rv = 0;
    491 	i = *off;
    492 
    493 	/*
    494 	 * XXX: need to do trickery here, telldir/seekdir would be nice, but
    495 	 * then we'd need to keep state, which I'm too lazy to keep
    496 	 */
    497 	while (i--) {
    498 		rv = readdir_r(dp, &entry, &result);
    499 		if (rv || !result)
    500 			goto out;
    501 	}
    502 
    503 	for (;;) {
    504 		rv = readdir_r(dp, &entry, &result);
    505 		if (rv != 0)
    506 			goto out;
    507 
    508 		if (!result)
    509 			goto out;
    510 
    511 		if (_DIRENT_SIZE(result) > *reslen)
    512 			goto out;
    513 
    514 		*de = *result;
    515 		*reslen -= _DIRENT_SIZE(result);
    516 		de = _DIRENT_NEXT(de);
    517 
    518 		(*off)++;
    519 	}
    520 
    521  out:
    522 	closedir(dp);
    523 	return 0;
    524 }
    525 
    526 /*ARGSUSED*/
    527 int
    528 puffs_null_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    529 	off_t offset, size_t *buflen, const struct puffs_cred *pcred,
    530 	int ioflag)
    531 {
    532 	struct puffs_node *pn = opc;
    533 	ssize_t n;
    534 	off_t off;
    535 	int fd, rv;
    536 
    537 	rv = 0;
    538 	fd = open(PNPATH(pn), O_RDONLY);
    539 	if (fd == -1)
    540 		return errno;
    541 	off = lseek(fd, offset, SEEK_SET);
    542 	if (off == -1) {
    543 		rv = errno;
    544 		goto out;
    545 	}
    546 
    547 	n = read(fd, buf, *buflen);
    548 	if (n == -1)
    549 		rv = errno;
    550 	else
    551 		*buflen -= n;
    552 
    553  out:
    554 	close(fd);
    555 	return rv;
    556 }
    557 
    558 /*ARGSUSED*/
    559 int
    560 puffs_null_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    561 	off_t offset, size_t *buflen, const struct puffs_cred *pcred,
    562 	int ioflag)
    563 {
    564 	struct puffs_node *pn = opc;
    565 	ssize_t n;
    566 	off_t off;
    567 	int fd, rv;
    568 
    569 	rv = 0;
    570 	fd = writeableopen(PNPATH(pn));
    571 	if (fd == -1)
    572 		return errno;
    573 
    574 	off = lseek(fd, offset, SEEK_SET);
    575 	if (off == -1) {
    576 		rv = errno;
    577 		goto out;
    578 	}
    579 
    580 	n = write(fd, buf, *buflen);
    581 	if (n == -1)
    582 		rv = errno;
    583 	else
    584 		*buflen -= n;
    585 
    586  out:
    587 	close(fd);
    588 	return rv;
    589 }
    590