Home | History | Annotate | Line # | Download | only in librefuse
refuse.c revision 1.24
      1 /*	$NetBSD: refuse.c,v 1.24 2007/02/18 22:42:33 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.24 2007/02/18 22:42:33 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 fuse		*fuse;
    548 	const char		*path_src = PCNPATH(pcn_src);
    549 	const char		*path_dest = PCNPATH(pcn_targ);
    550 	int			ret;
    551 
    552 	fuse = (struct fuse *)pu->pu_privdata;
    553 	if (fuse->op.rename == NULL) {
    554 		return ENOSYS;
    555 	}
    556 
    557 	ret = fuse->op.rename(path_src, path_dest);
    558 
    559 	if (ret == 0) {
    560 	}
    561 
    562 	return -ret;
    563 }
    564 
    565 /* create a link in the file system */
    566 /* ARGSUSED1 */
    567 static int
    568 puffs_fuse_node_link(struct puffs_cc *pcc, void *opc, void *targ,
    569 	const struct puffs_cn *pcn)
    570 {
    571 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    572 	struct puffs_node	*pn = targ;
    573 	struct fuse		*fuse;
    574 	int			ret;
    575 
    576 	fuse = (struct fuse *)pu->pu_privdata;
    577 	if (fuse->op.link == NULL) {
    578 		return ENOSYS;
    579 	}
    580 
    581 	/* wrap up return code */
    582 	ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
    583 
    584 	return -ret;
    585 }
    586 
    587 /*
    588  * fuse's regular interface provides chmod(), chown(), utimes()
    589  * and truncate() + some variations, so try to fit the square block
    590  * in the circle hole and the circle block .... something like that
    591  */
    592 /* ARGSUSED3 */
    593 static int
    594 puffs_fuse_node_setattr(struct puffs_cc *pcc, void *opc,
    595 	const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
    596 {
    597 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    598 	struct puffs_node	*pn = opc;
    599 	struct refusenode	*rn = pn->pn_data;
    600 	struct fuse		*fuse;
    601 	const char		*path = PNPATH(pn);
    602 	mode_t			mode;
    603 	uid_t			uid;
    604 	gid_t			gid;
    605 	int			error, ret;
    606 
    607 	fuse = (struct fuse *)pu->pu_privdata;
    608 
    609 	error = 0;
    610 
    611 	mode = va->va_mode;
    612 	uid = va->va_uid;
    613 	gid = va->va_gid;
    614 
    615 	if (mode != (mode_t)PUFFS_VNOVAL) {
    616 		ret = 0;
    617 
    618 		if (fuse->op.chmod == NULL) {
    619 			error = -ENOSYS;
    620 		} else {
    621 			ret = fuse->op.chmod(path, mode);
    622 			if (ret)
    623 				error = ret;
    624 		}
    625 	}
    626 	if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
    627 		ret = 0;
    628 
    629 		if (fuse->op.chown == NULL) {
    630 			error = -ENOSYS;
    631 		} else {
    632 			ret = fuse->op.chown(path, uid, gid);
    633 			if (ret)
    634 				error = ret;
    635 		}
    636 	}
    637 	if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
    638 	    || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
    639 		ret = 0;
    640 
    641 		if (fuse->op.utimens) {
    642 			struct timespec tv[2];
    643 
    644 			tv[0].tv_sec = va->va_atime.tv_sec;
    645 			tv[0].tv_nsec = va->va_atime.tv_nsec;
    646 			tv[1].tv_sec = va->va_mtime.tv_sec;
    647 			tv[1].tv_nsec = va->va_mtime.tv_nsec;
    648 
    649 			ret = fuse->op.utimens(path, tv);
    650 		} else if (fuse->op.utime) {
    651 			struct utimbuf timbuf;
    652 
    653 			timbuf.actime = va->va_atime.tv_sec;
    654 			timbuf.modtime = va->va_mtime.tv_sec;
    655 
    656 			ret = fuse->op.utime(path, &timbuf);
    657 		} else {
    658 			error = -ENOSYS;
    659 		}
    660 
    661 		if (ret)
    662 			error = ret;
    663 	}
    664 	if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
    665 		ret = 0;
    666 
    667 		if (fuse->op.truncate) {
    668 			ret = fuse->op.truncate(path, (off_t)va->va_size);
    669 		} else if (fuse->op.ftruncate) {
    670 			ret = fuse->op.ftruncate(path, (off_t)va->va_size,
    671 			    &rn->file_info);
    672 		} else {
    673 			error = -ENOSYS;
    674 		}
    675 
    676 		if (ret)
    677 			error = ret;
    678 	}
    679 
    680 	return -error;
    681 }
    682 
    683 /* ARGSUSED2 */
    684 static int
    685 puffs_fuse_node_open(struct puffs_cc *pcc, void *opc, int flags,
    686 	const struct puffs_cred *cred, pid_t pid)
    687 {
    688 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    689 	struct puffs_node	*pn = opc;
    690 	struct refusenode	*rn = pn->pn_data;
    691 	struct fuse		*fuse;
    692 	struct stat		 st;
    693 	const char		*path = PNPATH(pn);
    694 	int			 ret;
    695 
    696 	fuse = (struct fuse *)pu->pu_privdata;
    697 	if (fuse->op.open == NULL) {
    698 		return ENOSYS;
    699 	}
    700 
    701 	/* examine type - if directory, return 0 rather than open */
    702 	ret = (fuse->op.getattr == NULL) ?
    703 		stat(path, &st) :
    704 		(*fuse->op.getattr)(path, &st);
    705 	if (ret == 0 && (st.st_mode & S_IFMT) == S_IFDIR) {
    706 		return 0;
    707 	}
    708 
    709 	if (strcmp(path, "/") == 0) {
    710 		return 0;
    711 	}
    712 
    713 	ret = (*fuse->op.open)(path, &rn->file_info);
    714 
    715 	if (ret == 0) {
    716 		rn->flags |= RN_OPEN;
    717 	}
    718 
    719 	return -ret;
    720 }
    721 
    722 /* read some more from the file */
    723 /* ARGSUSED5 */
    724 static int
    725 puffs_fuse_node_read(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    726 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    727 	int ioflag)
    728 {
    729 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    730 	struct puffs_node	*pn = opc;
    731 	struct refusenode	*rn = pn->pn_data;
    732 	struct fuse		*fuse;
    733 	const char		*path = PNPATH(pn);
    734 	int			ret;
    735 
    736 	fuse = (struct fuse *)pu->pu_privdata;
    737 	if (fuse->op.read == NULL) {
    738 		return ENOSYS;
    739 	}
    740 
    741 	ret = (*fuse->op.read)(path, (char *)buf, *resid, offset,
    742 	    &rn->file_info);
    743 
    744 	if (ret > 0) {
    745 		*resid -= ret;
    746 		ret = 0;
    747 	}
    748 
    749 	return -ret;
    750 }
    751 
    752 /* write to the file */
    753 /* ARGSUSED0 */
    754 static int
    755 puffs_fuse_node_write(struct puffs_cc *pcc, void *opc, uint8_t *buf,
    756 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    757 	int ioflag)
    758 {
    759 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    760 	struct puffs_node	*pn = opc;
    761 	struct refusenode	*rn = pn->pn_data;
    762 	struct fuse		*fuse;
    763 	const char		*path = PNPATH(pn);
    764 	int			ret;
    765 
    766 	fuse = (struct fuse *)pu->pu_privdata;
    767 	if (fuse->op.write == NULL) {
    768 		return ENOSYS;
    769 	}
    770 
    771 	if (ioflag & PUFFS_IO_APPEND)
    772 		offset = pn->pn_va.va_size;
    773 
    774 	ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
    775 	    &rn->file_info);
    776 
    777 	if (ret > 0) {
    778 		*resid -= ret;
    779 		if (offset + ret > pn->pn_va.va_size)
    780 			pn->pn_va.va_size = offset + ret;
    781 		ret = 0;
    782 	}
    783 
    784 	return -ret;
    785 }
    786 
    787 
    788 /* ARGSUSED3 */
    789 static int
    790 puffs_fuse_node_readdir(struct puffs_cc *pcc, void *opc,
    791 	struct dirent *dent, const struct puffs_cred *pcr, off_t *readoff,
    792 	size_t *reslen)
    793 {
    794 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    795 	struct puffs_node	*pn = opc;
    796 	struct refusenode	*rn = pn->pn_data;
    797 	struct fuse		*fuse;
    798 	const char		*path = PNPATH(pn);
    799 	struct fuse_dirh	deh;
    800 	int			ret;
    801 
    802 	fuse = (struct fuse *)pu->pu_privdata;
    803 	if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
    804 		return ENOSYS;
    805 	}
    806 
    807 	/* XXX: how to handle this??? */
    808 	if (*readoff != 0) {
    809 		return 0;
    810 	}
    811 
    812 	deh.dent = dent;
    813 	deh.reslen = *reslen;
    814 	deh.readoff = *readoff;
    815 
    816 	if (fuse->op.readdir)
    817 		ret = fuse->op.readdir(path, &deh, puffs_fuse_fill_dir,
    818 		    *readoff, &rn->file_info);
    819 	else
    820 		ret = fuse->op.getdir(path, &deh, puffs_fuse_dirfil);
    821 	*reslen = deh.reslen;
    822 	*readoff = 1;
    823 
    824 	if (ret == 0) {
    825 	}
    826 
    827 	return -ret;
    828 }
    829 
    830 /* ARGSUSED2 */
    831 static int
    832 puffs_fuse_node_reclaim(struct puffs_cc *pcc, void *opc, pid_t pid)
    833 {
    834 	struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    835 	struct puffs_node	*pn = opc;
    836 	struct refusenode	*rn = pn->pn_data;
    837 	struct fuse		*fuse;
    838 	const char		*path = PNPATH(pn);
    839 	int			ret;
    840 
    841 	fuse = (struct fuse *)pu->pu_privdata;
    842 
    843 	if (rn->flags & RN_OPEN) {
    844 		if (pn->pn_va.va_type == VDIR) {
    845 			if (fuse->op.releasedir == NULL)
    846 				ret = -ENOSYS;
    847 
    848 			ret = fuse->op.releasedir(path, &rn->file_info);
    849 		} else {
    850 			if (fuse->op.release == NULL)
    851 				return ENOSYS;
    852 
    853 			ret = fuse->op.release(path, &rn->file_info);
    854 		}
    855 	}
    856 
    857 	nukern(pn);
    858 
    859 	/*
    860 	 * value ignored by the kernel, but we might as well
    861 	 * return something for debugging purposes
    862 	 */
    863 	return -ret;
    864 }
    865 
    866 /* ARGSUSED1 */
    867 static int
    868 puffs_fuse_fs_unmount(struct puffs_cc *pcc, int flags, pid_t pid)
    869 {
    870         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    871 	struct fuse		*fuse;
    872 
    873 	fuse = (struct fuse *)pu->pu_privdata;
    874 	if (fuse->op.destroy == NULL) {
    875 		return 0;
    876 	}
    877 	(*fuse->op.destroy)(fuse);
    878         return 0;
    879 }
    880 
    881 /* ARGSUSED0 */
    882 static int
    883 puffs_fuse_fs_sync(struct puffs_cc *pcc, int flags,
    884             const struct puffs_cred *cr, pid_t pid)
    885 {
    886         return 0;
    887 }
    888 
    889 /* ARGSUSED2 */
    890 static int
    891 puffs_fuse_fs_statvfs(struct puffs_cc *pcc, struct statvfs *svfsb, pid_t pid)
    892 {
    893         struct puffs_usermount	*pu = puffs_cc_getusermount(pcc);
    894 	struct fuse		*fuse;
    895 	int			ret;
    896 
    897 	fuse = (struct fuse *)pu->pu_privdata;
    898 	if (fuse->op.statfs == NULL) {
    899 		if ((ret = statvfs(PNPATH(pu->pu_pn_root), svfsb)) == -1) {
    900 			return errno;
    901 		}
    902 	} else {
    903 		ret = (*fuse->op.statfs)(PNPATH(pu->pu_pn_root), svfsb);
    904 	}
    905 
    906         return ret;
    907 }
    908 
    909 
    910 
    911 
    912 /* End of puffs_fuse operations */
    913 
    914 /* ARGSUSED3 */
    915 int
    916 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
    917 	size_t size, void *userdata)
    918 {
    919 	struct puffs_usermount	*pu;
    920 	struct puffs_pathobj	*po_root;
    921 	struct puffs_ops	*pops;
    922 	struct statvfs		svfsb;
    923 	struct fuse		*fuse;
    924 	char			 name[64];
    925 	char			*slash;
    926 	int			 ret;
    927 
    928 	/* initialise the puffs operations structure */
    929         PUFFSOP_INIT(pops);
    930 
    931         PUFFSOP_SET(pops, puffs_fuse, fs, sync);
    932         PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
    933         PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
    934 
    935 	/*
    936 	 * XXX: all of these don't possibly need to be
    937 	 * unconditionally set
    938 	 */
    939         PUFFSOP_SET(pops, puffs_fuse, node, lookup);
    940         PUFFSOP_SET(pops, puffs_fuse, node, getattr);
    941         PUFFSOP_SET(pops, puffs_fuse, node, setattr);
    942         PUFFSOP_SET(pops, puffs_fuse, node, readdir);
    943         PUFFSOP_SET(pops, puffs_fuse, node, readlink);
    944         PUFFSOP_SET(pops, puffs_fuse, node, mknod);
    945         PUFFSOP_SET(pops, puffs_fuse, node, create);
    946         PUFFSOP_SET(pops, puffs_fuse, node, remove);
    947         PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
    948         PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
    949         PUFFSOP_SET(pops, puffs_fuse, node, symlink);
    950         PUFFSOP_SET(pops, puffs_fuse, node, rename);
    951         PUFFSOP_SET(pops, puffs_fuse, node, link);
    952         PUFFSOP_SET(pops, puffs_fuse, node, open);
    953         PUFFSOP_SET(pops, puffs_fuse, node, read);
    954         PUFFSOP_SET(pops, puffs_fuse, node, write);
    955         PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
    956 
    957 	NEW(struct fuse, fuse, "fuse_main_real", exit(EXIT_FAILURE));
    958 
    959 	/* copy fuse ops to their own stucture */
    960 	(void) memcpy(&fuse->op, ops, sizeof(fuse->op));
    961 
    962 	fcon.fuse = fuse;
    963 	fcon.private_data = userdata;
    964 
    965 	/* whilst this (assigning the pu_privdata in the puffs
    966 	 * usermount struct to be the fuse struct) might seem like
    967 	 * we are chasing our tail here, the logic is as follows:
    968 		+ the operation wrapper gets called with the puffs
    969 		  calling conventions
    970 		+ we need to fix up args first
    971 		+ then call the fuse user-supplied operation
    972 		+ then we fix up any values on return that we need to
    973 		+ and fix up any nodes, etc
    974 	 * so we need to be able to get at the fuse ops from within the
    975 	 * puffs_usermount struct
    976 	 */
    977 	if ((slash = strrchr(*argv, '/')) == NULL) {
    978 		slash = *argv;
    979 	} else {
    980 		slash += 1;
    981 	}
    982 	(void) snprintf(name, sizeof(name), "refuse:%s", slash);
    983 	pu = puffs_mount(pops, argv[argc - 1], MNT_NODEV | MNT_NOSUID,
    984 			name, fuse,
    985 			PUFFS_FLAG_BUILDPATH
    986 			  | PUFFS_FLAG_OPDUMP
    987 			  | PUFFS_KFLAG_NOCACHE,
    988 			0);
    989 	if (pu == NULL) {
    990 		err(EXIT_FAILURE, "puffs_mount");
    991 	}
    992 
    993 	fuse->pu = pu;
    994 	pu->pu_pn_root = puffs_pn_new(pu, NULL);
    995 	po_root = puffs_getrootpathobj(pu);
    996 	po_root->po_path = strdup("/");
    997 	po_root->po_len = 1;
    998 
    999 	if (fuse->op.init)
   1000 		fcon.private_data = fuse->op.init(NULL); /* XXX */
   1001 
   1002 	statvfs(argv[argc - 1], &svfsb); /* XXX - not really the correct dir */
   1003 	if (puffs_start(pu, pu->pu_pn_root, &svfsb) == -1) {
   1004 		err(EXIT_FAILURE, "puffs_start");
   1005 	}
   1006 
   1007 	ret = puffs_mainloop(fuse->pu, PUFFSLOOP_NODAEMON);
   1008 
   1009 	(void) free(po_root->po_path);
   1010 	FREE(fuse);
   1011 	return ret;
   1012 }
   1013 
   1014 /* ARGSUSED0 */
   1015 int
   1016 fuse_opt_parse(struct fuse_args *args, void *data,
   1017 	const struct fuse_opt *opts, fuse_opt_proc_t proc)
   1018 {
   1019 	return 0;
   1020 }
   1021 
   1022 /* XXX: threads */
   1023 struct fuse_context *
   1024 fuse_get_context()
   1025 {
   1026 
   1027 	return &fcon;
   1028 }
   1029 
   1030 void
   1031 fuse_exit(struct fuse *f)
   1032 {
   1033 
   1034 	puffs_exit(f->pu, 1);
   1035 }
   1036