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