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