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