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