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