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