Home | History | Annotate | Line # | Download | only in librefuse
refuse.c revision 1.2
      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 puffs_node	*pn = opc;
    306 	struct fuse		*fuse;
    307 	const char		*path = PCNPATH(pcn);
    308 	int			ret;
    309 
    310 	fuse = (struct fuse *)pu->pu_privdata;
    311 	if (fuse->op.unlink == NULL) {
    312 		return ENOSYS;
    313 	}
    314 
    315 	/* wrap up return code */
    316 	ret = (*fuse->op.unlink)(path);
    317 
    318 	if (ret == 0) {
    319 		puffs_pn_put(pn);
    320 	}
    321 
    322 	return ret;
    323 }
    324 
    325 /* remove the directory */
    326 /* ARGSUSED1 */
    327 static int
    328 puffs_fuse_node_rmdir(struct puffs_cc *pcc, void *opc, void *targ,
    329 	const struct puffs_cn *pcn)
    330 {
    331 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    332 	struct puffs_node	*pn = opc;
    333 	struct fuse		*fuse;
    334 	const char		*path = PCNPATH(pcn);
    335 	int			ret;
    336 
    337 	fuse = (struct fuse *)pu->pu_privdata;
    338 	if (fuse->op.rmdir == NULL) {
    339 		return ENOSYS;
    340 	}
    341 
    342 	/* wrap up return code */
    343 	ret = (*fuse->op.rmdir)(path);
    344 
    345 	if (ret == 0) {
    346 		puffs_pn_put(pn);
    347 	}
    348 
    349 	return ret;
    350 }
    351 
    352 /* create a symbolic link */
    353 /* ARGSUSED1 */
    354 static int
    355 puffs_fuse_node_symlink(struct puffs_cc *pcc, void *opc, void **newnode,
    356 	const struct puffs_cn *pcn_src, const struct vattr *va,
    357 	const char *link_target)
    358 {
    359 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    360 	struct puffs_node	*pn;
    361 	struct fuse		*fuse;
    362 	const char		*path = PCNPATH(pcn_src);
    363 	int			ret;
    364 
    365 	fuse = (struct fuse *)pu->pu_privdata;
    366 	if (fuse->op.symlink == NULL) {
    367 		return ENOSYS;
    368 	}
    369 
    370 	/* wrap up return code */
    371 	ret = (*fuse->op.symlink)(path, link_target);
    372 	/* XXX - check I haven't transposed these args */
    373 
    374 	if (ret == 0) {
    375 		/* fix up nodes */
    376 		pn = puffs_pn_new(pu, NULL);
    377 		if (pn == NULL) {
    378 			unlink(link_target);
    379 			return ENOMEM;
    380 		}
    381 		puffs_setvattr(&pn->pn_va, va);
    382 
    383 		*newnode = pn;
    384 	}
    385 
    386 	return ret;
    387 }
    388 
    389 /* rename a directory entry */
    390 /* ARGSUSED1 */
    391 static int
    392 puffs_fuse_node_rename(struct puffs_cc *pcc, void *opc, void *src,
    393 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    394 	const struct puffs_cn *pcn_targ)
    395 {
    396 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    397 	struct puffs_node	*pn = opc;
    398 	struct vattr		va;
    399 	struct fuse		*fuse;
    400 	const char		*path = PCNPATH(pcn_src);
    401 	int			ret;
    402 
    403 	fuse = (struct fuse *)pu->pu_privdata;
    404 	if (fuse->op.rename == NULL) {
    405 		return ENOSYS;
    406 	}
    407 
    408 	/* wrap up return code */
    409 	ret = (*fuse->op.rename)(path, PCNPATH(pcn_targ));
    410 
    411 	if (ret == 0) {
    412 		(void) memcpy(&va, &pn->pn_va, sizeof(va));
    413 
    414 		puffs_pn_put(pn);
    415 
    416 		pn = puffs_pn_new(pu, NULL);
    417 		if (pn == NULL) {
    418 			return ENOMEM;
    419 		}
    420 		puffs_setvattr(&pn->pn_va, &va);
    421 
    422 	}
    423 
    424 	return ret;
    425 }
    426 
    427 /* create a link in the file system */
    428 /* ARGSUSED1 */
    429 static int
    430 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
    431 	const struct puffs_cn *pcn)
    432 {
    433 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    434 	struct puffs_node	*pn = targ;
    435 	struct fuse		*fuse;
    436 	int			ret;
    437 
    438 	fuse = (struct fuse *)pu->pu_privdata;
    439 	if (fuse->op.link == NULL) {
    440 		return ENOSYS;
    441 	}
    442 
    443 	/* wrap up return code */
    444 	ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
    445 
    446 	if (ret == 0) {
    447 		/* fix up nodes */
    448 		pn = puffs_pn_new(pu, NULL);
    449 		if (pn == NULL) {
    450 			unlink(PCNPATH(pcn));
    451 			return ENOMEM;
    452 		}
    453 	}
    454 
    455 	return ret;
    456 }
    457 
    458 /*
    459 We run into a slight problemette here - puffs provides
    460 setattr/getattr, whilst fuse provides all the usual chown/chmod/chgrp
    461 functionality.  So that we don't miss out on anything when calling a
    462 fuse operation, we have to get the vattr from the existing file,
    463 find out what's changed, and then switch on that to call the fuse
    464 function accordingly.
    465 */
    466 /* ARGSUSED3 */
    467 static int
    468 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
    469 	const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
    470 {
    471 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    472 	struct puffs_node	*pn = opc;
    473 	struct fuse		*fuse;
    474 	const char		*path = PNPATH(pn);
    475 	mode_t			mode;
    476 	uid_t			uid;
    477 	gid_t			gid;
    478 	int			ret;
    479 
    480 	fuse = (struct fuse *)pu->pu_privdata;
    481 
    482 	ret = -1;
    483 
    484 	mode = va->va_mode;
    485 	uid = va->va_uid;
    486 	gid = va->va_gid;
    487 
    488 	if (mode != 0) {
    489 		if (fuse->op.chmod == NULL) {
    490 			return ENOSYS;
    491 		}
    492 		ret = (*fuse->op.chmod)(path, mode);
    493 	}
    494 	if (uid != 0 || gid != 0) {
    495 		if (fuse->op.chown == NULL) {
    496 			return ENOSYS;
    497 		}
    498 		ret = (*fuse->op.chown)(path, uid, gid);
    499 	}
    500 
    501 	if (ret == 0) {
    502 	}
    503 
    504 	return ret;
    505 }
    506 
    507 /* ARGSUSED2 */
    508 static int
    509 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int flags,
    510 	const struct puffs_cred *cred, pid_t pid)
    511 {
    512 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    513 	struct fuse_file_info	 file_info;
    514 	struct puffs_node	*pn = opc;
    515 	struct fuse		*fuse;
    516 	struct stat		 st;
    517 	const char		*path = PNPATH(pn);
    518 	int			 ret;
    519 
    520 	fuse = (struct fuse *)pu->pu_privdata;
    521 	if (fuse->op.open == NULL) {
    522 		return ENOSYS;
    523 	}
    524 
    525 	(void) memset(&file_info, 0x0, sizeof(file_info));
    526 
    527 	/* examine type - if directory, return 0 rather than open */
    528 	ret = (fuse->op.getattr == NULL) ?
    529 		stat(path, &st) :
    530 		(*fuse->op.getattr)(path, &st);
    531 	if (ret == 0 && (st.st_mode & S_IFMT) == S_IFDIR) {
    532 		return 0;
    533 	}
    534 
    535 	if (strcmp(path, "/") == 0) {
    536 		return 0;
    537 	}
    538 
    539 	ret = (*fuse->op.open)(path, &file_info);
    540 
    541 	if (ret == 0) {
    542 	}
    543 
    544 	return ret;
    545 }
    546 
    547 /* read some more from the file */
    548 /* ARGSUSED5 */
    549 static int
    550 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    551 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    552 	int ioflag)
    553 {
    554 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    555 	struct fuse_file_info	file_info;
    556 	struct puffs_node	*pn = opc;
    557 	struct fuse		*fuse;
    558 	const char		*path = PNPATH(pn);
    559 	int			ret;
    560 
    561 	fuse = (struct fuse *)pu->pu_privdata;
    562 	if (fuse->op.read == NULL) {
    563 		return ENOSYS;
    564 	}
    565 
    566 	(void) memset(&file_info, 0x0, sizeof(file_info));
    567 	/* XXX fill in file_info here */
    568 
    569 	ret = (*fuse->op.read)(path, (char *)buf, *resid, offset, &file_info);
    570 
    571 	if (ret > 0) {
    572 		*resid -= ret;
    573 	}
    574 
    575 	return 0;
    576 }
    577 
    578 /* write to the file */
    579 /* ARGSUSED0 */
    580 static int
    581 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    582 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    583 	int ioflag)
    584 {
    585 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    586 	struct fuse_file_info	file_info;
    587 	struct puffs_node	*pn = opc;
    588 	struct fuse		*fuse;
    589 	const char		*path = PNPATH(pn);
    590 	int			ret;
    591 
    592 	fuse = (struct fuse *)pu->pu_privdata;
    593 	if (fuse->op.write == NULL) {
    594 		return ENOSYS;
    595 	}
    596 
    597 	(void) memset(&file_info, 0x0, sizeof(file_info));
    598 
    599 	/* XXX fill in file_info here */
    600 
    601 	ret = (*fuse->op.write)(path, (char *)buf, *resid, offset, &file_info);
    602 
    603 	if (ret > 0) {
    604 		*resid -= ret;
    605 	}
    606 
    607 	return ret;
    608 }
    609 
    610 
    611 /* ARGSUSED3 */
    612 static int
    613 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
    614 	struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
    615 	size_t *reslen)
    616 {
    617 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    618 	struct fuse_file_info	file_info;
    619 	struct puffs_node	*pn = opc;
    620 	struct fuse		*fuse;
    621 	const char		*path = PNPATH(pn);
    622 	struct feh		feh; /* XXX */
    623 	int			ret;
    624 
    625 	fuse = (struct fuse *)pu->pu_privdata;
    626 	if (fuse->op.readdir == NULL) {
    627 		return ENOSYS;
    628 	}
    629 
    630 	/* XXX: how to handle this??? */
    631 	if (*readoff != 0) {
    632 		return 0;
    633 	}
    634 
    635 	(void) memset(&file_info, 0x0, sizeof(file_info));
    636 	/* XXX - fill in file_info here */
    637 
    638 	feh.dent = dent;
    639 	feh.reslen = *reslen;
    640 	ret = (*fuse->op.readdir)(path, &feh, puffs_fuse_dirfiller, *readoff, &file_info);
    641 	*reslen = feh.reslen;
    642 	*readoff = 1;
    643 
    644 	if (ret == 0) {
    645 	}
    646 
    647 	return ret;
    648 }
    649 
    650 /* ARGSUSED1 */
    651 static int
    652 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
    653 {
    654         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    655 	struct fuse		*fuse;
    656 
    657 	fuse = (struct fuse *)pu->pu_privdata;
    658 	if (fuse->op.destroy == NULL) {
    659 		return 0;
    660 	}
    661 	(*fuse->op.destroy)(fuse);
    662         return 0;
    663 }
    664 
    665 /* ARGSUSED0 */
    666 static int
    667 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
    668             const struct puffs_cred *cr, pid_t pid)
    669 {
    670         return 0;
    671 }
    672 
    673 /* ARGSUSED2 */
    674 static int
    675 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
    676 {
    677         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    678 	struct fuse		*fuse;
    679 	int			ret;
    680 
    681 	fuse = (struct fuse *)pu->pu_privdata;
    682 	if (fuse->op.statfs == NULL) {
    683 		if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
    684 			return errno;
    685 		}
    686 	} else {
    687 		ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
    688 	}
    689 
    690         return ret;
    691 }
    692 
    693 
    694 
    695 
    696 /* End of puffs_fuse operations */
    697 
    698 /* ARGSUSED3 */
    699 int
    700 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
    701 	size_t size, void *userdata)
    702 {
    703 	struct puffs_usermount	*pu;
    704 	struct puffs_pathobj	*po_root;
    705 	struct puffs_ops	*pops;
    706 	struct statvfs		svfsb;
    707 	struct fuse		*fuse;
    708 	char			 name[64];
    709 	char			*slash;
    710 	int			 ret;
    711 
    712 	/* initialise the puffs operations structure */
    713         PUFFSOP_INIT(pops);
    714 
    715         PUFFSOP_SET(pops, puffs_fuse, fs, sync);
    716         PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
    717         PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
    718 
    719 	/*
    720 	 * XXX: all of these don't possibly need to be
    721 	 * unconditionally set
    722 	 */
    723         PUFFSOP_SET(pops, puffs_fuse, node, lookup);
    724         PUFFSOP_SET(pops, puffs_fuse, node, getattr);
    725         PUFFSOP_SET(pops, puffs_fuse, node, readdir);
    726         PUFFSOP_SET(pops, puffs_fuse, node, readlink);
    727         PUFFSOP_SET(pops, puffs_fuse, node, mknod);
    728         PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
    729         PUFFSOP_SET(pops, puffs_fuse, node, remove);
    730         PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
    731         PUFFSOP_SET(pops, puffs_fuse, node, symlink);
    732         PUFFSOP_SET(pops, puffs_fuse, node, rename);
    733         PUFFSOP_SET(pops, puffs_fuse, node, link);
    734         PUFFSOP_SET(pops, puffs_fuse, node, setattr);
    735         PUFFSOP_SET(pops, puffs_fuse, node, open);
    736         PUFFSOP_SET(pops, puffs_fuse, node, read);
    737         PUFFSOP_SET(pops, puffs_fuse, node, write);
    738         PUFFSOP_SET(pops, puffs_fuse, node, readdir);
    739         PUFFSOP_SET(pops, puffs_fuse, node, read);
    740         PUFFSOP_SET(pops, puffs_fuse, node, write);
    741 	/* XXX: reclaim is missing */
    742 
    743 	NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
    744 
    745 	/* copy fuse ops to their own stucture */
    746 	(void) memcpy(&fuse->op, ops, sizeof(fuse->op));
    747 
    748 	/* whilst this (assigning the pu_privdata in the puffs
    749 	 * usermount struct to be the fuse struct) might seem like
    750 	 * we are chasing our tail here, the logic is as follows:
    751 		+ the operation wrapper gets called with the puffs
    752 		  calling conventions
    753 		+ we need to fix up args first
    754 		+ then call the fuse user-supplied operation
    755 		+ then we fix up any values on return that we need to
    756 		+ and fix up any nodes, etc
    757 	* so we need to be able to get at the fuse ops from within the
    758 	* puffs_usermount struct
    759 	*/
    760 	if ((slash = strrchr(*argv, '/')) == NULL) {
    761 		slash = *argv;
    762 	} else {
    763 		slash += 1;
    764 	}
    765 	(void) snprintf(name, sizeof(name), "refuse:%s", slash);
    766 	pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
    767 			name, fuse,
    768 			PUFFS_FLAG_BUILDPATH | PUFFS_FLAG_OPDUMP, 0);
    769 	if (pu == NULL) {
    770 		err(EXIT_FAILURE, "puffs_mount");
    771 	}
    772 
    773 	fuse->pu = pu;
    774 	pu->pu_pn_root = puffs_pn_new(pu, NULL);
    775 	po_root = puffs_getrootpathobj(pu);
    776 	po_root->po_path = strdup("/");
    777 	po_root->po_len = 1;
    778 
    779 	statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
    780 	if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
    781 		err(EXIT_FAILURE, "puffs_start");
    782 	}
    783 
    784 	ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
    785 
    786 	(void) free(po_root->po_path);
    787 	FREE(fuse);
    788 	return ret;
    789 }
    790 
    791 /* ARGSUSED0 */
    792 int
    793 fuse_opt_parse(struct fuse_args *args, void *data,
    794 	const struct fuse_opt *opts, fuse_opt_proc_t proc)
    795 {
    796 	return 0;
    797 }
    798