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