Home | History | Annotate | Line # | Download | only in librefuse
refuse.c revision 1.41
      1 /*	$NetBSD: refuse.c,v 1.41 2007/03/13 20:50:47 agc Exp $	*/
      2 
      3 /*
      4  * Copyright  2007 Alistair Crooks.  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 author may not be used to endorse or promote
     15  *    products derived from this software without specific prior written
     16  *    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
     21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     24  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 #if !defined(lint)
     33 __RCSID("$NetBSD: refuse.c,v 1.41 2007/03/13 20:50:47 agc Exp $");
     34 #endif /* !lint */
     35 
     36 #include <assert.h>
     37 #include <err.h>
     38 #include <errno.h>
     39 #include <fuse.h>
     40 #include <unistd.h>
     41 
     42 #include "defs.h"
     43 
     44 /*
     45 This module implements refuse, a re-implementation of the FUSE model,
     46 using puffs and libpuffs.  It is intended to be source code compatible
     47 with FUSE.  Specifically, it implements FUSE versions 2.5 and 2.6,
     48 although some effort was put in to make it backwards compatible (by
     49 Antti, not me).
     50 
     51 The error codes returned from refuse require some explanation.  Linux
     52 error codes in the kernel are negative, whereas traditional NetBSD
     53 error codes are positive.  For this reason, negative error codes are
     54 returned from refuse, so that they are reported correctly to the
     55 invoking application.
     56 */
     57 
     58 typedef uint64_t	 fuse_ino_t;
     59 
     60 struct fuse_config {
     61 	uid_t		uid;
     62 	gid_t		gid;
     63 	mode_t		umask;
     64 	double		entry_timeout;
     65 	double		negative_timeout;
     66 	double		attr_timeout;
     67 	double		ac_attr_timeout;
     68 	int		ac_attr_timeout_set;
     69 	int		debug;
     70 	int		hard_remove;
     71 	int		use_ino;
     72 	int		readdir_ino;
     73 	int		set_mode;
     74 	int		set_uid;
     75 	int		set_gid;
     76 	int		direct_io;
     77 	int		kernel_cache;
     78 	int		auto_cache;
     79 	int		intr;
     80 	int		intr_signal;
     81 };
     82 
     83 /* the fuse channel describes the physical attributes of the FUSE instance */
     84 struct fuse_chan {
     85 	const char		*dir;		/* directory */
     86 	struct fuse_args	*args;		/* arguments to FUSE/puffs */
     87 	struct puffs_usermount	*pu;		/* puffs information */
     88 };
     89 
     90 /* this is the private fuse structure */
     91 struct fuse {
     92 	struct fuse_chan	*fc;		/* fuse channel pointer */
     93 	struct fuse_operations	op;		/* switch table of operations */
     94 	int			compat;		/* compat level -
     95 						 * not used in puffs_fuse */
     96 	struct node		**name_table;
     97 	size_t			name_table_size;
     98 	struct node		**id_table;
     99 	size_t			id_table_size;
    100 	fuse_ino_t		ctr;
    101 	unsigned int		generation;
    102 	unsigned int		hidectr;
    103 	pthread_mutex_t		lock;
    104 	pthread_rwlock_t	tree_lock;
    105 	void			*user_data;
    106 	struct fuse_config	conf;
    107 	int			intr_installed;
    108 };
    109 
    110 /* this struct describes a directory handle */
    111 struct puffs_fuse_dirh {
    112 	void		*dbuf;			/* directory buffer */
    113 	struct dirent	*d;			/* pointer to its dirent */
    114 	size_t		reslen;			/* result length */
    115 	size_t		bufsize;		/* buffer size */
    116 };
    117 
    118 /* this struct describes a node in refuse land */
    119 typedef struct refusenode {
    120 	struct fuse_file_info	file_info;	/* file information */
    121 	struct puffs_fuse_dirh	dirh;		/* directory handle */
    122 	int			opencount;	/* # of times opened */
    123 	int			flags;		/* associated flags */
    124 } refusenode_t;
    125 #define RN_ROOT		0x01
    126 #define RN_OPEN		0x02	/* XXX: could just use opencount */
    127 
    128 /* debugging functions */
    129 
    130 /* change the debug level by increment */
    131 int
    132 __fuse_debug(int incr)
    133 {
    134 	static int	__fuse_debug_level;
    135 
    136 	return __fuse_debug_level += incr;
    137 }
    138 
    139 /* print argv vector */
    140 void
    141 __fuse_pargs(const char *s, int argc, char **argv)
    142 {
    143 	int	i;
    144 
    145 	for (i = 0 ; i < argc ; i++) {
    146 		printf("%s: argv[%d] = `%s'\n", s, i, argv[i]);
    147 	}
    148 }
    149 
    150 
    151 /* create a new struct puffs_node and return it */
    152 static struct puffs_node *
    153 newrn(struct puffs_usermount *pu)
    154 {
    155 	struct puffs_node *pn;
    156 	struct refusenode *rn;
    157 
    158 	NEW(struct refusenode, rn, "newrn", exit(EXIT_FAILURE));
    159 	pn = puffs_pn_new(pu, rn);
    160 
    161 	return pn;
    162 }
    163 
    164 /* destroy a struct puffs_node */
    165 static void
    166 nukern(struct puffs_node *pn)
    167 {
    168 	struct refusenode *rn = pn->pn_data;
    169 
    170 	free(rn->dirh.dbuf);
    171 	free(rn);
    172 	puffs_pn_put(pn);
    173 }
    174 
    175 static ino_t fakeino = 3;
    176 
    177 /*
    178  * XXX: do this differently if/when we grow thread support
    179  *
    180  * XXX2: does not consistently supply uid, gid or pid currently
    181  */
    182 static struct fuse_context fcon;
    183 
    184 #define DIR_CHUNKSIZE 4096
    185 static int
    186 fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino,
    187 	uint8_t dtype)
    188 {
    189 
    190 	/* initial? */
    191 	if (dh->bufsize == 0) {
    192 		dh->dbuf = malloc(DIR_CHUNKSIZE);
    193 		if (dh->dbuf == NULL) {
    194 			err(EXIT_FAILURE, "fill_dirbuf");
    195 		}
    196 		dh->d = dh->dbuf;
    197 		dh->reslen = dh->bufsize = DIR_CHUNKSIZE;
    198 	}
    199 
    200 	if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen)) {
    201 		return 0;
    202 	}
    203 
    204 	/* try to increase buffer space */
    205 	dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE);
    206 	if (dh->dbuf == NULL) {
    207 		err(EXIT_FAILURE, "fill_dirbuf realloc");
    208 	}
    209 	dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen));
    210 	dh->reslen += DIR_CHUNKSIZE;
    211 	dh->bufsize += DIR_CHUNKSIZE;
    212 
    213 	return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen);
    214 }
    215 
    216 /* ARGSUSED3 */
    217 /* XXX: I have no idea how "off" is supposed to be used */
    218 static int
    219 puffs_fuse_fill_dir(void *buf, const char *name,
    220 	const struct stat *stbuf, off_t off)
    221 {
    222 	struct puffs_fuse_dirh *deh = buf;
    223 	ino_t dino;
    224 	uint8_t dtype;
    225 
    226 	if (stbuf == NULL) {
    227 		dtype = DT_UNKNOWN;
    228 		dino = fakeino++;
    229 	} else {
    230 		dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
    231 		dino = stbuf->st_ino;
    232 	}
    233 
    234 	return fill_dirbuf(deh, name, dino, dtype);
    235 }
    236 
    237 static int
    238 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
    239 {
    240 	ino_t	dino;
    241 	int	dtype;
    242 
    243 	if ((dtype = type) == 0) {
    244 		dtype = DT_UNKNOWN;
    245 	}
    246 	if ((dino = ino) == 0) {
    247 		dino = fakeino++;
    248 	}
    249 
    250 	return fill_dirbuf(h, name, dino, dtype);
    251 }
    252 
    253 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
    254 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
    255 
    256 /* ARGSUSED1 */
    257 static int
    258 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
    259 	struct vattr *va)
    260 {
    261 	struct stat		 st;
    262 	int			ret;
    263 
    264 	if (fuse->op.getattr == NULL) {
    265 		return ENOSYS;
    266 	}
    267 
    268 	/* wrap up return code */
    269 	ret = (*fuse->op.getattr)(path, &st);
    270 
    271 	if (ret == 0) {
    272 		puffs_stat2vattr(va, &st);
    273 	}
    274 
    275 	return -ret;
    276 }
    277 
    278 static int
    279 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
    280 	const struct vattr *va)
    281 {
    282 	struct refusenode	*rn = pn->pn_data;
    283 	mode_t			mode;
    284 	uid_t			uid;
    285 	gid_t			gid;
    286 	int			error, ret;
    287 
    288 	error = 0;
    289 
    290 	mode = va->va_mode;
    291 	uid = va->va_uid;
    292 	gid = va->va_gid;
    293 
    294 	if (mode != (mode_t)PUFFS_VNOVAL) {
    295 		ret = 0;
    296 
    297 		if (fuse->op.chmod == NULL) {
    298 			error = -ENOSYS;
    299 		} else {
    300 			ret = fuse->op.chmod(path, mode);
    301 			if (ret)
    302 				error = ret;
    303 		}
    304 	}
    305 	if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
    306 		ret = 0;
    307 
    308 		if (fuse->op.chown == NULL) {
    309 			error = -ENOSYS;
    310 		} else {
    311 			ret = fuse->op.chown(path, uid, gid);
    312 			if (ret)
    313 				error = ret;
    314 		}
    315 	}
    316 	if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
    317 	    || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
    318 		ret = 0;
    319 
    320 		if (fuse->op.utimens) {
    321 			struct timespec tv[2];
    322 
    323 			tv[0].tv_sec = va->va_atime.tv_sec;
    324 			tv[0].tv_nsec = va->va_atime.tv_nsec;
    325 			tv[1].tv_sec = va->va_mtime.tv_sec;
    326 			tv[1].tv_nsec = va->va_mtime.tv_nsec;
    327 
    328 			ret = fuse->op.utimens(path, tv);
    329 		} else if (fuse->op.utime) {
    330 			struct utimbuf timbuf;
    331 
    332 			timbuf.actime = va->va_atime.tv_sec;
    333 			timbuf.modtime = va->va_mtime.tv_sec;
    334 
    335 			ret = fuse->op.utime(path, &timbuf);
    336 		} else {
    337 			error = -ENOSYS;
    338 		}
    339 
    340 		if (ret)
    341 			error = ret;
    342 	}
    343 	if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
    344 		ret = 0;
    345 
    346 		if (fuse->op.truncate) {
    347 			ret = fuse->op.truncate(path, (off_t)va->va_size);
    348 		} else if (fuse->op.ftruncate) {
    349 			ret = fuse->op.ftruncate(path, (off_t)va->va_size,
    350 			    &rn->file_info);
    351 		} else {
    352 			error = -ENOSYS;
    353 		}
    354 
    355 		if (ret)
    356 			error = ret;
    357 	}
    358 	/* XXX: no reflection with reality */
    359 	puffs_setvattr(&pn->pn_va, va);
    360 
    361 	return -error;
    362 
    363 }
    364 
    365 static int
    366 fuse_newnode(struct puffs_usermount *pu, const char *path,
    367 	const struct vattr *va, struct fuse_file_info *fi, void **newnode)
    368 {
    369 	struct vattr		newva;
    370 	struct fuse		*fuse;
    371 	struct puffs_node	*pn;
    372 	struct refusenode	*rn;
    373 
    374 	fuse = (struct fuse *)pu->pu_privdata;
    375 
    376 	/* fix up nodes */
    377 	pn = newrn(pu);
    378 	if (pn == NULL) {
    379 		if (va->va_type == VDIR) {
    380 			FUSE_ERR_RMDIR(fuse, path);
    381 		} else {
    382 			FUSE_ERR_UNLINK(fuse, path);
    383 		}
    384 		return ENOMEM;
    385 	}
    386 	fuse_setattr(fuse, pn, path, va);
    387 	if (fuse_getattr(fuse, pn, path, &newva) == 0)
    388 		puffs_setvattr(&pn->pn_va, &newva);
    389 
    390 	rn = pn->pn_data;
    391 	if (fi)
    392 		memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
    393 
    394 	*newnode = pn;
    395 
    396 	return 0;
    397 }
    398 
    399 
    400 /* operation wrappers start here */
    401 
    402 /* lookup the path */
    403 /* ARGSUSED1 */
    404 static int
    405 puffs_fuse_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
    406 	enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
    407 	const struct puffs_cn *pcn)
    408 {
    409 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    410 	struct puffs_node	*pn_res;
    411 	struct stat		st;
    412 	struct fuse		*fuse;
    413 	const char		*path = PCNPATH(pcn);
    414 	int			ret;
    415 
    416 	fuse = (struct fuse *)pu->pu_privdata;
    417 	ret = fuse->op.getattr(path, &st);
    418 
    419 	if (ret != 0) {
    420 		return -ret;
    421 	}
    422 
    423 	/* XXX: fiXXXme unconst */
    424 	pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
    425 	    __UNCONST(&pcn->pcn_po_full));
    426 	if (pn_res == NULL) {
    427 		pn_res = newrn(pu);
    428 		if (pn_res == NULL)
    429 			return errno;
    430 		puffs_stat2vattr(&pn_res->pn_va, &st);
    431 	}
    432 
    433 	*newnode = pn_res;
    434 	*newtype = pn_res->pn_va.va_type;
    435 	*newsize = pn_res->pn_va.va_size;
    436 	*newrdev = pn_res->pn_va.va_rdev;
    437 
    438 	return 0;
    439 }
    440 
    441 /* get attributes for the path name */
    442 /* ARGSUSED3 */
    443 static int
    444 puffs_fuse_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
    445 	const struct puffs_cred *pcr, pid_t pid)
    446 {
    447 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    448 	struct puffs_node	*pn = opc;
    449 	struct fuse		*fuse;
    450 	const char		*path = PNPATH(pn);
    451 
    452 	fuse = (struct fuse *)pu->pu_privdata;
    453 	return fuse_getattr(fuse, pn, path, va);
    454 }
    455 
    456 /* read the contents of the symbolic link */
    457 /* ARGSUSED2 */
    458 static int
    459 puffs_fuse_node_readlink(struct puffs_cc *pcc, void *opc,
    460 	const struct puffs_cred *cred, char *linkname, size_t *linklen)
    461 {
    462 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    463 	struct puffs_node	*pn = opc;
    464 	struct fuse		*fuse;
    465 	const char		*path = PNPATH(pn), *p;
    466 	int			ret;
    467 
    468 	fuse = (struct fuse *)pu->pu_privdata;
    469 	if (fuse->op.readlink == NULL) {
    470 		return ENOSYS;
    471 	}
    472 
    473 	/* wrap up return code */
    474 	ret = (*fuse->op.readlink)(path, linkname, *linklen);
    475 
    476 	if (ret == 0) {
    477 		p = memchr(linkname, 0x0, *linklen);
    478 		if (!p)
    479 			return EINVAL;
    480 
    481 		*linklen = p - linkname;
    482 	}
    483 
    484 	return -ret;
    485 }
    486 
    487 /* make the special node */
    488 /* ARGSUSED1 */
    489 static int
    490 puffs_fuse_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
    491 	const struct puffs_cn *pcn, const struct vattr *va)
    492 {
    493 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    494 	struct fuse		*fuse;
    495 	mode_t			 mode = va->va_mode;
    496 	const char		*path = PCNPATH(pcn);
    497 	int			ret;
    498 
    499 	fuse = (struct fuse *)pu->pu_privdata;
    500 	if (fuse->op.mknod == NULL) {
    501 		return ENOSYS;
    502 	}
    503 
    504 	/* wrap up return code */
    505 	ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
    506 
    507 	if (ret == 0) {
    508 		ret = fuse_newnode(pu, path, va, NULL, newnode);
    509 	}
    510 
    511 	return -ret;
    512 }
    513 
    514 /* make a directory */
    515 /* ARGSUSED1 */
    516 static int
    517 puffs_fuse_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
    518 	const struct puffs_cn *pcn, const struct vattr *va)
    519 {
    520 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    521 	struct fuse		*fuse;
    522 	mode_t			 mode = va->va_mode;
    523 	const char		*path = PCNPATH(pcn);
    524 	int			ret;
    525 
    526 	fuse = (struct fuse *)pu->pu_privdata;
    527 	if (fuse->op.mkdir == NULL) {
    528 		return ENOSYS;
    529 	}
    530 
    531 	/* wrap up return code */
    532 	ret = (*fuse->op.mkdir)(path, mode);
    533 
    534 	if (ret == 0) {
    535 		ret = fuse_newnode(pu, path, va, NULL, newnode);
    536 	}
    537 
    538 	return -ret;
    539 }
    540 
    541 /*
    542  * create a regular file
    543  *
    544  * since linux/fuse sports using mknod for creating regular files
    545  * instead of having a separate call for it in some versions, if
    546  * we don't have create, just jump to op->mknod.
    547  */
    548 /*ARGSUSED1*/
    549 static int
    550 puffs_fuse_node_create(struct puffs_cc *pcc, void *opc, void **newnode,
    551 	const struct puffs_cn *pcn, const struct vattr *va)
    552 {
    553 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    554 	struct fuse		*fuse;
    555 	struct fuse_file_info	fi;
    556 	mode_t			mode = va->va_mode;
    557 	const char		*path = PCNPATH(pcn);
    558 	int			ret, created;
    559 
    560 	fuse = (struct fuse *)pu->pu_privdata;
    561 
    562 	created = 0;
    563 	if (fuse->op.create) {
    564 		ret = fuse->op.create(path, mode, &fi);
    565 		if (ret == 0)
    566 			created = 1;
    567 
    568 	} else if (fuse->op.mknod) {
    569 		fcon.uid = va->va_uid; /*XXX*/
    570 		fcon.gid = va->va_gid; /*XXX*/
    571 
    572 		ret = fuse->op.mknod(path, mode | S_IFREG, 0);
    573 
    574 	} else {
    575 		ret = -ENOSYS;
    576 	}
    577 
    578 	if (ret == 0) {
    579 		ret = fuse_newnode(pu, path, va, &fi, newnode);
    580 
    581 		/* sweet..  create also open the file */
    582 		if (created) {
    583 			struct puffs_node *pn;
    584 			struct refusenode *rn;
    585 
    586 			pn = *newnode;
    587 			rn = pn->pn_data;
    588 			rn->flags |= RN_OPEN;
    589 			rn->opencount++;
    590 		}
    591 	}
    592 
    593 	return -ret;
    594 }
    595 
    596 /* remove the directory entry */
    597 /* ARGSUSED1 */
    598 static int
    599 puffs_fuse_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
    600 	const struct puffs_cn *pcn)
    601 {
    602 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    603 	struct puffs_node	*pn_targ = targ;
    604 	struct fuse		*fuse;
    605 	const char		*path = PNPATH(pn_targ);
    606 	int			ret;
    607 
    608 	fuse = (struct fuse *)pu->pu_privdata;
    609 	if (fuse->op.unlink == NULL) {
    610 		return ENOSYS;
    611 	}
    612 
    613 	/* wrap up return code */
    614 	ret = (*fuse->op.unlink)(path);
    615 
    616 	return -ret;
    617 }
    618 
    619 /* remove the directory */
    620 /* ARGSUSED1 */
    621 static int
    622 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
    623 	const struct puffs_cn *pcn)
    624 {
    625 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    626 	struct puffs_node	*pn_targ = targ;
    627 	struct fuse		*fuse;
    628 	const char		*path = PNPATH(pn_targ);
    629 	int			ret;
    630 
    631 	fuse = (struct fuse *)pu->pu_privdata;
    632 	if (fuse->op.rmdir == NULL) {
    633 		return ENOSYS;
    634 	}
    635 
    636 	/* wrap up return code */
    637 	ret = (*fuse->op.rmdir)(path);
    638 
    639 	return -ret;
    640 }
    641 
    642 /* create a symbolic link */
    643 /* ARGSUSED1 */
    644 static int
    645 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
    646 	const struct puffs_cn *pcn_src, const struct vattr *va,
    647 	const char *link_target)
    648 {
    649 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    650 	struct fuse		*fuse;
    651 	const char		*path = PCNPATH(pcn_src);
    652 	int			ret;
    653 
    654 	fuse = (struct fuse *)pu->pu_privdata;
    655 	if (fuse->op.symlink == NULL) {
    656 		return ENOSYS;
    657 	}
    658 
    659 	/* wrap up return code */
    660 	ret = fuse->op.symlink(link_target, path);
    661 
    662 	if (ret == 0) {
    663 		ret = fuse_newnode(pu, path, va, NULL, newnode);
    664 	}
    665 
    666 	return -ret;
    667 }
    668 
    669 /* rename a directory entry */
    670 /* ARGSUSED1 */
    671 static int
    672 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
    673 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    674 	const struct puffs_cn *pcn_targ)
    675 {
    676 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    677 	struct fuse		*fuse;
    678 	const char		*path_src = PCNPATH(pcn_src);
    679 	const char		*path_dest = PCNPATH(pcn_targ);
    680 	int			ret;
    681 
    682 	fuse = (struct fuse *)pu->pu_privdata;
    683 	if (fuse->op.rename == NULL) {
    684 		return ENOSYS;
    685 	}
    686 
    687 	ret = fuse->op.rename(path_src, path_dest);
    688 
    689 	if (ret == 0) {
    690 	}
    691 
    692 	return -ret;
    693 }
    694 
    695 /* create a link in the file system */
    696 /* ARGSUSED1 */
    697 static int
    698 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
    699 	const struct puffs_cn *pcn)
    700 {
    701 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    702 	struct puffs_node	*pn = targ;
    703 	struct fuse		*fuse;
    704 	int			ret;
    705 
    706 	fuse = (struct fuse *)pu->pu_privdata;
    707 	if (fuse->op.link == NULL) {
    708 		return ENOSYS;
    709 	}
    710 
    711 	/* wrap up return code */
    712 	ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
    713 
    714 	return -ret;
    715 }
    716 
    717 /*
    718  * fuse's regular interface provides chmod(), chown(), utimes()
    719  * and truncate() + some variations, so try to fit the square block
    720  * in the circle hole and the circle block .... something like that
    721  */
    722 /* ARGSUSED3 */
    723 static int
    724 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
    725 	const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
    726 {
    727 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    728 	struct puffs_node	*pn = opc;
    729 	struct fuse		*fuse;
    730 	const char		*path = PNPATH(pn);
    731 
    732 	fuse = (struct fuse *)pu->pu_privdata;
    733 
    734 	return fuse_setattr(fuse, pn, path, va);
    735 }
    736 
    737 /* ARGSUSED2 */
    738 static int
    739 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int mode,
    740 	const struct puffs_cred *cred, pid_t pid)
    741 {
    742 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    743 	struct puffs_node	*pn = opc;
    744 	struct refusenode	*rn = pn->pn_data;
    745 	struct fuse_file_info	*fi = &rn->file_info;
    746 	struct fuse		*fuse;
    747 	const char		*path = PNPATH(pn);
    748 
    749 	fuse = (struct fuse *)pu->pu_privdata;
    750 
    751 	/* if open, don't open again, lest risk nuking file private info */
    752 	if (rn->flags & RN_OPEN) {
    753 		rn->opencount++;
    754 		return 0;
    755 	}
    756 
    757 	/* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
    758 	fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
    759 
    760 	if (pn->pn_va.va_type == VDIR) {
    761 		if (fuse->op.opendir)
    762 			fuse->op.opendir(path, fi);
    763 	} else {
    764 		if (fuse->op.open)
    765 			fuse->op.open(path, fi);
    766 	}
    767 
    768 	rn->flags |= RN_OPEN;
    769 	rn->opencount++;
    770 
    771 	return 0;
    772 }
    773 
    774 /* ARGSUSED2 */
    775 static int
    776 puffs_fuse_node_close(struct puffs_cc *pcc, void *opc, int fflag,
    777 	const struct puffs_cred *pcr, pid_t pid)
    778 {
    779 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    780 	struct puffs_node	*pn = opc;
    781 	struct refusenode	*rn = pn->pn_data;
    782 	struct fuse		*fuse;
    783 	struct fuse_file_info	*fi;
    784 	const char		*path = PNPATH(pn);
    785 	int			ret;
    786 
    787 	fuse = (struct fuse *)pu->pu_privdata;
    788 	fi = &rn->file_info;
    789 	ret = 0;
    790 
    791 	if (rn->flags & RN_OPEN) {
    792 		if (pn->pn_va.va_type == VDIR) {
    793 			if (fuse->op.releasedir)
    794 				ret = fuse->op.releasedir(path, fi);
    795 		} else {
    796 			if (fuse->op.release)
    797 				ret = fuse->op.release(path, fi);
    798 		}
    799 	}
    800 	rn->flags &= ~RN_OPEN;
    801 	rn->opencount--;
    802 
    803 	return ret;
    804 }
    805 
    806 /* read some more from the file */
    807 /* ARGSUSED5 */
    808 static int
    809 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    810 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    811 	int ioflag)
    812 {
    813 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    814 	struct puffs_node	*pn = opc;
    815 	struct refusenode	*rn = pn->pn_data;
    816 	struct fuse		*fuse;
    817 	const char		*path = PNPATH(pn);
    818 	size_t			maxread;
    819 	int			ret;
    820 
    821 	fuse = (struct fuse *)pu->pu_privdata;
    822 	if (fuse->op.read == NULL) {
    823 		return ENOSYS;
    824 	}
    825 
    826 	maxread = *resid;
    827 	if (maxread > pn->pn_va.va_size - offset) {
    828 		/*LINTED*/
    829 		maxread = pn->pn_va.va_size - offset;
    830 	}
    831 	if (maxread == 0)
    832 		return 0;
    833 
    834 	ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
    835 	    &rn->file_info);
    836 
    837 	if (ret > 0) {
    838 		*resid -= ret;
    839 		ret = 0;
    840 	}
    841 
    842 	return -ret;
    843 }
    844 
    845 /* write to the file */
    846 /* ARGSUSED0 */
    847 static int
    848 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    849 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    850 	int ioflag)
    851 {
    852 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    853 	struct puffs_node	*pn = opc;
    854 	struct refusenode	*rn = pn->pn_data;
    855 	struct fuse		*fuse;
    856 	const char		*path = PNPATH(pn);
    857 	int			ret;
    858 
    859 	fuse = (struct fuse *)pu->pu_privdata;
    860 	if (fuse->op.write == NULL) {
    861 		return ENOSYS;
    862 	}
    863 
    864 	if (ioflag & PUFFS_IO_APPEND)
    865 		offset = pn->pn_va.va_size;
    866 
    867 	ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
    868 	    &rn->file_info);
    869 
    870 	if (ret > 0) {
    871 		if (offset + ret > pn->pn_va.va_size)
    872 			pn->pn_va.va_size = offset + ret;
    873 		*resid -= ret;
    874 		ret = 0;
    875 	}
    876 
    877 	return -ret;
    878 }
    879 
    880 
    881 /* ARGSUSED3 */
    882 static int
    883 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
    884 	struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
    885 	size_t *reslen)
    886 {
    887 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    888 	struct puffs_fuse_dirh	*dirh;
    889 	struct puffs_node	*pn = opc;
    890 	struct refusenode	*rn = pn->pn_data;
    891 	struct dirent		*fromdent;
    892 	struct fuse		*fuse;
    893 	const char		*path = PNPATH(pn);
    894 	int			ret;
    895 
    896 	fuse = (struct fuse *)pu->pu_privdata;
    897 	if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
    898 		return ENOSYS;
    899 	}
    900 
    901 	if (pn->pn_va.va_type != VDIR)
    902 		return ENOTDIR;
    903 
    904 	dirh = &rn->dirh;
    905 
    906 	/*
    907 	 * if we are starting from the beginning, slurp entire directory
    908 	 * into our buffers
    909 	 */
    910 	if (*readoff == 0) {
    911 		/* free old buffers */
    912 		free(dirh->dbuf);
    913 		memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
    914 
    915 		if (fuse->op.readdir)
    916 			ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
    917 			    0, &rn->file_info);
    918 		else
    919 			ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
    920 		if (ret)
    921 			return -ret;
    922 	}
    923 
    924 	/* now, stuff results into the kernel buffers */
    925 	while (*readoff < dirh->bufsize - dirh->reslen) {
    926 		/*LINTED*/
    927 		fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
    928 
    929 		if (*reslen < _DIRENT_SIZE(fromdent))
    930 			break;
    931 
    932 		memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
    933 		*readoff += _DIRENT_SIZE(fromdent);
    934 		*reslen -= _DIRENT_SIZE(fromdent);
    935 
    936 		dent = _DIRENT_NEXT(dent);
    937 	}
    938 
    939 	return 0;
    940 }
    941 
    942 /* ARGSUSED */
    943 static int
    944 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
    945 {
    946 	struct puffs_node	*pn = opc;
    947 
    948 	nukern(pn);
    949 
    950 	return 0;
    951 }
    952 
    953 /* ARGSUSED1 */
    954 static int
    955 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
    956 {
    957         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    958 	struct fuse		*fuse;
    959 
    960 	fuse = (struct fuse *)pu->pu_privdata;
    961 	if (fuse->op.destroy == NULL) {
    962 		return 0;
    963 	}
    964 	(*fuse->op.destroy)(fuse);
    965         return 0;
    966 }
    967 
    968 /* ARGSUSED0 */
    969 static int
    970 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
    971             const struct puffs_cred *cr, pid_t pid)
    972 {
    973         return 0;
    974 }
    975 
    976 /* ARGSUSED2 */
    977 static int
    978 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
    979 {
    980         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    981 	struct fuse		*fuse;
    982 	int			ret;
    983 
    984 	fuse = (struct fuse *)pu->pu_privdata;
    985 	if (fuse->op.statfs == NULL) {
    986 #ifdef REFUSE_INHERIT_FS_CHARACTERISTICS
    987 		if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
    988 			return errno;
    989 		}
    990 #else
    991 		(void) memset(svfsb, 0x0, sizeof(*svfsb));
    992 		ret = 0;
    993 #endif
    994 	} else {
    995 		ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
    996 	}
    997 
    998         return ret;
    999 }
   1000 
   1001 
   1002 /* End of puffs_fuse operations */
   1003 
   1004 
   1005 /* ARGSUSED3 */
   1006 int
   1007 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
   1008 	size_t size, void *userdata)
   1009 {
   1010 	struct fuse_chan	*fc;
   1011 	struct fuse_args	 args;
   1012 	struct fuse		*fuse;
   1013 	int			 ret;
   1014 
   1015 	(void) memset(&args, 0x0, sizeof(args));
   1016 	args.argc = argc;
   1017 	args.argv = argv;
   1018 
   1019 	if (__fuse_debug(0)) {
   1020 		__fuse_pargs("fuse_main_real", argc, argv);
   1021 	}
   1022 	fc = fuse_mount(argv[argc - 1], &args);
   1023 	/* XXX: stuff name into fuse_args */
   1024 	fuse = fuse_new(fc, fc->args, ops, size, userdata);
   1025 
   1026 	ret = fuse_loop(fuse);
   1027 
   1028 	return ret;
   1029 }
   1030 
   1031 /*
   1032  * XXX: just defer the operation until fuse_new() when we have more
   1033  * info on our hands.  The real beef is why's this separate in fuse in
   1034  * the first place?
   1035  */
   1036 /* ARGSUSED1 */
   1037 struct fuse_chan *
   1038 fuse_mount(const char *dir, struct fuse_args *args)
   1039 {
   1040 	struct fuse_chan	*fc;
   1041 	int			i;
   1042 
   1043 	NEW(struct fuse_chan, fc, "fuse_mount", return NULL);
   1044 
   1045 	fc->dir = strdup(dir);
   1046 
   1047 	if (args && args->argc > 0) {
   1048 		NEW(struct fuse_args, fc->args, "fuse_mount2", return NULL);
   1049 
   1050 		/* yes, we do need to deep copy */
   1051 		fc->args->allocated = ((args->argc / 32) + 1) * 32;
   1052 		NEWARRAY(char *, fc->args->argv, fc->args->allocated, "fuse_mount3", return NULL);
   1053 
   1054 		for (i = 0 ; i < args->argc ; i++) {
   1055 			fc->args->argv[i] = strdup(args->argv[i]);
   1056 		}
   1057 	}
   1058 
   1059 	return fc;
   1060 }
   1061 
   1062 /* ARGSUSED1 */
   1063 struct fuse *
   1064 fuse_new(struct fuse_chan *fc, struct fuse_args *args,
   1065 	const struct fuse_operations *ops, size_t size, void *userdata)
   1066 {
   1067 	struct puffs_usermount	*pu;
   1068 	struct puffs_pathobj	*po_root;
   1069 	struct refusenode	*rn_root;
   1070 	struct puffs_ops	*pops;
   1071 	struct statvfs		svfsb;
   1072 	struct stat		st;
   1073 	struct fuse		*fuse;
   1074 	char			 name[64];
   1075 	char			*slash;
   1076 
   1077 	NEW(struct fuse, fuse, "fuse_new", exit(EXIT_FAILURE));
   1078 
   1079 	/* copy fuse ops to their own stucture */
   1080 	(void) memcpy(&fuse->op, ops, sizeof(fuse->op));
   1081 
   1082 	fcon.fuse = fuse;
   1083 	fcon.private_data = userdata;
   1084 
   1085 	fuse->fc = fc;
   1086 
   1087 	/* initialise the puffs operations structure */
   1088         PUFFSOP_INIT(pops);
   1089 
   1090         PUFFSOP_SET(pops, puffs_fuse, fs, sync);
   1091         PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
   1092         PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
   1093 
   1094 	/*
   1095 	 * XXX: all of these don't possibly need to be
   1096 	 * unconditionally set
   1097 	 */
   1098         PUFFSOP_SET(pops, puffs_fuse, node, lookup);
   1099         PUFFSOP_SET(pops, puffs_fuse, node, getattr);
   1100         PUFFSOP_SET(pops, puffs_fuse, node, setattr);
   1101         PUFFSOP_SET(pops, puffs_fuse, node, readdir);
   1102         PUFFSOP_SET(pops, puffs_fuse, node, readlink);
   1103         PUFFSOP_SET(pops, puffs_fuse, node, mknod);
   1104         PUFFSOP_SET(pops, puffs_fuse, node, create);
   1105         PUFFSOP_SET(pops, puffs_fuse, node, remove);
   1106         PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
   1107         PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
   1108         PUFFSOP_SET(pops, puffs_fuse, node, symlink);
   1109         PUFFSOP_SET(pops, puffs_fuse, node, rename);
   1110         PUFFSOP_SET(pops, puffs_fuse, node, link);
   1111         PUFFSOP_SET(pops, puffs_fuse, node, open);
   1112         PUFFSOP_SET(pops, puffs_fuse, node, close);
   1113         PUFFSOP_SET(pops, puffs_fuse, node, read);
   1114         PUFFSOP_SET(pops, puffs_fuse, node, write);
   1115         PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
   1116 
   1117 	/* work out what we'll call ourselves in df output */
   1118 	if (args == NULL) {
   1119 		args = fc->args;
   1120 	}
   1121 	if (args == NULL || args->argv == NULL || args->argv[0] == NULL) {
   1122 		(void) strlcpy(name, "refuse", sizeof(name));
   1123 	} else {
   1124 		if ((slash = strrchr(*args->argv, '/')) == NULL) {
   1125 			slash = *args->argv;
   1126 		} else {
   1127 			slash += 1;
   1128 		}
   1129 		(void) snprintf(name, sizeof(name), "refuse:%s", slash);
   1130 	}
   1131 
   1132 	pu = puffs_mount(pops, fc->dir, MNT_NODEV | MNT_NOSUID,
   1133 			 name, NULL,
   1134 			 PUFFS_FLAG_BUILDPATH
   1135 			   | PUFFS_FLAG_OPDUMP
   1136 			   | PUFFS_KFLAG_NOCACHE,
   1137 			 0);
   1138 	if (pu == NULL) {
   1139 		err(EXIT_FAILURE, "puffs_mount");
   1140 	}
   1141 	fc->pu = pu;
   1142 	/* whilst this (assigning the pu_privdata in the puffs
   1143 	 * usermount struct to be the fuse struct) might seem like
   1144 	 * we are chasing our tail here, the logic is as follows:
   1145 		+ the operation wrapper gets called with the puffs
   1146 		  calling conventions
   1147 		+ we need to fix up args first
   1148 		+ then call the fuse user-supplied operation
   1149 		+ then we fix up any values on return that we need to
   1150 		+ and fix up any nodes, etc
   1151 	 * so we need to be able to get at the fuse ops from within the
   1152 	 * puffs_usermount struct
   1153 	 */
   1154 	pu->pu_privdata = fuse;
   1155 
   1156 	pu->pu_pn_root = newrn(pu);
   1157 	rn_root = pu->pu_pn_root->pn_data;
   1158 	rn_root->flags |= RN_ROOT;
   1159 
   1160 	po_root = puffs_getrootpathobj(pu);
   1161 	po_root->po_path = strdup("/");
   1162 	po_root->po_len = 1;
   1163 
   1164 	/* sane defaults */
   1165 	puffs_vattr_null(&pu->pu_pn_root->pn_va);
   1166 	pu->pu_pn_root->pn_va.va_type = VDIR;
   1167 	pu->pu_pn_root->pn_va.va_mode = 0755;
   1168 	if (fuse->op.getattr)
   1169 		if (fuse->op.getattr(po_root->po_path, &st) == 0)
   1170 			puffs_stat2vattr(&pu->pu_pn_root->pn_va, &st);
   1171 	assert(pu->pu_pn_root->pn_va.va_type == VDIR);
   1172 
   1173 	if (fuse->op.init)
   1174 		fcon.private_data = fuse->op.init(NULL); /* XXX */
   1175 
   1176 	puffs_zerostatvfs(&svfsb);
   1177 	if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
   1178 		err(EXIT_FAILURE, "puffs_start");
   1179 	}
   1180 
   1181 	return fuse;
   1182 }
   1183 
   1184 int
   1185 fuse_loop(struct fuse *fuse)
   1186 {
   1187 
   1188 	return puffs_mainloop(fuse->fc->pu, PUFFSLOOP_NODAEMON);
   1189 }
   1190 
   1191 void
   1192 fuse_destroy(struct fuse *fuse)
   1193 {
   1194 
   1195 
   1196 	/* XXXXXX: missing stuff */
   1197 	FREE(fuse);
   1198 }
   1199 
   1200 /* XXX: threads */
   1201 struct fuse_context *
   1202 fuse_get_context()
   1203 {
   1204 
   1205 	return &fcon;
   1206 }
   1207 
   1208 void
   1209 fuse_exit(struct fuse *fuse)
   1210 {
   1211 
   1212 	puffs_exit(fuse->fc->pu, 1);
   1213 }
   1214 
   1215 /*
   1216  * XXX: obviously not the most perfect of functions, but needs some
   1217  * puffs tweaking for a better tomorrow
   1218  */
   1219 /*ARGSUSED*/
   1220 void
   1221 fuse_unmount(const char *mp, struct fuse_chan *fc)
   1222 {
   1223 
   1224 	puffs_exit(fc->pu, 1);
   1225 }
   1226 
   1227 /*ARGSUSED*/
   1228 void
   1229 fuse_unmount_compat22(const char *mp)
   1230 {
   1231 
   1232 	return;
   1233 }
   1234