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