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