Home | History | Annotate | Line # | Download | only in librefuse
refuse.c revision 1.112
      1 /*	$NetBSD: refuse.c,v 1.112 2022/01/22 08:05:35 pho Exp $	*/
      2 
      3 /*
      4  * Copyright  2007 Alistair Crooks.  All rights reserved.
      5  * Copyright  2007 Antti Kantee.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of the author may not be used to endorse or promote
     16  *    products derived from this software without specific prior written
     17  *    permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     25  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #if !defined(lint)
     34 __RCSID("$NetBSD: refuse.c,v 1.112 2022/01/22 08:05:35 pho Exp $");
     35 #endif /* !lint */
     36 
     37 #include <sys/types.h>
     38 
     39 #include <assert.h>
     40 #include <err.h>
     41 #include <errno.h>
     42 #include <fuse_internal.h>
     43 #include <fuse_opt.h>
     44 #include <paths.h>
     45 #include <puffs.h>
     46 #include <stdbool.h>
     47 #include <stddef.h>
     48 #include <stdio.h>
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <unistd.h>
     52 #ifdef MULTITHREADED_REFUSE
     53 #include <pthread.h>
     54 #endif
     55 
     56 typedef uint64_t	 fuse_ino_t;
     57 
     58 struct refuse_config {
     59 	int debug;
     60 	char *fsname;
     61 };
     62 
     63 #define REFUSE_OPT(t, p, v) \
     64 	{ t, offsetof(struct refuse_config, p), v }
     65 
     66 static struct fuse_opt refuse_opts[] = {
     67 	REFUSE_OPT("debug"    , debug , 1),
     68 	REFUSE_OPT("fsname=%s", fsname, 0),
     69 	FUSE_OPT_END
     70 };
     71 
     72 /* this is the private fuse structure */
     73 struct fuse {
     74 	struct puffs_usermount	*pu;
     75 	int			dead;
     76 	struct fuse_operations	op;		/* switch table of operations */
     77 	int			compat;		/* compat level -
     78 						 * not used in puffs_fuse */
     79 	struct node		**name_table;
     80 	size_t			name_table_size;
     81 	struct node		**id_table;
     82 	size_t			id_table_size;
     83 	fuse_ino_t		ctr;
     84 	unsigned int		generation;
     85 	unsigned int		hidectr;
     86 	pthread_mutex_t		lock;
     87 	pthread_rwlock_t	tree_lock;
     88 	void			*user_data;
     89 	int			intr_installed;
     90 };
     91 
     92 struct puffs_fuse_dirh {
     93 	void *dbuf;
     94 	struct dirent *d;
     95 
     96 	size_t reslen;
     97 	size_t bufsize;
     98 };
     99 
    100 struct refusenode {
    101 	struct fuse_file_info	file_info;
    102 	struct puffs_fuse_dirh	dirh;
    103 	int opencount;
    104 	int flags;
    105 };
    106 #define RN_ROOT		0x01
    107 #define RN_OPEN		0x02	/* XXX: could just use opencount */
    108 
    109 static int fuse_setattr(struct fuse *, struct puffs_node *,
    110 			const char *, const struct vattr *);
    111 
    112 static struct puffs_node *
    113 newrn(struct puffs_usermount *pu)
    114 {
    115 	struct puffs_node *pn;
    116 	struct refusenode *rn;
    117 
    118 	if ((rn = calloc(1, sizeof(*rn))) == NULL) {
    119 		err(EXIT_FAILURE, "newrn");
    120 	}
    121 	pn = puffs_pn_new(pu, rn);
    122 
    123 	return pn;
    124 }
    125 
    126 static void
    127 nukern(struct puffs_node *pn)
    128 {
    129 	struct refusenode *rn = pn->pn_data;
    130 
    131 	free(rn->dirh.dbuf);
    132 	free(rn);
    133 	puffs_pn_put(pn);
    134 }
    135 
    136 /* XXX - not threadsafe */
    137 static ino_t fakeino = 3;
    138 
    139 /***************** start of pthread context routines ************************/
    140 
    141 /*
    142  * Notes on fuse_context:
    143  * we follow fuse's lead and use the pthread specific information to hold
    144  * a reference to the fuse_context structure for this thread.
    145  */
    146 #ifdef MULTITHREADED_REFUSE
    147 static pthread_mutex_t		context_mutex = PTHREAD_MUTEX_INITIALIZER;
    148 static pthread_key_t		context_key;
    149 static unsigned long		context_refc;
    150 #endif
    151 
    152 /* return the fuse_context struct related to this thread */
    153 struct fuse_context *
    154 fuse_get_context(void)
    155 {
    156 #ifdef MULTITHREADED_REFUSE
    157 	struct fuse_context	*ctxt;
    158 
    159 	if ((ctxt = pthread_getspecific(context_key)) == NULL) {
    160 		if ((ctxt = calloc(1, sizeof(struct fuse_context))) == NULL) {
    161 			abort();
    162 		}
    163 		pthread_setspecific(context_key, ctxt);
    164 	}
    165 	return ctxt;
    166 #else
    167 	static struct fuse_context	fcon;
    168 
    169 	return &fcon;
    170 #endif
    171 }
    172 
    173 /* used as a callback function */
    174 #ifdef MULTITHREADED_REFUSE
    175 static void
    176 free_context(void *ctxt)
    177 {
    178 	free(ctxt);
    179 }
    180 #endif
    181 
    182 /*
    183  * Create the pthread key.  The reason for the complexity is to
    184  * enable use of multiple fuse instances within a single process.
    185  */
    186 static int
    187 create_context_key(void)
    188 {
    189 #ifdef MULTITHREADED_REFUSE
    190 	int rv;
    191 
    192 	rv = pthread_mutex_lock(&context_mutex);
    193 	assert(rv == 0);
    194 
    195 	if (context_refc == 0) {
    196 		if (pthread_key_create(&context_key, free_context) != 0) {
    197 			warnx("create_context_key: pthread_key_create failed");
    198 			pthread_mutex_unlock(&context_mutex);
    199 			return 0;
    200 		}
    201 	}
    202 	context_refc += 1;
    203 	pthread_mutex_unlock(&context_mutex);
    204 	return 1;
    205 #else
    206 	return 1;
    207 #endif
    208 }
    209 
    210 static void
    211 delete_context_key(void)
    212 {
    213 #ifdef MULTITHREADED_REFUSE
    214 	pthread_mutex_lock(&context_mutex);
    215 	/* If we are the last fuse instances using the key, delete it */
    216 	if (--context_refc == 0) {
    217 		free(pthread_getspecific(context_key));
    218 		pthread_key_delete(context_key);
    219 	}
    220 	pthread_mutex_unlock(&context_mutex);
    221 #endif
    222 }
    223 
    224 /* set the uid and gid of the calling process in the current fuse context */
    225 static void
    226 set_fuse_context_uid_gid(const struct puffs_cred *cred)
    227 {
    228 	struct fuse_context	*fusectx;
    229 	uid_t			 uid;
    230 	gid_t			 gid;
    231 
    232 	fusectx = fuse_get_context();
    233 	if (puffs_cred_getuid(cred, &uid) == 0) {
    234 		fusectx->uid = uid;
    235 	}
    236 	if (puffs_cred_getgid(cred, &gid) == 0) {
    237 		fusectx->gid = gid;
    238 	}
    239 }
    240 
    241 /* set the pid of the calling process in the current fuse context */
    242 static void
    243 set_fuse_context_pid(struct puffs_usermount *pu)
    244 {
    245 	struct puffs_cc		*pcc = puffs_cc_getcc(pu);
    246 	struct fuse_context	*fusectx;
    247 
    248 	fusectx = fuse_get_context();
    249 	puffs_cc_getcaller(pcc, &fusectx->pid, NULL);
    250 }
    251 
    252 /***************** end of pthread context routines ************************/
    253 
    254 #define DIR_CHUNKSIZE 4096
    255 static int
    256 fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino,
    257 	uint8_t dtype)
    258 {
    259 
    260 	/* initial? */
    261 	if (dh->bufsize == 0) {
    262 		if ((dh->dbuf = calloc(1, DIR_CHUNKSIZE)) == NULL) {
    263 			abort();
    264 		}
    265 		dh->d = dh->dbuf;
    266 		dh->reslen = dh->bufsize = DIR_CHUNKSIZE;
    267 	}
    268 
    269 	if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen)) {
    270 		return 0;
    271 	}
    272 
    273 	/* try to increase buffer space */
    274 	dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE);
    275 	if (dh->dbuf == NULL) {
    276 		abort();
    277 	}
    278 	dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen));
    279 	dh->reslen += DIR_CHUNKSIZE;
    280 	dh->bufsize += DIR_CHUNKSIZE;
    281 
    282 	return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen);
    283 }
    284 
    285 /* ARGSUSED3 */
    286 /* XXX: I have no idea how "off" is supposed to be used */
    287 static int
    288 puffs_fuse_fill_dir(void *buf, const char *name,
    289 	const struct stat *stbuf, off_t off)
    290 {
    291 	struct puffs_fuse_dirh *deh = buf;
    292 	ino_t dino;
    293 	uint8_t dtype;
    294 
    295 	if (stbuf == NULL) {
    296 		dtype = DT_UNKNOWN;
    297 		dino = fakeino++;
    298 	} else {
    299 		dtype = (uint8_t)puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
    300 		dino = stbuf->st_ino;
    301 
    302 		/*
    303 		 * Some FUSE file systems like to always use 0 as the
    304 		 * inode number.   Our readdir() doesn't like to show
    305 		 * directory entries with inode number 0 ==> workaround.
    306 		 */
    307 		if (dino == 0) {
    308 			dino = fakeino++;
    309 		}
    310 	}
    311 
    312 	return fill_dirbuf(deh, name, dino, dtype);
    313 }
    314 
    315 static int
    316 puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
    317 {
    318 	ino_t dino;
    319 	int dtype;
    320 
    321 	if ((dtype = type) == 0) {
    322 		dtype = DT_UNKNOWN;
    323 	}
    324 
    325 	dino = (ino) ? ino : fakeino++;
    326 
    327 	return fill_dirbuf(h, name, dino, dtype);
    328 }
    329 
    330 #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
    331 #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
    332 
    333 /* ARGSUSED1 */
    334 static int
    335 fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
    336 	struct vattr *va)
    337 {
    338 	struct stat		 st;
    339 	int			ret;
    340 
    341 	if (fuse->op.getattr == NULL) {
    342 		return ENOSYS;
    343 	}
    344 
    345 	/* wrap up return code */
    346 	memset(&st, 0, sizeof(st));
    347 	ret = (*fuse->op.getattr)(path, &st);
    348 
    349 	if (ret == 0) {
    350 		if (st.st_blksize == 0)
    351 			st.st_blksize = DEV_BSIZE;
    352 		puffs_stat2vattr(va, &st);
    353 	}
    354 
    355 	return -ret;
    356 }
    357 
    358 /* utility function to set various elements of the attribute */
    359 static int
    360 fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
    361 	const struct vattr *va)
    362 {
    363 	struct refusenode	*rn = pn->pn_data;
    364 	mode_t			mode;
    365 	uid_t			uid;
    366 	gid_t			gid;
    367 	int			error, ret;
    368 
    369 	error = 0;
    370 
    371 	mode = va->va_mode;
    372 	uid = va->va_uid;
    373 	gid = va->va_gid;
    374 
    375 	if (mode != (mode_t)PUFFS_VNOVAL) {
    376 		ret = 0;
    377 
    378 		if (fuse->op.chmod == NULL) {
    379 			error = -ENOSYS;
    380 		} else {
    381 			ret = fuse->op.chmod(path, mode);
    382 			if (ret)
    383 				error = ret;
    384 		}
    385 	}
    386 	if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
    387 		ret = 0;
    388 
    389 		if (fuse->op.chown == NULL) {
    390 			error = -ENOSYS;
    391 		} else {
    392 			ret = fuse->op.chown(path, uid, gid);
    393 			if (ret)
    394 				error = ret;
    395 		}
    396 	}
    397 	if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
    398 	    || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
    399 		ret = 0;
    400 
    401 		if (fuse->op.utimens) {
    402 			struct timespec tv[2];
    403 
    404 			tv[0].tv_sec = va->va_atime.tv_sec;
    405 			tv[0].tv_nsec = va->va_atime.tv_nsec;
    406 			tv[1].tv_sec = va->va_mtime.tv_sec;
    407 			tv[1].tv_nsec = va->va_mtime.tv_nsec;
    408 
    409 			ret = fuse->op.utimens(path, tv);
    410 		} else if (fuse->op.utime) {
    411 			struct utimbuf timbuf;
    412 
    413 			timbuf.actime = va->va_atime.tv_sec;
    414 			timbuf.modtime = va->va_mtime.tv_sec;
    415 
    416 			ret = fuse->op.utime(path, &timbuf);
    417 		} else {
    418 			error = -ENOSYS;
    419 		}
    420 
    421 		if (ret)
    422 			error = ret;
    423 	}
    424 	if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
    425 		ret = 0;
    426 
    427 		if (fuse->op.truncate) {
    428 			ret = fuse->op.truncate(path, (off_t)va->va_size);
    429 		} else if (fuse->op.ftruncate) {
    430 			ret = fuse->op.ftruncate(path, (off_t)va->va_size,
    431 			    &rn->file_info);
    432 		} else {
    433 			error = -ENOSYS;
    434 		}
    435 
    436 		if (ret)
    437 			error = ret;
    438 	}
    439 	/* XXX: no reflection with reality */
    440 	puffs_setvattr(&pn->pn_va, va);
    441 
    442 	return -error;
    443 
    444 }
    445 
    446 static int
    447 fuse_newnode(struct puffs_usermount *pu, const char *path,
    448 	const struct vattr *va, struct fuse_file_info *fi,
    449 	struct puffs_newinfo *pni, struct puffs_node **pn_new)
    450 {
    451 	struct puffs_node	*pn;
    452 	struct refusenode	*rn;
    453 	struct vattr		 newva;
    454 	struct fuse		*fuse;
    455 
    456 	fuse = puffs_getspecific(pu);
    457 
    458 	/* fix up nodes */
    459 	pn = newrn(pu);
    460 	if (pn == NULL) {
    461 		if (va->va_type == VDIR) {
    462 			FUSE_ERR_RMDIR(fuse, path);
    463 		} else {
    464 			FUSE_ERR_UNLINK(fuse, path);
    465 		}
    466 		return ENOMEM;
    467 	}
    468 	fuse_setattr(fuse, pn, path, va);
    469 	if (fuse_getattr(fuse, pn, path, &newva) == 0)
    470 		puffs_setvattr(&pn->pn_va, &newva);
    471 
    472 	rn = pn->pn_data;
    473 	if (fi)
    474 		memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
    475 
    476 	puffs_newinfo_setcookie(pni, pn);
    477 	if (pn_new)
    478 		*pn_new = pn;
    479 
    480 	return 0;
    481 }
    482 
    483 
    484 /* operation wrappers start here */
    485 
    486 /* lookup the path */
    487 /* ARGSUSED1 */
    488 static int
    489 puffs_fuse_node_lookup(struct puffs_usermount *pu, void *opc,
    490 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
    491 {
    492 	struct puffs_node	*pn_res;
    493 	struct stat		 st;
    494 	struct fuse		*fuse;
    495 	const char		*path = PCNPATH(pcn);
    496 	int			 ret;
    497 
    498 	fuse = puffs_getspecific(pu);
    499 
    500 	set_fuse_context_uid_gid(pcn->pcn_cred);
    501 
    502 	ret = fuse->op.getattr(path, &st);
    503 
    504 	if (ret != 0) {
    505 		return -ret;
    506 	}
    507 
    508 	/* XXX: fiXXXme unconst */
    509 	pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
    510 	    __UNCONST(&pcn->pcn_po_full));
    511 	if (pn_res == NULL) {
    512 		pn_res = newrn(pu);
    513 		if (pn_res == NULL)
    514 			return errno;
    515 		puffs_stat2vattr(&pn_res->pn_va, &st);
    516 	}
    517 
    518 	puffs_newinfo_setcookie(pni, pn_res);
    519 	puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type);
    520 	puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size);
    521 	puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev);
    522 
    523 	return 0;
    524 }
    525 
    526 /* get attributes for the path name */
    527 /* ARGSUSED3 */
    528 static int
    529 puffs_fuse_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
    530 	const struct puffs_cred *pcr)
    531 {
    532 	struct puffs_node	*pn = opc;
    533 	struct fuse		*fuse;
    534 	const char		*path = PNPATH(pn);
    535 
    536 	fuse = puffs_getspecific(pu);
    537 
    538 	set_fuse_context_uid_gid(pcr);
    539 
    540 	return fuse_getattr(fuse, pn, path, va);
    541 }
    542 
    543 /* read the contents of the symbolic link */
    544 /* ARGSUSED2 */
    545 static int
    546 puffs_fuse_node_readlink(struct puffs_usermount *pu, void *opc,
    547 	const struct puffs_cred *cred, char *linkname, size_t *linklen)
    548 {
    549 	struct puffs_node	*pn = opc;
    550 	struct fuse		*fuse;
    551 	const char		*path = PNPATH(pn), *p;
    552 	int			ret;
    553 
    554 	fuse = puffs_getspecific(pu);
    555 	if (fuse->op.readlink == NULL) {
    556 		return ENOSYS;
    557 	}
    558 
    559 	set_fuse_context_uid_gid(cred);
    560 
    561 	/* wrap up return code */
    562 	ret = (*fuse->op.readlink)(path, linkname, *linklen);
    563 
    564 	if (ret == 0) {
    565 		p = memchr(linkname, '\0', *linklen);
    566 		if (!p)
    567 			return EINVAL;
    568 
    569 		*linklen = (size_t)(p - linkname);
    570 	}
    571 
    572 	return -ret;
    573 }
    574 
    575 /* make the special node */
    576 /* ARGSUSED1 */
    577 static int
    578 puffs_fuse_node_mknod(struct puffs_usermount *pu, void *opc,
    579 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
    580 	const struct vattr *va)
    581 {
    582 	struct fuse		*fuse;
    583 	mode_t			 mode;
    584 	const char		*path = PCNPATH(pcn);
    585 	int			ret;
    586 
    587 	fuse = puffs_getspecific(pu);
    588 	if (fuse->op.mknod == NULL) {
    589 		return ENOSYS;
    590 	}
    591 
    592 	set_fuse_context_uid_gid(pcn->pcn_cred);
    593 
    594 	/* wrap up return code */
    595 	mode = puffs_addvtype2mode(va->va_mode, va->va_type);
    596 	ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
    597 
    598 	if (ret == 0) {
    599 		ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
    600 	}
    601 
    602 	return -ret;
    603 }
    604 
    605 /* make a directory */
    606 /* ARGSUSED1 */
    607 static int
    608 puffs_fuse_node_mkdir(struct puffs_usermount *pu, void *opc,
    609 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
    610 	const struct vattr *va)
    611 {
    612 	struct fuse		*fuse;
    613 	mode_t			 mode = va->va_mode;
    614 	const char		*path = PCNPATH(pcn);
    615 	int			ret;
    616 
    617 	fuse = puffs_getspecific(pu);
    618 
    619 	set_fuse_context_uid_gid(pcn->pcn_cred);
    620 
    621 	if (fuse->op.mkdir == NULL) {
    622 		return ENOSYS;
    623 	}
    624 
    625 	/* wrap up return code */
    626 	ret = (*fuse->op.mkdir)(path, mode);
    627 
    628 	if (ret == 0) {
    629 		ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
    630 	}
    631 
    632 	return -ret;
    633 }
    634 
    635 /*
    636  * create a regular file
    637  *
    638  * since linux/fuse sports using mknod for creating regular files
    639  * instead of having a separate call for it in some versions, if
    640  * we don't have create, just jump to op->mknod.
    641  */
    642 /*ARGSUSED1*/
    643 static int
    644 puffs_fuse_node_create(struct puffs_usermount *pu, void *opc,
    645 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
    646 	const struct vattr *va)
    647 {
    648 	struct fuse		*fuse;
    649 	struct fuse_file_info	fi;
    650 	struct puffs_node	*pn;
    651 	mode_t			mode = va->va_mode;
    652 	const char		*path = PCNPATH(pcn);
    653 	int			ret, created;
    654 
    655 	fuse = puffs_getspecific(pu);
    656 
    657 	set_fuse_context_uid_gid(pcn->pcn_cred);
    658 
    659 	memset(&fi, 0, sizeof(fi));
    660 	created = 0;
    661 	if (fuse->op.create) {
    662 		/* In puffs "create" and "open" are two separate operations
    663 		 * with atomicity achieved by locking the parent vnode. In
    664 		 * fuse, on the other hand, "create" is actually a
    665 		 * create-and-open-atomically and the open flags (O_RDWR,
    666 		 * O_APPEND, ...) are passed via fi.flags. So the only way to
    667 		 * emulate the fuse semantics is to open the file with dummy
    668 		 * flags and then immediately close it.
    669 		 *
    670 		 * You might think that we could simply use fuse->op.mknod all
    671 		 * the time but no, that's not possible because most file
    672 		 * systems nowadays expect op.mknod to be called only for
    673 		 * non-regular files and many don't even support it. */
    674 		fi.flags = O_WRONLY | O_CREAT | O_EXCL;
    675 		ret = fuse->op.create(path, mode | S_IFREG, &fi);
    676 		if (ret == 0)
    677 			created = 1;
    678 
    679 	} else if (fuse->op.mknod) {
    680 		ret = fuse->op.mknod(path, mode | S_IFREG, 0);
    681 
    682 	} else {
    683 		ret = -ENOSYS;
    684 	}
    685 
    686 	if (ret == 0) {
    687 		ret = fuse_newnode(pu, path, va, &fi, pni, &pn);
    688 
    689 		/* sweet..  create also open the file */
    690 		if (created && fuse->op.release) {
    691 			struct refusenode *rn = pn->pn_data;
    692 			/* The return value of op.release is expected to be
    693 			 * discarded. */
    694 			(void)fuse->op.release(path, &rn->file_info);
    695 		}
    696 	}
    697 
    698 	return -ret;
    699 }
    700 
    701 /* remove the directory entry */
    702 /* ARGSUSED1 */
    703 static int
    704 puffs_fuse_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
    705 	const struct puffs_cn *pcn)
    706 {
    707 	struct puffs_node	*pn_targ = targ;
    708 	struct fuse		*fuse;
    709 	const char		*path = PNPATH(pn_targ);
    710 	int			ret;
    711 
    712 	fuse = puffs_getspecific(pu);
    713 
    714 	set_fuse_context_uid_gid(pcn->pcn_cred);
    715 
    716 	if (fuse->op.unlink == NULL) {
    717 		return ENOSYS;
    718 	}
    719 
    720 	/* wrap up return code */
    721 	ret = (*fuse->op.unlink)(path);
    722 
    723 	return -ret;
    724 }
    725 
    726 /* remove the directory */
    727 /* ARGSUSED1 */
    728 static int
    729 puffs_fuse_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
    730 	const struct puffs_cn *pcn)
    731 {
    732 	struct puffs_node	*pn_targ = targ;
    733 	struct fuse		*fuse;
    734 	const char		*path = PNPATH(pn_targ);
    735 	int			ret;
    736 
    737 	fuse = puffs_getspecific(pu);
    738 
    739 	set_fuse_context_uid_gid(pcn->pcn_cred);
    740 
    741 	if (fuse->op.rmdir == NULL) {
    742 		return ENOSYS;
    743 	}
    744 
    745 	/* wrap up return code */
    746 	ret = (*fuse->op.rmdir)(path);
    747 
    748 	return -ret;
    749 }
    750 
    751 /* create a symbolic link */
    752 /* ARGSUSED1 */
    753 static int
    754 puffs_fuse_node_symlink(struct puffs_usermount *pu, void *opc,
    755 	struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
    756 	const struct vattr *va, const char *link_target)
    757 {
    758 	struct fuse		*fuse;
    759 	const char		*path = PCNPATH(pcn_src);
    760 	int			ret;
    761 
    762 	fuse = puffs_getspecific(pu);
    763 
    764 	set_fuse_context_uid_gid(pcn_src->pcn_cred);
    765 
    766 	if (fuse->op.symlink == NULL) {
    767 		return ENOSYS;
    768 	}
    769 
    770 	/* wrap up return code */
    771 	ret = fuse->op.symlink(link_target, path);
    772 
    773 	if (ret == 0) {
    774 		ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
    775 	}
    776 
    777 	return -ret;
    778 }
    779 
    780 /* rename a directory entry */
    781 /* ARGSUSED1 */
    782 static int
    783 puffs_fuse_node_rename(struct puffs_usermount *pu, void *opc, void *src,
    784 	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
    785 	const struct puffs_cn *pcn_targ)
    786 {
    787 	struct fuse		*fuse;
    788 	const char		*path_src = PCNPATH(pcn_src);
    789 	const char		*path_dest = PCNPATH(pcn_targ);
    790 	int			ret;
    791 
    792 	fuse = puffs_getspecific(pu);
    793 
    794 	set_fuse_context_uid_gid(pcn_targ->pcn_cred);
    795 
    796 	if (fuse->op.rename == NULL) {
    797 		return ENOSYS;
    798 	}
    799 
    800 	ret = fuse->op.rename(path_src, path_dest);
    801 
    802 	if (ret == 0) {
    803 	}
    804 
    805 	return -ret;
    806 }
    807 
    808 /* create a link in the file system */
    809 /* ARGSUSED1 */
    810 static int
    811 puffs_fuse_node_link(struct puffs_usermount *pu, void *opc, void *targ,
    812 	const struct puffs_cn *pcn)
    813 {
    814 	struct puffs_node	*pn = targ;
    815 	struct fuse		*fuse;
    816 	int			ret;
    817 
    818 	fuse = puffs_getspecific(pu);
    819 
    820 	set_fuse_context_uid_gid(pcn->pcn_cred);
    821 
    822 	if (fuse->op.link == NULL) {
    823 		return ENOSYS;
    824 	}
    825 
    826 	/* wrap up return code */
    827 	ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
    828 
    829 	return -ret;
    830 }
    831 
    832 /*
    833  * fuse's regular interface provides chmod(), chown(), utimes()
    834  * and truncate() + some variations, so try to fit the square block
    835  * in the circle hole and the circle block .... something like that
    836  */
    837 /* ARGSUSED3 */
    838 static int
    839 puffs_fuse_node_setattr(struct puffs_usermount *pu, void *opc,
    840 	const struct vattr *va, const struct puffs_cred *pcr)
    841 {
    842 	struct puffs_node	*pn = opc;
    843 	struct fuse		*fuse;
    844 	const char		*path = PNPATH(pn);
    845 
    846 	fuse = puffs_getspecific(pu);
    847 
    848 	set_fuse_context_uid_gid(pcr);
    849 
    850 	return fuse_setattr(fuse, pn, path, va);
    851 }
    852 
    853 static int
    854 puffs_fuse_node_pathconf(struct puffs_usermount *pu, void *opc,
    855 	int name, __register_t *retval)
    856 {
    857 	/* Returning EINVAL for pathconf(2) means that this filesystem
    858 	 * does not support an association of the given name with the
    859 	 * file. This is necessary because the default error code
    860 	 * returned by the puffs kernel module (ENOTSUPP) is not
    861 	 * suitable for an errno from pathconf(2), and "ls -l"
    862 	 * complains about it. */
    863 	return EINVAL;
    864 }
    865 
    866 /* ARGSUSED2 */
    867 static int
    868 puffs_fuse_node_open(struct puffs_usermount *pu, void *opc, int mode,
    869 	const struct puffs_cred *cred)
    870 {
    871 	struct puffs_node	*pn = opc;
    872 	struct refusenode	*rn = pn->pn_data;
    873 	struct fuse_file_info	*fi = &rn->file_info;
    874 	struct fuse		*fuse;
    875 	const char		*path = PNPATH(pn);
    876 
    877 	fuse = puffs_getspecific(pu);
    878 
    879 	set_fuse_context_uid_gid(cred);
    880 
    881 	/* if open, don't open again, lest risk nuking file private info */
    882 	if (rn->flags & RN_OPEN) {
    883 		rn->opencount++;
    884 		return 0;
    885 	}
    886 
    887 	/* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
    888 	fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
    889 
    890 	if (pn->pn_va.va_type == VDIR) {
    891 		if (fuse->op.opendir)
    892 			fuse->op.opendir(path, fi);
    893 	} else {
    894 		if (fuse->op.open)
    895 			fuse->op.open(path, fi);
    896 	}
    897 
    898 	rn->flags |= RN_OPEN;
    899 	rn->opencount++;
    900 
    901 	return 0;
    902 }
    903 
    904 /* ARGSUSED2 */
    905 static int
    906 puffs_fuse_node_close(struct puffs_usermount *pu, void *opc, int fflag,
    907 	const struct puffs_cred *pcr)
    908 {
    909 	struct puffs_node	*pn = opc;
    910 	struct refusenode	*rn = pn->pn_data;
    911 	struct fuse		*fuse;
    912 	struct fuse_file_info	*fi;
    913 	const char		*path = PNPATH(pn);
    914 	int			ret;
    915 
    916 	fuse = puffs_getspecific(pu);
    917 	fi = &rn->file_info;
    918 	ret = 0;
    919 
    920 	set_fuse_context_uid_gid(pcr);
    921 
    922 	if (rn->flags & RN_OPEN) {
    923 		if (pn->pn_va.va_type == VDIR) {
    924 			if (fuse->op.releasedir)
    925 				ret = fuse->op.releasedir(path, fi);
    926 		} else {
    927 			if (fuse->op.release)
    928 				ret = fuse->op.release(path, fi);
    929 		}
    930 	}
    931 	rn->flags &= ~RN_OPEN;
    932 	rn->opencount--;
    933 
    934 	return ret;
    935 }
    936 
    937 /* read some more from the file */
    938 /* ARGSUSED5 */
    939 static int
    940 puffs_fuse_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    941 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    942 	int ioflag)
    943 {
    944 	struct puffs_node	*pn = opc;
    945 	struct refusenode	*rn = pn->pn_data;
    946 	struct fuse		*fuse;
    947 	const char		*path = PNPATH(pn);
    948 	size_t			maxread;
    949 	int			ret;
    950 
    951 	fuse = puffs_getspecific(pu);
    952 	if (fuse->op.read == NULL) {
    953 		return ENOSYS;
    954 	}
    955 
    956 	set_fuse_context_uid_gid(pcr);
    957 
    958 	maxread = *resid;
    959 	if (maxread > (size_t)((off_t)pn->pn_va.va_size - offset)) {
    960 		/*LINTED*/
    961 		maxread = (size_t)((off_t)pn->pn_va.va_size - offset);
    962 	}
    963 	if (maxread == 0)
    964 		return 0;
    965 
    966 	ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
    967 	    &rn->file_info);
    968 
    969 	if (ret > 0) {
    970 		*resid -= (size_t)ret;
    971 		ret = 0;
    972 	}
    973 
    974 	return -ret;
    975 }
    976 
    977 /* write to the file */
    978 /* ARGSUSED0 */
    979 static int
    980 puffs_fuse_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
    981 	off_t offset, size_t *resid, const struct puffs_cred *pcr,
    982 	int ioflag)
    983 {
    984 	struct puffs_node	*pn = opc;
    985 	struct refusenode	*rn = pn->pn_data;
    986 	struct fuse		*fuse;
    987 	const char		*path = PNPATH(pn);
    988 	int			ret;
    989 
    990 	fuse = puffs_getspecific(pu);
    991 	if (fuse->op.write == NULL) {
    992 		return ENOSYS;
    993 	}
    994 
    995 	set_fuse_context_uid_gid(pcr);
    996 
    997 	if (ioflag & PUFFS_IO_APPEND)
    998 		offset = (off_t)pn->pn_va.va_size;
    999 
   1000 	ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
   1001 	    &rn->file_info);
   1002 
   1003 	if (ret >= 0) {
   1004 		if ((uint64_t)(offset + ret) > pn->pn_va.va_size)
   1005 			pn->pn_va.va_size = (u_quad_t)(offset + ret);
   1006 		*resid -= (size_t)ret;
   1007 		ret = (*resid == 0) ? 0 : ENOSPC;
   1008 	} else {
   1009 		ret = -ret;
   1010 	}
   1011 
   1012 	return ret;
   1013 }
   1014 
   1015 
   1016 /* ARGSUSED3 */
   1017 static int
   1018 puffs_fuse_node_readdir(struct puffs_usermount *pu, void *opc,
   1019 	struct dirent *dent, off_t *readoff, size_t *reslen,
   1020 	const struct puffs_cred *pcr, int *eofflag,
   1021 	off_t *cookies, size_t *ncookies)
   1022 {
   1023 	struct puffs_node	*pn = opc;
   1024 	struct refusenode	*rn = pn->pn_data;
   1025 	struct puffs_fuse_dirh	*dirh;
   1026 	struct fuse		*fuse;
   1027 	struct dirent		*fromdent;
   1028 	const char		*path = PNPATH(pn);
   1029 	int			ret;
   1030 
   1031 	fuse = puffs_getspecific(pu);
   1032 	if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
   1033 		return ENOSYS;
   1034 	}
   1035 
   1036 	set_fuse_context_uid_gid(pcr);
   1037 
   1038 	if (pn->pn_va.va_type != VDIR)
   1039 		return ENOTDIR;
   1040 
   1041 	dirh = &rn->dirh;
   1042 
   1043 	/*
   1044 	 * if we are starting from the beginning, slurp entire directory
   1045 	 * into our buffers
   1046 	 */
   1047 	if (*readoff == 0) {
   1048 		/* free old buffers */
   1049 		free(dirh->dbuf);
   1050 		memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
   1051 
   1052 		if (fuse->op.readdir)
   1053 			ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
   1054 			    0, &rn->file_info);
   1055 		else
   1056 			ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
   1057 		if (ret)
   1058 			return -ret;
   1059 	}
   1060 
   1061         /* Both op.readdir and op.getdir read full directory */
   1062         *eofflag = 1;
   1063 
   1064 	/* now, stuff results into the kernel buffers */
   1065 	while (*readoff < (off_t)(dirh->bufsize - dirh->reslen)) {
   1066 		/*LINTED*/
   1067 		fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
   1068 
   1069 		if (*reslen < _DIRENT_SIZE(fromdent))
   1070 			break;
   1071 
   1072 		memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
   1073 		*readoff += (off_t)_DIRENT_SIZE(fromdent);
   1074 		*reslen -= _DIRENT_SIZE(fromdent);
   1075 
   1076 		dent = _DIRENT_NEXT(dent);
   1077 	}
   1078 
   1079 	return 0;
   1080 }
   1081 
   1082 /* ARGSUSED */
   1083 static int
   1084 puffs_fuse_node_reclaim(struct puffs_usermount *pu, void *opc)
   1085 {
   1086 	struct puffs_node	*pn = opc;
   1087 
   1088 	nukern(pn);
   1089 	return 0;
   1090 }
   1091 
   1092 /* ARGSUSED1 */
   1093 static int
   1094 puffs_fuse_fs_unmount(struct puffs_usermount *pu, int flags)
   1095 {
   1096 	struct fuse		*fuse;
   1097 
   1098 	fuse = puffs_getspecific(pu);
   1099 	if (fuse->op.destroy == NULL) {
   1100 		return 0;
   1101 	}
   1102 	(*fuse->op.destroy)(fuse);
   1103         return 0;
   1104 }
   1105 
   1106 /* ARGSUSED0 */
   1107 static int
   1108 puffs_fuse_fs_sync(struct puffs_usermount *pu, int flags,
   1109             const struct puffs_cred *cr)
   1110 {
   1111 	set_fuse_context_uid_gid(cr);
   1112         return 0;
   1113 }
   1114 
   1115 /* ARGSUSED2 */
   1116 static int
   1117 puffs_fuse_fs_statvfs(struct puffs_usermount *pu, struct puffs_statvfs *svfsb)
   1118 {
   1119 	struct fuse		*fuse;
   1120 	int			ret;
   1121 	struct statvfs		sb;
   1122 
   1123 	fuse = puffs_getspecific(pu);
   1124 	if (fuse->op.statfs == NULL) {
   1125 		if ((ret = statvfs(PNPATH(puffs_getroot(pu)), &sb)) == -1) {
   1126 			return errno;
   1127 		}
   1128 	} else {
   1129 		ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), &sb);
   1130 	}
   1131 	statvfs_to_puffs_statvfs(&sb, svfsb);
   1132 
   1133         return -ret;
   1134 }
   1135 
   1136 
   1137 /* End of puffs_fuse operations */
   1138 /* ARGSUSED3 */
   1139 int
   1140 fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
   1141 	size_t size, void *user_data)
   1142 {
   1143 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
   1144 	struct fuse_cmdline_opts opts;
   1145 	struct fuse *fuse;
   1146 	int rv;
   1147 
   1148 	/* parse low-level options */
   1149 	if (fuse_parse_cmdline(&args, &opts) == -1) {
   1150 		return 1;
   1151 	}
   1152 
   1153 	if (opts.show_version) {
   1154 		fuse_lowlevel_version();
   1155 		rv = 0;
   1156 		goto free_args;
   1157 	}
   1158 
   1159 	if (opts.show_help) {
   1160 		switch (opts.show_help) {
   1161 		case REFUSE_SHOW_HELP_FULL:
   1162 			if (args.argv[0] != NULL && args.argv[0][0] != '\0') {
   1163 				/* argv[0] being empty means that the application doesn't
   1164 				 * want us to print the usage string.
   1165 				 */
   1166 				printf("Usage: %s [options] mountpoint\n\n", args.argv[0]);
   1167 			}
   1168 			break;
   1169 		case REFUSE_SHOW_HELP_NO_HEADER:
   1170 			break;
   1171 		}
   1172 		fuse_cmdline_help();
   1173 		rv = 0;
   1174 		goto free_args;
   1175 	}
   1176 
   1177 	if (opts.mountpoint == NULL) {
   1178 		fprintf(stderr, "fuse: no mountpoint specified\n");
   1179 		rv = 1;
   1180 		goto free_args;
   1181 	}
   1182 
   1183 	if (opts.debug) {
   1184 		if (fuse_opt_add_arg(&args, "-odebug") == -1) {
   1185 			rv = 1;
   1186 			goto free_args;
   1187 		}
   1188 	}
   1189 
   1190 	fuse = fuse_new(&args, ops, size, user_data);
   1191 	if (fuse == NULL) {
   1192 		rv = 1;
   1193 		goto free_args;
   1194 	}
   1195 
   1196 	if (!opts.foreground) {
   1197 		if (fuse_daemonize(fuse) == -1) {
   1198 			rv = 1;
   1199 			goto destroy;
   1200 		}
   1201 	}
   1202 
   1203 	if (fuse_mount(fuse, opts.mountpoint) == -1) {
   1204 		rv = 1;
   1205 		goto destroy;
   1206 	}
   1207 
   1208 	rv = fuse_loop(fuse);
   1209 
   1210 	fuse_unmount(fuse);
   1211 destroy:
   1212 	fuse_destroy(fuse);
   1213 free_args:
   1214 	free(opts.mountpoint);
   1215 	fuse_opt_free_args(&args);
   1216 	return rv;
   1217 }
   1218 
   1219 int fuse_mount(struct fuse *fuse, const char *mountpoint)
   1220 {
   1221 	struct puffs_pathobj	*po_root;
   1222 	struct puffs_node	*pn_root;
   1223 	struct refusenode	*rn_root;
   1224 	struct puffs_statvfs	 svfsb;
   1225 
   1226 	pn_root = newrn(fuse->pu);
   1227 	puffs_setroot(fuse->pu, pn_root);
   1228 	rn_root = pn_root->pn_data;
   1229 	rn_root->flags |= RN_ROOT;
   1230 
   1231 	po_root = puffs_getrootpathobj(fuse->pu);
   1232 	if ((po_root->po_path = strdup("/")) == NULL)
   1233 		err(1, "fuse_mount");
   1234 	po_root->po_len = 1;
   1235 	puffs_path_buildhash(fuse->pu, po_root);
   1236 
   1237 	/* sane defaults */
   1238 	puffs_vattr_null(&pn_root->pn_va);
   1239 	pn_root->pn_va.va_type = VDIR;
   1240 	pn_root->pn_va.va_mode = 0755;
   1241 	/* It might be tempting to call op.getattr("/") here to
   1242 	 * populate pn_root->pa_va, but that would mean invoking an
   1243 	 * operation callback without initializing the filesystem. We
   1244 	 * cannot call op.init() either, because that is supposed to
   1245 	 * be called right before entering the main loop. */
   1246 
   1247 	puffs_set_prepost(fuse->pu, set_fuse_context_pid, NULL);
   1248 
   1249 	puffs_zerostatvfs(&svfsb);
   1250 	if (puffs_mount(fuse->pu, mountpoint, MNT_NODEV | MNT_NOSUID, pn_root) == -1) {
   1251 		err(EXIT_FAILURE, "puffs_mount: directory \"%s\"", mountpoint);
   1252 	}
   1253 
   1254 	return 0;
   1255 }
   1256 
   1257 int fuse_daemonize(int foreground)
   1258 {
   1259 	/* There is an impedance mismatch here: FUSE wants to
   1260 	 * daemonize the process without any contexts but puffs wants
   1261 	 * one. */
   1262 	struct fuse *fuse = fuse_get_context()->fuse;
   1263 
   1264 	if (!fuse)
   1265 		/* FUSE would probably allow this, but we cannot. */
   1266 		errx(EXIT_FAILURE,
   1267 		     "%s: librefuse doesn't allow calling"
   1268 		     " this function before fuse_new().", __func__);
   1269 
   1270 	if (!foreground)
   1271 		return puffs_daemon(fuse->pu, 0, 0);
   1272 
   1273 	return 0;
   1274 }
   1275 
   1276 /* ARGSUSED1 */
   1277 struct fuse *
   1278 fuse_new(struct fuse_args *args,
   1279 	const struct fuse_operations *ops, size_t size, void *userdata)
   1280 {
   1281 	struct refuse_config	config;
   1282 	struct puffs_usermount	*pu;
   1283 	struct fuse_context	*fusectx;
   1284 	struct puffs_ops	*pops;
   1285 	struct fuse		*fuse;
   1286 	uint32_t		puffs_flags;
   1287 
   1288 	/* parse refuse options */
   1289 	if (fuse_opt_parse(args, &config, refuse_opts, NULL) == -1)
   1290 		return NULL;
   1291 
   1292 	if ((fuse = calloc(1, sizeof(*fuse))) == NULL) {
   1293 		err(EXIT_FAILURE, "fuse_new");
   1294 	}
   1295 
   1296 	/* copy fuse ops to their own structure */
   1297 	(void) memcpy(&fuse->op, ops, sizeof(fuse->op));
   1298 
   1299 	/* grab the pthread context key */
   1300 	if (!create_context_key()) {
   1301 		free(config.fsname);
   1302 		free(fuse);
   1303 		return NULL;
   1304 	}
   1305 
   1306 	fusectx = fuse_get_context();
   1307 	fusectx->fuse = fuse;
   1308 	fusectx->uid = 0;
   1309 	fusectx->gid = 0;
   1310 	fusectx->pid = 0;
   1311 	fusectx->private_data = userdata;
   1312 
   1313 	/* initialise the puffs operations structure */
   1314         PUFFSOP_INIT(pops);
   1315 
   1316         PUFFSOP_SET(pops, puffs_fuse, fs, sync);
   1317         PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
   1318         PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
   1319 
   1320 	/*
   1321 	 * XXX: all of these don't possibly need to be
   1322 	 * unconditionally set
   1323 	 */
   1324         PUFFSOP_SET(pops, puffs_fuse, node, lookup);
   1325         PUFFSOP_SET(pops, puffs_fuse, node, getattr);
   1326         PUFFSOP_SET(pops, puffs_fuse, node, setattr);
   1327 	PUFFSOP_SET(pops, puffs_fuse, node, pathconf);
   1328         PUFFSOP_SET(pops, puffs_fuse, node, readdir);
   1329         PUFFSOP_SET(pops, puffs_fuse, node, readlink);
   1330         PUFFSOP_SET(pops, puffs_fuse, node, mknod);
   1331         PUFFSOP_SET(pops, puffs_fuse, node, create);
   1332         PUFFSOP_SET(pops, puffs_fuse, node, remove);
   1333         PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
   1334         PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
   1335         PUFFSOP_SET(pops, puffs_fuse, node, symlink);
   1336         PUFFSOP_SET(pops, puffs_fuse, node, rename);
   1337         PUFFSOP_SET(pops, puffs_fuse, node, link);
   1338         PUFFSOP_SET(pops, puffs_fuse, node, open);
   1339         PUFFSOP_SET(pops, puffs_fuse, node, close);
   1340         PUFFSOP_SET(pops, puffs_fuse, node, read);
   1341         PUFFSOP_SET(pops, puffs_fuse, node, write);
   1342         PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
   1343 
   1344 	puffs_flags = PUFFS_FLAG_BUILDPATH
   1345 		| PUFFS_FLAG_HASHPATH
   1346 		| PUFFS_KFLAG_NOCACHE;
   1347 	if (config.debug)
   1348 		puffs_flags |= PUFFS_FLAG_OPDUMP;
   1349 
   1350 	pu = puffs_init(pops, _PATH_PUFFS, config.fsname, fuse, puffs_flags);
   1351 	if (pu == NULL) {
   1352 		err(EXIT_FAILURE, "puffs_init");
   1353 	}
   1354 	fuse->pu = pu;
   1355 
   1356 	free(config.fsname);
   1357 	return fuse;
   1358 }
   1359 
   1360 int
   1361 fuse_loop(struct fuse *fuse)
   1362 {
   1363 	if (fuse->op.init != NULL) {
   1364 		struct fuse_context *fusectx = fuse_get_context();
   1365 
   1366 		/* XXX: prototype incompatible with FUSE: a secondary argument
   1367 		 * of struct fuse_config* needs to be passed.
   1368 		 *
   1369 		 * XXX: Our struct fuse_conn_info is not fully compatible with
   1370 		 * the FUSE one.
   1371 		 */
   1372 		fusectx->private_data = fuse->op.init(NULL);
   1373 	}
   1374 
   1375 	return puffs_mainloop(fuse->pu);
   1376 }
   1377 
   1378 void
   1379 fuse_destroy(struct fuse *fuse)
   1380 {
   1381 
   1382 	/*
   1383 	 * TODO: needs to assert the fs is quiescent, i.e. no other
   1384 	 * threads exist
   1385 	 */
   1386 
   1387 	delete_context_key();
   1388 	/* XXXXXX: missing stuff */
   1389 	free(fuse);
   1390 }
   1391 
   1392 void
   1393 fuse_exit(struct fuse *fuse)
   1394 {
   1395 	/* XXX: puffs_exit() is WRONG */
   1396 	if (fuse->dead == 0)
   1397 		puffs_exit(fuse->pu, 1);
   1398 	fuse->dead = 1;
   1399 }
   1400 
   1401 /*
   1402  * XXX: obviously not the most perfect of functions, but needs some
   1403  * puffs tweaking for a better tomorrow
   1404  */
   1405 /*ARGSUSED*/
   1406 void
   1407 fuse_unmount(struct fuse* fuse)
   1408 {
   1409 	/* XXX: puffs_exit() is WRONG */
   1410 	if (fuse->dead == 0)
   1411 		puffs_exit(fuse->pu, 1);
   1412 	fuse->dead = 1;
   1413 }
   1414 
   1415 /*ARGSUSED*/
   1416 void
   1417 fuse_unmount_compat22(const char *mp)
   1418 {
   1419 
   1420 	return;
   1421 }
   1422 
   1423 void
   1424 fuse_lib_help(struct fuse_args *args __attribute__((__unused__)))
   1425 {
   1426 	fuse_cmdline_help();
   1427 }
   1428 
   1429 int
   1430 fuse_interrupted(void)
   1431 {
   1432 	/* ReFUSE doesn't support request interruption at the
   1433 	 * moment. */
   1434 	return 0;
   1435 }
   1436 
   1437 int
   1438 fuse_invalidate_path(struct fuse *fuse __attribute__((__unused__)),
   1439 		     const char *path __attribute__((__unused__)))
   1440 {
   1441     /* ReFUSE doesn't cache anything at the moment. No need to do
   1442      * anything. */
   1443     return -ENOENT;
   1444 }
   1445 
   1446 int
   1447 fuse_version(void)
   1448 {
   1449 	return _REFUSE_VERSION_;
   1450 }
   1451 
   1452 const char *
   1453 fuse_pkgversion(void)
   1454 {
   1455 	return "ReFUSE " ___STRING(_REFUSE_MAJOR_VERSION_)
   1456 		"." ___STRING(_REFUSE_MINOR_VERSION_);
   1457 }
   1458 
   1459 int
   1460 fuse_getgroups(int size, gid_t list[])
   1461 {
   1462 	/* XXX: In order to implement this, we need to save a pointer
   1463 	 * to struct puffs_cred in struct fuse upon entering a puffs
   1464 	 * callback, and set it back to NULL upon leaving it. Then we
   1465 	 * can use puffs_cred_getgroups(3) here. */
   1466 	return -ENOSYS;
   1467 }
   1468 
   1469 int
   1470 fuse_start_cleanup_thread(struct fuse *fuse)
   1471 {
   1472 	/* XXX: ReFUSE doesn't support -oremember at the moment. */
   1473 	return 0;
   1474 }
   1475 
   1476 void
   1477 fuse_stop_cleanup_thread(struct fuse *fuse) {
   1478 	/* XXX: ReFUSE doesn't support -oremember at the moment. */
   1479 }
   1480 
   1481 int
   1482 fuse_clean_cache(struct fuse *fuse) {
   1483 	/* XXX: ReFUSE doesn't support -oremember at the moment. */
   1484 	return 3600;
   1485 }
   1486 
   1487 /* This is a legacy function that has been removed from the FUSE API,
   1488  * but is defined here because it needs to access refuse_opts. */
   1489 int
   1490 fuse_is_lib_option(const char *opt)
   1491 {
   1492 	return fuse_opt_match(refuse_opts, opt);
   1493 }
   1494