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