Home | History | Annotate | Line # | Download | only in librefuse
refuse.c revision 1.12
      1 /*	$NetBSD: refuse.c,v 1.12 2007/02/15 17:06:24 pooka 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.12 2007/02/15 17:06:24 pooka Exp $");
     34 #endif /* !lint */
     35 
     36 #include <err.h>
     37 #include <errno.h>
     38 #include <fuse.h>
     39 #include <ucontext.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 /* this is the private fuse structure */
     70 struct fuse {
     71 	struct fuse_session	*se;		/* fuse session pointer */
     72 	struct fuse_operations	op;		/* switch table of operations */
     73 	int			compat;		/* compat level -
     74 						 * not used in puffs_fuse */
     75 	struct node		**name_table;
     76 	size_t			name_table_size;
     77 	struct node		**id_table;
     78 	size_t			id_table_size;
     79 	fuse_ino_t		ctr;
     80 	unsigned int		generation;
     81 	unsigned int		hidectr;
     82 	pthread_mutex_t		lock;
     83 	pthread_rwlock_t	tree_lock;
     84 	void			*user_data;
     85 	struct fuse_config	conf;
     86 	int			intr_installed;
     87 	struct puffs_usermount	*pu;
     88 };
     89 
     90 struct refusenode {
     91 	struct fuse_file_info	 file_info;
     92 };
     93 
     94 static struct puffs_node *
     95 newrn(struct puffs_usermount *pu)
     96 {
     97 	struct puffs_node *pn;
     98 	struct refusenode *rn;
     99 
    100 	rn = malloc(sizeof(struct refusenode));
    101 	if (!rn)
    102 		abort(); /*XXX*/
    103 
    104 	memset(rn, 0, sizeof(struct refusenode));
    105 	pn = puffs_pn_new(pu, rn);
    106 
    107 	return pn;
    108 }
    109 
    110 static void
    111 nukern(struct puffs_node *pn)
    112 {
    113 
    114 	free(pn->pn_data);
    115 	puffs_pn_put(pn);
    116 }
    117 
    118 static ino_t fakeino = 3;
    119 
    120 /* XXX: rethinkme */
    121 struct fuse_dirh {
    122 	struct dirent *dent;
    123 	size_t reslen;
    124 	off_t readoff;
    125 };
    126 
    127 /* ARGSUSED2 */
    128 static int
    129 puffs_fuse_fill_dir(void *buf, const char *name,
    130 	const struct stat *stbuf, off_t off)
    131 {
    132 	struct fuse_dirh *deh = buf;
    133 	uint8_t dtype;
    134 
    135 	/* XXX: this is hacked *purely* for hellofs, so fiXXXme */
    136 	if (*name == '.')
    137 		dtype = DT_DIR;
    138 	else
    139 		dtype = DT_REG;
    140 
    141 	return !puffs_nextdent(&deh->dent, name, fakeino++, dtype,&deh->reslen);
    142 }
    143 
    144 static int
    145 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
    146 {
    147 	ino_t dino;
    148 	int dtype;
    149 
    150 	/* XXX: this is hacked *purely* for cddafs, so fiXXXme */
    151 	if (type == 0) {
    152 		if (*name == '.')
    153 			dtype = DT_DIR;
    154 		else
    155 			dtype = DT_REG;
    156 	} else
    157 		dtype = type;
    158 
    159 	if (ino)
    160 		dino = ino;
    161 	else
    162 		dino = fakeino++;
    163 
    164 	return !puffs_nextdent(&h->dent, name, dino, dtype, &h->reslen);
    165 }
    166 
    167 int
    168 fuse_opt_add_arg(struct fuse_args *args, const char *arg)
    169 {
    170 	char	**oldargv;
    171 	int	oldargc;
    172 
    173 	if (args->allocated) {
    174 		RENEW(char *, args->argv, args->argc + 1,
    175 		    "fuse_opt_add_arg1", return 0);
    176 	} else {
    177 		oldargv = args->argv;
    178 		oldargc = args->argc;
    179 		NEWARRAY(char *, args->argv, oldargc + 1,
    180 		    "fuse_opt_add_arg2", return 0);
    181 		(void) memcpy(args->argv, oldargv, oldargc * sizeof(char *));
    182 		args->allocated = 1;
    183 	}
    184 	args->argv[args->argc++] = strdup(arg);
    185 	return 1;
    186 }
    187 
    188 /* operation wrappers start here */
    189 
    190 /* lookup the path */
    191 /* ARGSUSED1 */
    192 static int
    193 puffs_fuse_node_lookup(struct puffs_cc *pcc, void *opc, void **newnode,
    194 	enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
    195 	const struct puffs_cn *pcn)
    196 {
    197 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    198 	struct puffs_node	*pn_res;
    199 	struct stat		st;
    200 	struct fuse		*fuse;
    201 	const char		*path = PCNPATH(pcn);
    202 	int			ret;
    203 
    204 	fuse = (struct fuse *)pu->pu_privdata;
    205 	ret = fuse->op.getattr(path, &st);
    206 
    207 	if (ret != 0) {
    208 		return -ret; /* XXX: linux foo */
    209 	}
    210 
    211 	/* XXX: fiXXXme unconst */
    212 	pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
    213 	    __UNCONST(&pcn->pcn_po_full));
    214 	if (pn_res == NULL) {
    215 		pn_res = newrn(pu);
    216 		if (pn_res == NULL)
    217 			return errno;
    218 		puffs_stat2vattr(&pn_res->pn_va, &st);
    219 	}
    220 
    221 	*newnode = pn_res;
    222 	*newtype = pn_res->pn_va.va_type;
    223 	*newsize = pn_res->pn_va.va_size;
    224 	*newrdev = pn_res->pn_va.va_rdev;
    225 
    226 	return 0;
    227 }
    228 
    229 /* get attributes for the path name */
    230 /* ARGSUSED3 */
    231 static int
    232 puffs_fuse_node_getattr(struct puffs_cc *pcc, void *opc, struct vattr *va,
    233 	const struct puffs_cred *pcr, pid_t pid)
    234 {
    235 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    236 	struct puffs_node	*pn = opc;
    237 	struct stat		 st;
    238 	struct fuse		*fuse;
    239 	const char		*path = PNPATH(pn);
    240 	int			ret;
    241 
    242 	fuse = (struct fuse *)pu->pu_privdata;
    243 	if (fuse->op.getattr == NULL) {
    244 		return ENOSYS;
    245 	}
    246 
    247 	/* wrap up return code */
    248 	ret = (*fuse->op.getattr)(path, &st);
    249 
    250 	if (ret == 0) {
    251 		/* fill in va from st */
    252 		va->va_mode = st.st_mode;
    253 		va->va_nlink = st.st_nlink;
    254 		va->va_uid = st.st_uid;
    255 		va->va_gid = st.st_gid;
    256 		va->va_fsid = st.st_rdev;
    257 		va->va_fileid = st.st_ino;
    258 		va->va_size = st.st_size;
    259 		va->va_blocksize = st.st_blksize;
    260 		va->va_atime = st.st_atimespec;
    261 		va->va_mtime = st.st_mtimespec;
    262 		va->va_ctime = st.st_ctimespec;
    263 		va->va_birthtime = st.st_birthtimespec;
    264 		va->va_gen = st.st_gen;
    265 		va->va_flags = st.st_flags;
    266 		va->va_rdev = st.st_rdev;
    267 		va->va_bytes = st.st_size;
    268 		va->va_filerev = st.st_gen;
    269 		va->va_vaflags = st.st_flags;
    270 
    271 	}
    272 
    273 	return ret;
    274 }
    275 
    276 /* read the contents of the symbolic link */
    277 /* ARGSUSED2 */
    278 static int
    279 puffs_fuse_node_readlink(struct puffs_cc *pcc, void *opc,
    280 	const struct puffs_cred *cred, char *linkname, size_t *linklen)
    281 {
    282 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    283 	struct puffs_node	*pn = opc;
    284 	struct fuse		*fuse;
    285 	const char		*path = PNPATH(pn);
    286 	int			ret;
    287 
    288 	fuse = (struct fuse *)pu->pu_privdata;
    289 	if (fuse->op.readlink == NULL) {
    290 		return ENOSYS;
    291 	}
    292 
    293 	/* wrap up return code */
    294 	ret = (*fuse->op.readlink)(path, linkname, *linklen);
    295 
    296 	if (ret == 0) {
    297 	}
    298 
    299 	return ret;
    300 }
    301 
    302 /* make the special node */
    303 /* ARGSUSED1 */
    304 static int
    305 puffs_fuse_node_mknod(struct puffs_cc *pcc, void *opc, void **newnode,
    306 	const struct puffs_cn *pcn, const struct vattr *va)
    307 {
    308 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    309 	struct puffs_node	*pn;
    310 	struct fuse		*fuse;
    311 	mode_t			 mode = va->va_mode;
    312 	const char		*path = PCNPATH(pcn);
    313 	int			ret;
    314 
    315 	fuse = (struct fuse *)pu->pu_privdata;
    316 	if (fuse->op.mknod == NULL) {
    317 		return ENOSYS;
    318 	}
    319 
    320 	/* wrap up return code */
    321 	ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
    322 
    323 	if (ret == 0) {
    324 		/* fix up nodes */
    325 		pn = newrn(pu);
    326 		if (pn == NULL) {
    327 			unlink(PCNPATH(pcn));
    328 			return ENOMEM;
    329 		}
    330 		puffs_setvattr(&pn->pn_va, va);
    331 
    332 		*newnode = pn;
    333 	}
    334 
    335 	return ret;
    336 }
    337 
    338 /* make a directory */
    339 /* ARGSUSED1 */
    340 static int
    341 puffs_fuse_node_mkdir(struct puffs_cc *pcc, void *opc, void **newnode,
    342 	const struct puffs_cn *pcn, const struct vattr *va)
    343 {
    344 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    345 	struct puffs_node	*pn;
    346 	struct fuse		*fuse;
    347 	mode_t			 mode = va->va_mode;
    348 	const char		*path = PCNPATH(pcn);
    349 	int			ret;
    350 
    351 	fuse = (struct fuse *)pu->pu_privdata;
    352 	if (fuse->op.mkdir == NULL) {
    353 		return ENOSYS;
    354 	}
    355 
    356 	/* wrap up return code */
    357 	ret = (*fuse->op.mkdir)(path, mode);
    358 
    359 	if (ret == 0) {
    360 		/* fix up nodes */
    361 		pn = newrn(pu);
    362 		if (pn == NULL) {
    363 			rmdir(PCNPATH(pcn));
    364 			return ENOMEM;
    365 		}
    366 		puffs_setvattr(&pn->pn_va, va);
    367 
    368 		*newnode = pn;
    369 	}
    370 
    371 	return ret;
    372 }
    373 
    374 /* remove the directory entry */
    375 /* ARGSUSED1 */
    376 static int
    377 puffs_fuse_node_remove(struct puffs_cc *pcc, void *opc, void *targ,
    378 	const struct puffs_cn *pcn)
    379 {
    380 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    381 	struct fuse		*fuse;
    382 	const char		*path = PCNPATH(pcn);
    383 	int			ret;
    384 
    385 	fuse = (struct fuse *)pu->pu_privdata;
    386 	if (fuse->op.unlink == NULL) {
    387 		return ENOSYS;
    388 	}
    389 
    390 	/* wrap up return code */
    391 	ret = (*fuse->op.unlink)(path);
    392 
    393 	return ret;
    394 }
    395 
    396 /* remove the directory */
    397 /* ARGSUSED1 */
    398 static int
    399 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
    400 	const struct puffs_cn *pcn)
    401 {
    402 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    403 	struct fuse		*fuse;
    404 	const char		*path = PCNPATH(pcn);
    405 	int			ret;
    406 
    407 	fuse = (struct fuse *)pu->pu_privdata;
    408 	if (fuse->op.rmdir == NULL) {
    409 		return ENOSYS;
    410 	}
    411 
    412 	/* wrap up return code */
    413 	ret = (*fuse->op.rmdir)(path);
    414 
    415 	return ret;
    416 }
    417 
    418 /* create a symbolic link */
    419 /* ARGSUSED1 */
    420 static int
    421 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
    422 	const struct puffs_cn *pcn_src, const struct vattr *va,
    423 	const char *link_target)
    424 {
    425 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    426 	struct puffs_node	*pn;
    427 	struct fuse		*fuse;
    428 	const char		*path = PCNPATH(pcn_src);
    429 	int			ret;
    430 
    431 	fuse = (struct fuse *)pu->pu_privdata;
    432 	if (fuse->op.symlink == NULL) {
    433 		return ENOSYS;
    434 	}
    435 
    436 	/* wrap up return code */
    437 	ret = (*fuse->op.symlink)(path, link_target);
    438 	/* XXX - check I haven't transposed these args */
    439 
    440 	if (ret == 0) {
    441 		/* fix up nodes */
    442 		pn = newrn(pu);
    443 		if (pn == NULL) {
    444 			unlink(link_target);
    445 			return ENOMEM;
    446 		}
    447 		puffs_setvattr(&pn->pn_va, va);
    448 
    449 		*newnode = pn;
    450 	}
    451 
    452 	return ret;
    453 }
    454 
    455 /* rename a directory entry */
    456 /* ARGSUSED1 */
    457 static int
    458 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
    459 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    460 	const struct puffs_cn *pcn_targ)
    461 {
    462 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    463 	struct puffs_node	*pn = opc;
    464 	struct vattr		va;
    465 	struct fuse		*fuse;
    466 	const char		*path = PCNPATH(pcn_src);
    467 	int			ret;
    468 
    469 	fuse = (struct fuse *)pu->pu_privdata;
    470 	if (fuse->op.rename == NULL) {
    471 		return ENOSYS;
    472 	}
    473 
    474 	/* wrap up return code */
    475 	ret = (*fuse->op.rename)(path, PCNPATH(pcn_targ));
    476 
    477 	/* XXX: what's this guy doing??? */
    478 	if (ret == 0) {
    479 		(void) memcpy(&va, &pn->pn_va, sizeof(va));
    480 
    481 		puffs_pn_put(pn);
    482 
    483 		pn = puffs_pn_new(pu, NULL);
    484 		if (pn == NULL) {
    485 			return ENOMEM;
    486 		}
    487 		puffs_setvattr(&pn->pn_va, &va);
    488 
    489 	}
    490 
    491 	return ret;
    492 }
    493 
    494 /* create a link in the file system */
    495 /* ARGSUSED1 */
    496 static int
    497 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
    498 	const struct puffs_cn *pcn)
    499 {
    500 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    501 	struct puffs_node	*pn = targ;
    502 	struct fuse		*fuse;
    503 	int			ret;
    504 
    505 	fuse = (struct fuse *)pu->pu_privdata;
    506 	if (fuse->op.link == NULL) {
    507 		return ENOSYS;
    508 	}
    509 
    510 	/* wrap up return code */
    511 	ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
    512 
    513 	return ret;
    514 }
    515 
    516 /*
    517  * We run into a slight problemette here - puffs provides
    518  * setattr/getattr, whilst fuse provides all the usual chown/chmod/chgrp
    519  * functionality.  So that we don't miss out on anything when calling a
    520  * fuse operation, we have to get the vattr from the existing file,
    521  * find out what's changed, and then switch on that to call the fuse
    522  * function accordingly.
    523  */
    524 /* ARGSUSED3 */
    525 static int
    526 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
    527 	const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
    528 {
    529 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    530 	struct puffs_node	*pn = opc;
    531 	struct fuse		*fuse;
    532 	const char		*path = PNPATH(pn);
    533 	mode_t			mode;
    534 	uid_t			uid;
    535 	gid_t			gid;
    536 	int			ret;
    537 
    538 	fuse = (struct fuse *)pu->pu_privdata;
    539 
    540 	ret = -1;
    541 
    542 	mode = va->va_mode;
    543 	uid = va->va_uid;
    544 	gid = va->va_gid;
    545 
    546 	if (mode != (mode_t)PUFFS_VNOVAL) {
    547 		if (fuse->op.chmod == NULL) {
    548 			return ENOSYS;
    549 		}
    550 		ret = (*fuse->op.chmod)(path, mode);
    551 	}
    552 	if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
    553 		if (fuse->op.chown == NULL) {
    554 			return ENOSYS;
    555 		}
    556 		ret = (*fuse->op.chown)(path, uid, gid);
    557 	}
    558 
    559 	if (ret == 0) {
    560 	}
    561 
    562 	return ret;
    563 }
    564 
    565 /* ARGSUSED2 */
    566 static int
    567 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int flags,
    568 	const struct puffs_cred *cred, pid_t pid)
    569 {
    570 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    571 	struct puffs_node	*pn = opc;
    572 	struct refusenode	*rn = pn->pn_data;
    573 	struct fuse		*fuse;
    574 	struct stat		 st;
    575 	const char		*path = PNPATH(pn);
    576 	int			 ret;
    577 
    578 	fuse = (struct fuse *)pu->pu_privdata;
    579 	if (fuse->op.open == NULL) {
    580 		return ENOSYS;
    581 	}
    582 
    583 	/* examine type - if directory, return 0 rather than open */
    584 	ret = (fuse->op.getattr == NULL) ?
    585 		stat(path, &st) :
    586 		(*fuse->op.getattr)(path, &st);
    587 	if (ret == 0 && (st.st_mode & S_IFMT) == S_IFDIR) {
    588 		return 0;
    589 	}
    590 
    591 	if (strcmp(path, "/") == 0) {
    592 		return 0;
    593 	}
    594 
    595 	ret = (*fuse->op.open)(path, &rn->file_info);
    596 
    597 	if (ret == 0) {
    598 	}
    599 
    600 	return ret;
    601 }
    602 
    603 /* read some more from the file */
    604 /* ARGSUSED5 */
    605 static int
    606 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    607 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    608 	int ioflag)
    609 {
    610 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    611 	struct puffs_node	*pn = opc;
    612 	struct refusenode	*rn = pn->pn_data;
    613 	struct fuse		*fuse;
    614 	const char		*path = PNPATH(pn);
    615 	int			ret;
    616 
    617 	fuse = (struct fuse *)pu->pu_privdata;
    618 	if (fuse->op.read == NULL) {
    619 		return ENOSYS;
    620 	}
    621 
    622 	ret = (*fuse->op.read)(path, (char *)buf, *resid, offset,
    623 	    &rn->file_info);
    624 
    625 	if (ret > 0) {
    626 		*resid -= ret;
    627 	}
    628 
    629 	return 0;
    630 }
    631 
    632 /* write to the file */
    633 /* ARGSUSED0 */
    634 static int
    635 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    636 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    637 	int ioflag)
    638 {
    639 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    640 	struct puffs_node	*pn = opc;
    641 	struct refusenode	*rn = pn->pn_data;
    642 	struct fuse		*fuse;
    643 	const char		*path = PNPATH(pn);
    644 	int			ret;
    645 
    646 	fuse = (struct fuse *)pu->pu_privdata;
    647 	if (fuse->op.write == NULL) {
    648 		return ENOSYS;
    649 	}
    650 
    651 	ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
    652 	    &rn->file_info);
    653 
    654 	if (ret > 0) {
    655 		*resid -= ret;
    656 	}
    657 
    658 	return ret;
    659 }
    660 
    661 
    662 /* ARGSUSED3 */
    663 static int
    664 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
    665 	struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
    666 	size_t *reslen)
    667 {
    668 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    669 	struct puffs_node	*pn = opc;
    670 	struct refusenode	*rn = pn->pn_data;
    671 	struct fuse		*fuse;
    672 	const char		*path = PNPATH(pn);
    673 	struct fuse_dirh	deh;
    674 	int			ret;
    675 
    676 	fuse = (struct fuse *)pu->pu_privdata;
    677 	if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
    678 		return ENOSYS;
    679 	}
    680 
    681 	/* XXX: how to handle this??? */
    682 	if (*readoff != 0) {
    683 		return 0;
    684 	}
    685 
    686 	deh.dent = dent;
    687 	deh.reslen = *reslen;
    688 	deh.readoff = *readoff;
    689 
    690 	if (fuse->op.readdir)
    691 		ret = fuse->op.readdir(path, &deh, puffs_fuse_fill_dir,
    692 		    *readoff, &rn->file_info);
    693 	else
    694 		ret = fuse->op.getdir(path, &deh, puffs_fuse_dirfil);
    695 	*reslen = deh.reslen;
    696 	*readoff = 1;
    697 
    698 	if (ret == 0) {
    699 	}
    700 
    701 	return ret;
    702 }
    703 
    704 /* ARGSUSED */
    705 static int
    706 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
    707 {
    708 	struct puffs_node	*pn = opc;
    709 
    710 	nukern(pn);
    711 
    712 	return 0;
    713 }
    714 
    715 /* ARGSUSED1 */
    716 static int
    717 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
    718 {
    719         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    720 	struct fuse		*fuse;
    721 
    722 	fuse = (struct fuse *)pu->pu_privdata;
    723 	if (fuse->op.destroy == NULL) {
    724 		return 0;
    725 	}
    726 	(*fuse->op.destroy)(fuse);
    727         return 0;
    728 }
    729 
    730 /* ARGSUSED0 */
    731 static int
    732 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
    733             const struct puffs_cred *cr, pid_t pid)
    734 {
    735         return 0;
    736 }
    737 
    738 /* ARGSUSED2 */
    739 static int
    740 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
    741 {
    742         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    743 	struct fuse		*fuse;
    744 	int			ret;
    745 
    746 	fuse = (struct fuse *)pu->pu_privdata;
    747 	if (fuse->op.statfs == NULL) {
    748 		if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
    749 			return errno;
    750 		}
    751 	} else {
    752 		ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
    753 	}
    754 
    755         return ret;
    756 }
    757 
    758 
    759 
    760 
    761 /* End of puffs_fuse operations */
    762 
    763 /*
    764  * XXX: do this otherwise if/when we grow thread support
    765  *
    766  * XXX2: does not supply uid, gid or pid currently
    767  */
    768 static struct fuse_context fcon;
    769 
    770 /* ARGSUSED3 */
    771 int
    772 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
    773 	size_t size, void *userdata)
    774 {
    775 	struct puffs_usermount	*pu;
    776 	struct puffs_pathobj	*po_root;
    777 	struct puffs_ops	*pops;
    778 	struct statvfs		svfsb;
    779 	struct fuse		*fuse;
    780 	char			 name[64];
    781 	char			*slash;
    782 	int			 ret;
    783 
    784 	/* initialise the puffs operations structure */
    785         PUFFSOP_INIT(pops);
    786 
    787         PUFFSOP_SET(pops, puffs_fuse, fs, sync);
    788         PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
    789         PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
    790 
    791 	/*
    792 	 * XXX: all of these don't possibly need to be
    793 	 * unconditionally set
    794 	 */
    795         PUFFSOP_SET(pops, puffs_fuse, node, lookup);
    796         PUFFSOP_SET(pops, puffs_fuse, node, getattr);
    797         PUFFSOP_SET(pops, puffs_fuse, node, readdir);
    798         PUFFSOP_SET(pops, puffs_fuse, node, readlink);
    799         PUFFSOP_SET(pops, puffs_fuse, node, mknod);
    800         PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
    801         PUFFSOP_SET(pops, puffs_fuse, node, remove);
    802         PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
    803         PUFFSOP_SET(pops, puffs_fuse, node, symlink);
    804         PUFFSOP_SET(pops, puffs_fuse, node, rename);
    805         PUFFSOP_SET(pops, puffs_fuse, node, link);
    806         PUFFSOP_SET(pops, puffs_fuse, node, setattr);
    807         PUFFSOP_SET(pops, puffs_fuse, node, open);
    808         PUFFSOP_SET(pops, puffs_fuse, node, read);
    809         PUFFSOP_SET(pops, puffs_fuse, node, write);
    810         PUFFSOP_SET(pops, puffs_fuse, node, readdir);
    811         PUFFSOP_SET(pops, puffs_fuse, node, read);
    812         PUFFSOP_SET(pops, puffs_fuse, node, write);
    813         PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
    814 
    815 	NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
    816 
    817 	/* copy fuse ops to their own stucture */
    818 	(void) memcpy(&fuse->op, ops, sizeof(fuse->op));
    819 
    820 	fcon.fuse = fuse;
    821 	fcon.private_data = userdata;
    822 
    823 	/* whilst this (assigning the pu_privdata in the puffs
    824 	 * usermount struct to be the fuse struct) might seem like
    825 	 * we are chasing our tail here, the logic is as follows:
    826 		+ the operation wrapper gets called with the puffs
    827 		  calling conventions
    828 		+ we need to fix up args first
    829 		+ then call the fuse user-supplied operation
    830 		+ then we fix up any values on return that we need to
    831 		+ and fix up any nodes, etc
    832 	 * so we need to be able to get at the fuse ops from within the
    833 	 * puffs_usermount struct
    834 	 */
    835 	if ((slash = strrchr(*argv, '/')) == NULL) {
    836 		slash = *argv;
    837 	} else {
    838 		slash += 1;
    839 	}
    840 	(void) snprintf(name, sizeof(name), "refuse:%s", slash);
    841 	pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
    842 			name, fuse,
    843 			PUFFS_FLAG_BUILDPATH | PUFFS_FLAG_OPDUMP, 0);
    844 	if (pu == NULL) {
    845 		err(EXIT_FAILURE, "puffs_mount");
    846 	}
    847 
    848 	fuse->pu = pu;
    849 	pu->pu_pn_root = puffs_pn_new(pu, NULL);
    850 	po_root = puffs_getrootpathobj(pu);
    851 	po_root->po_path = strdup("/");
    852 	po_root->po_len = 1;
    853 
    854 	if (fuse->op.init)
    855 		fcon.private_data = fuse->op.init(NULL); /* XXX */
    856 
    857 	statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
    858 	if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
    859 		err(EXIT_FAILURE, "puffs_start");
    860 	}
    861 
    862 	ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
    863 
    864 	(void) free(po_root->po_path);
    865 	FREE(fuse);
    866 	return ret;
    867 }
    868 
    869 /* ARGSUSED0 */
    870 int
    871 fuse_opt_parse(struct fuse_args *args, void *data,
    872 	const struct fuse_opt *opts, fuse_opt_proc_t proc)
    873 {
    874 	return 0;
    875 }
    876 
    877 /* XXX: threads */
    878 struct fuse_context *
    879 fuse_get_context()
    880 {
    881 
    882 	return &fcon;
    883 }
    884