]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/fuse/fuse_ipc.c
Rename fuse(4) to fusefs(4)
[FreeBSD/FreeBSD.git] / sys / fs / fuse / fuse_ipc.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2007-2009 Google Inc. and Amit Singh
5  * 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 are
9  * met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  *   copyright notice, this list of conditions and the following disclaimer
15  *   in the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of Google Inc. nor the names of its
18  *   contributors may be used to endorse or promote products derived from
19  *   this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Copyright (C) 2005 Csaba Henk.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57
58 #include <sys/cdefs.h>
59 __FBSDID("$FreeBSD$");
60
61 #include <sys/types.h>
62 #include <sys/module.h>
63 #include <sys/systm.h>
64 #include <sys/errno.h>
65 #include <sys/param.h>
66 #include <sys/kernel.h>
67 #include <sys/conf.h>
68 #include <sys/uio.h>
69 #include <sys/malloc.h>
70 #include <sys/queue.h>
71 #include <sys/lock.h>
72 #include <sys/sx.h>
73 #include <sys/mutex.h>
74 #include <sys/proc.h>
75 #include <sys/mount.h>
76 #include <sys/vnode.h>
77 #include <sys/signalvar.h>
78 #include <sys/syscallsubr.h>
79 #include <sys/sysctl.h>
80 #include <vm/uma.h>
81
82 #include "fuse.h"
83 #include "fuse_node.h"
84 #include "fuse_ipc.h"
85 #include "fuse_internal.h"
86
87 #define FUSE_DEBUG_MODULE IPC
88 #include "fuse_debug.h"
89
90 static struct fuse_ticket *fticket_alloc(struct fuse_data *data);
91 static void fticket_refresh(struct fuse_ticket *ftick);
92 static void fticket_destroy(struct fuse_ticket *ftick);
93 static int fticket_wait_answer(struct fuse_ticket *ftick);
94 static inline int 
95 fticket_aw_pull_uio(struct fuse_ticket *ftick,
96     struct uio *uio);
97
98 static int fuse_body_audit(struct fuse_ticket *ftick, size_t blen);
99
100 static fuse_handler_t fuse_standard_handler;
101
102 SYSCTL_NODE(_vfs, OID_AUTO, fusefs, CTLFLAG_RW, 0, "FUSE tunables");
103 SYSCTL_STRING(_vfs_fusefs, OID_AUTO, version, CTLFLAG_RD,
104     FUSE_FREEBSD_VERSION, 0, "fuse-freebsd version");
105 static int fuse_ticket_count = 0;
106
107 SYSCTL_INT(_vfs_fusefs, OID_AUTO, ticket_count, CTLFLAG_RW,
108     &fuse_ticket_count, 0, "number of allocated tickets");
109 static long fuse_iov_permanent_bufsize = 1 << 19;
110
111 SYSCTL_LONG(_vfs_fusefs, OID_AUTO, iov_permanent_bufsize, CTLFLAG_RW,
112     &fuse_iov_permanent_bufsize, 0,
113     "limit for permanently stored buffer size for fuse_iovs");
114 static int fuse_iov_credit = 16;
115
116 SYSCTL_INT(_vfs_fusefs, OID_AUTO, iov_credit, CTLFLAG_RW,
117     &fuse_iov_credit, 0,
118     "how many times is an oversized fuse_iov tolerated");
119
120 MALLOC_DEFINE(M_FUSEMSG, "fuse_msgbuf", "fuse message buffer");
121 static uma_zone_t ticket_zone;
122
123 static void
124 fuse_block_sigs(sigset_t *oldset)
125 {
126         sigset_t newset;
127
128         SIGFILLSET(newset);
129         SIGDELSET(newset, SIGKILL);
130         if (kern_sigprocmask(curthread, SIG_BLOCK, &newset, oldset, 0))
131                 panic("%s: Invalid operation for kern_sigprocmask()",
132                     __func__);
133 }
134
135 static void
136 fuse_restore_sigs(sigset_t *oldset)
137 {
138
139         if (kern_sigprocmask(curthread, SIG_SETMASK, oldset, NULL, 0))
140                 panic("%s: Invalid operation for kern_sigprocmask()",
141                     __func__);
142 }
143
144 void
145 fiov_init(struct fuse_iov *fiov, size_t size)
146 {
147         uint32_t msize = FU_AT_LEAST(size);
148
149         debug_printf("fiov=%p, size=%zd\n", fiov, size);
150
151         fiov->len = 0;
152
153         fiov->base = malloc(msize, M_FUSEMSG, M_WAITOK | M_ZERO);
154
155         fiov->allocated_size = msize;
156         fiov->credit = fuse_iov_credit;
157 }
158
159 void
160 fiov_teardown(struct fuse_iov *fiov)
161 {
162         debug_printf("fiov=%p\n", fiov);
163
164         MPASS(fiov->base != NULL);
165         free(fiov->base, M_FUSEMSG);
166 }
167
168 void
169 fiov_adjust(struct fuse_iov *fiov, size_t size)
170 {
171         debug_printf("fiov=%p, size=%zd\n", fiov, size);
172
173         if (fiov->allocated_size < size ||
174             (fuse_iov_permanent_bufsize >= 0 &&
175             fiov->allocated_size - size > fuse_iov_permanent_bufsize &&
176             --fiov->credit < 0)) {
177
178                 fiov->base = realloc(fiov->base, FU_AT_LEAST(size), M_FUSEMSG,
179                     M_WAITOK | M_ZERO);
180                 if (!fiov->base) {
181                         panic("FUSE: realloc failed");
182                 }
183                 fiov->allocated_size = FU_AT_LEAST(size);
184                 fiov->credit = fuse_iov_credit;
185         }
186         fiov->len = size;
187 }
188
189 void
190 fiov_refresh(struct fuse_iov *fiov)
191 {
192         debug_printf("fiov=%p\n", fiov);
193
194         bzero(fiov->base, fiov->len);
195         fiov_adjust(fiov, 0);
196 }
197
198 static int
199 fticket_ctor(void *mem, int size, void *arg, int flags)
200 {
201         struct fuse_ticket *ftick = mem;
202         struct fuse_data *data = arg;
203
204         debug_printf("ftick=%p data=%p\n", ftick, data);
205
206         FUSE_ASSERT_MS_DONE(ftick);
207         FUSE_ASSERT_AW_DONE(ftick);
208
209         ftick->tk_data = data;
210
211         if (ftick->tk_unique != 0)
212                 fticket_refresh(ftick);
213
214         /* May be truncated to 32 bits */
215         ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1);
216         if (ftick->tk_unique == 0)
217                 ftick->tk_unique = atomic_fetchadd_long(&data->ticketer, 1);
218
219         refcount_init(&ftick->tk_refcount, 1);
220         atomic_add_acq_int(&fuse_ticket_count, 1);
221
222         return 0;
223 }
224
225 static void
226 fticket_dtor(void *mem, int size, void *arg)
227 {
228         struct fuse_ticket *ftick = mem;
229
230         debug_printf("ftick=%p\n", ftick);
231
232         FUSE_ASSERT_MS_DONE(ftick);
233         FUSE_ASSERT_AW_DONE(ftick);
234
235         atomic_subtract_acq_int(&fuse_ticket_count, 1);
236 }
237
238 static int
239 fticket_init(void *mem, int size, int flags)
240 {
241         struct fuse_ticket *ftick = mem;
242
243         FS_DEBUG("ftick=%p\n", ftick);
244
245         bzero(ftick, sizeof(struct fuse_ticket));
246
247         fiov_init(&ftick->tk_ms_fiov, sizeof(struct fuse_in_header));
248         ftick->tk_ms_type = FT_M_FIOV;
249
250         mtx_init(&ftick->tk_aw_mtx, "fuse answer delivery mutex", NULL, MTX_DEF);
251         fiov_init(&ftick->tk_aw_fiov, 0);
252         ftick->tk_aw_type = FT_A_FIOV;
253
254         return 0;
255 }
256
257 static void
258 fticket_fini(void *mem, int size)
259 {
260         struct fuse_ticket *ftick = mem;
261
262         FS_DEBUG("ftick=%p\n", ftick);
263
264         fiov_teardown(&ftick->tk_ms_fiov);
265         fiov_teardown(&ftick->tk_aw_fiov);
266         mtx_destroy(&ftick->tk_aw_mtx);
267 }
268
269 static inline struct fuse_ticket *
270 fticket_alloc(struct fuse_data *data)
271 {
272         return uma_zalloc_arg(ticket_zone, data, M_WAITOK);
273 }
274
275 static inline void
276 fticket_destroy(struct fuse_ticket *ftick)
277 {
278         return uma_zfree(ticket_zone, ftick);
279 }
280
281 static  inline
282 void
283 fticket_refresh(struct fuse_ticket *ftick)
284 {
285         debug_printf("ftick=%p\n", ftick);
286
287         FUSE_ASSERT_MS_DONE(ftick);
288         FUSE_ASSERT_AW_DONE(ftick);
289
290         fiov_refresh(&ftick->tk_ms_fiov);
291         ftick->tk_ms_bufdata = NULL;
292         ftick->tk_ms_bufsize = 0;
293         ftick->tk_ms_type = FT_M_FIOV;
294
295         bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header));
296
297         fiov_refresh(&ftick->tk_aw_fiov);
298         ftick->tk_aw_errno = 0;
299         ftick->tk_aw_bufdata = NULL;
300         ftick->tk_aw_bufsize = 0;
301         ftick->tk_aw_type = FT_A_FIOV;
302
303         ftick->tk_flag = 0;
304 }
305
306 static int
307 fticket_wait_answer(struct fuse_ticket *ftick)
308 {
309         sigset_t tset;
310         int err = 0;
311         struct fuse_data *data;
312
313         debug_printf("ftick=%p\n", ftick);
314         fuse_lck_mtx_lock(ftick->tk_aw_mtx);
315
316         if (fticket_answered(ftick)) {
317                 goto out;
318         }
319         data = ftick->tk_data;
320
321         if (fdata_get_dead(data)) {
322                 err = ENOTCONN;
323                 fticket_set_answered(ftick);
324                 goto out;
325         }
326         fuse_block_sigs(&tset);
327         err = msleep(ftick, &ftick->tk_aw_mtx, PCATCH, "fu_ans",
328             data->daemon_timeout * hz);
329         fuse_restore_sigs(&tset);
330         if (err == EAGAIN) {            /* same as EWOULDBLOCK */
331 #ifdef XXXIP                            /* die conditionally */
332                 if (!fdata_get_dead(data)) {
333                         fdata_set_dead(data);
334                 }
335 #endif
336                 err = ETIMEDOUT;
337                 fticket_set_answered(ftick);
338         }
339 out:
340         if (!(err || fticket_answered(ftick))) {
341                 debug_printf("FUSE: requester was woken up but still no answer");
342                 err = ENXIO;
343         }
344         fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
345
346         return err;
347 }
348
349 static  inline
350 int
351 fticket_aw_pull_uio(struct fuse_ticket *ftick, struct uio *uio)
352 {
353         int err = 0;
354         size_t len = uio_resid(uio);
355
356         debug_printf("ftick=%p, uio=%p\n", ftick, uio);
357
358         if (len) {
359                 switch (ftick->tk_aw_type) {
360                 case FT_A_FIOV:
361                         fiov_adjust(fticket_resp(ftick), len);
362                         err = uiomove(fticket_resp(ftick)->base, len, uio);
363                         if (err) {
364                                 debug_printf("FUSE: FT_A_FIOV: error is %d"
365                                              " (%p, %zd, %p)\n",
366                                              err, fticket_resp(ftick)->base, 
367                                              len, uio);
368                         }
369                         break;
370
371                 case FT_A_BUF:
372                         ftick->tk_aw_bufsize = len;
373                         err = uiomove(ftick->tk_aw_bufdata, len, uio);
374                         if (err) {
375                                 debug_printf("FUSE: FT_A_BUF: error is %d"
376                                              " (%p, %zd, %p)\n",
377                                              err, ftick->tk_aw_bufdata, len, uio);
378                         }
379                         break;
380
381                 default:
382                         panic("FUSE: unknown answer type for ticket %p", ftick);
383                 }
384         }
385         return err;
386 }
387
388 int
389 fticket_pull(struct fuse_ticket *ftick, struct uio *uio)
390 {
391         int err = 0;
392
393         debug_printf("ftick=%p, uio=%p\n", ftick, uio);
394
395         if (ftick->tk_aw_ohead.error) {
396                 return 0;
397         }
398         err = fuse_body_audit(ftick, uio_resid(uio));
399         if (!err) {
400                 err = fticket_aw_pull_uio(ftick, uio);
401         }
402         return err;
403 }
404
405 struct fuse_data *
406 fdata_alloc(struct cdev *fdev, struct ucred *cred)
407 {
408         struct fuse_data *data;
409
410         debug_printf("fdev=%p\n", fdev);
411
412         data = malloc(sizeof(struct fuse_data), M_FUSEMSG, M_WAITOK | M_ZERO);
413
414         data->fdev = fdev;
415         mtx_init(&data->ms_mtx, "fuse message list mutex", NULL, MTX_DEF);
416         STAILQ_INIT(&data->ms_head);
417         mtx_init(&data->aw_mtx, "fuse answer list mutex", NULL, MTX_DEF);
418         TAILQ_INIT(&data->aw_head);
419         data->daemoncred = crhold(cred);
420         data->daemon_timeout = FUSE_DEFAULT_DAEMON_TIMEOUT;
421         sx_init(&data->rename_lock, "fuse rename lock");
422         data->ref = 1;
423
424         return data;
425 }
426
427 void
428 fdata_trydestroy(struct fuse_data *data)
429 {
430         FS_DEBUG("data=%p data.mp=%p data.fdev=%p data.flags=%04x\n",
431             data, data->mp, data->fdev, data->dataflags);
432
433         FS_DEBUG("destroy: data=%p\n", data);
434         data->ref--;
435         MPASS(data->ref >= 0);
436         if (data->ref != 0)
437                 return;
438
439         /* Driving off stage all that stuff thrown at device... */
440         mtx_destroy(&data->ms_mtx);
441         mtx_destroy(&data->aw_mtx);
442         sx_destroy(&data->rename_lock);
443
444         crfree(data->daemoncred);
445
446         free(data, M_FUSEMSG);
447 }
448
449 void
450 fdata_set_dead(struct fuse_data *data)
451 {
452         debug_printf("data=%p\n", data);
453
454         FUSE_LOCK();
455         if (fdata_get_dead(data)) {
456                 FUSE_UNLOCK();
457                 return;
458         }
459         fuse_lck_mtx_lock(data->ms_mtx);
460         data->dataflags |= FSESS_DEAD;
461         wakeup_one(data);
462         selwakeuppri(&data->ks_rsel, PZERO + 1);
463         wakeup(&data->ticketer);
464         fuse_lck_mtx_unlock(data->ms_mtx);
465         FUSE_UNLOCK();
466 }
467
468 struct fuse_ticket *
469 fuse_ticket_fetch(struct fuse_data *data)
470 {
471         int err = 0;
472         struct fuse_ticket *ftick;
473
474         debug_printf("data=%p\n", data);
475
476         ftick = fticket_alloc(data);
477
478         if (!(data->dataflags & FSESS_INITED)) {
479                 /* Sleep until get answer for INIT messsage */
480                 FUSE_LOCK();
481                 if (!(data->dataflags & FSESS_INITED) && data->ticketer > 2) {
482                         err = msleep(&data->ticketer, &fuse_mtx, PCATCH | PDROP,
483                             "fu_ini", 0);
484                         if (err)
485                                 fdata_set_dead(data);
486                 } else
487                         FUSE_UNLOCK();
488         }
489         return ftick;
490 }
491
492 int
493 fuse_ticket_drop(struct fuse_ticket *ftick)
494 {
495         int die;
496
497         die = refcount_release(&ftick->tk_refcount);
498         debug_printf("ftick=%p refcount=%d\n", ftick, ftick->tk_refcount);
499         if (die)
500                 fticket_destroy(ftick);
501
502         return die;
503 }
504
505 void
506 fuse_insert_callback(struct fuse_ticket *ftick, fuse_handler_t * handler)
507 {
508         debug_printf("ftick=%p, handler=%p data=%p\n", ftick, ftick->tk_data, 
509                      handler);
510
511         if (fdata_get_dead(ftick->tk_data)) {
512                 return;
513         }
514         ftick->tk_aw_handler = handler;
515
516         fuse_lck_mtx_lock(ftick->tk_data->aw_mtx);
517         fuse_aw_push(ftick);
518         fuse_lck_mtx_unlock(ftick->tk_data->aw_mtx);
519 }
520
521 void
522 fuse_insert_message(struct fuse_ticket *ftick)
523 {
524         debug_printf("ftick=%p\n", ftick);
525
526         if (ftick->tk_flag & FT_DIRTY) {
527                 panic("FUSE: ticket reused without being refreshed");
528         }
529         ftick->tk_flag |= FT_DIRTY;
530
531         if (fdata_get_dead(ftick->tk_data)) {
532                 return;
533         }
534         fuse_lck_mtx_lock(ftick->tk_data->ms_mtx);
535         fuse_ms_push(ftick);
536         wakeup_one(ftick->tk_data);
537         selwakeuppri(&ftick->tk_data->ks_rsel, PZERO + 1);
538         fuse_lck_mtx_unlock(ftick->tk_data->ms_mtx);
539 }
540
541 static int
542 fuse_body_audit(struct fuse_ticket *ftick, size_t blen)
543 {
544         int err = 0;
545         enum fuse_opcode opcode;
546
547         debug_printf("ftick=%p, blen = %zu\n", ftick, blen);
548
549         opcode = fticket_opcode(ftick);
550
551         switch (opcode) {
552         case FUSE_LOOKUP:
553                 err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
554                 break;
555
556         case FUSE_FORGET:
557                 panic("FUSE: a handler has been intalled for FUSE_FORGET");
558                 break;
559
560         case FUSE_GETATTR:
561                 err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL;
562                 break;
563
564         case FUSE_SETATTR:
565                 err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL;
566                 break;
567
568         case FUSE_READLINK:
569                 err = (PAGE_SIZE >= blen) ? 0 : EINVAL;
570                 break;
571
572         case FUSE_SYMLINK:
573                 err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
574                 break;
575
576         case FUSE_MKNOD:
577                 err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
578                 break;
579
580         case FUSE_MKDIR:
581                 err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
582                 break;
583
584         case FUSE_UNLINK:
585                 err = (blen == 0) ? 0 : EINVAL;
586                 break;
587
588         case FUSE_RMDIR:
589                 err = (blen == 0) ? 0 : EINVAL;
590                 break;
591
592         case FUSE_RENAME:
593                 err = (blen == 0) ? 0 : EINVAL;
594                 break;
595
596         case FUSE_LINK:
597                 err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
598                 break;
599
600         case FUSE_OPEN:
601                 err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL;
602                 break;
603
604         case FUSE_READ:
605                 err = (((struct fuse_read_in *)(
606                     (char *)ftick->tk_ms_fiov.base +
607                     sizeof(struct fuse_in_header)
608                     ))->size >= blen) ? 0 : EINVAL;
609                 break;
610
611         case FUSE_WRITE:
612                 err = (blen == sizeof(struct fuse_write_out)) ? 0 : EINVAL;
613                 break;
614
615         case FUSE_STATFS:
616                 if (fuse_libabi_geq(ftick->tk_data, 7, 4)) {
617                         err = (blen == sizeof(struct fuse_statfs_out)) ? 
618                           0 : EINVAL;
619                 } else {
620                         err = (blen == FUSE_COMPAT_STATFS_SIZE) ? 0 : EINVAL;
621                 }
622                 break;
623
624         case FUSE_RELEASE:
625                 err = (blen == 0) ? 0 : EINVAL;
626                 break;
627
628         case FUSE_FSYNC:
629                 err = (blen == 0) ? 0 : EINVAL;
630                 break;
631
632         case FUSE_SETXATTR:
633                 err = (blen == 0) ? 0 : EINVAL;
634                 break;
635
636         case FUSE_GETXATTR:
637         case FUSE_LISTXATTR:
638                 /*
639                  * These can have varying response lengths, and 0 length
640                  * isn't necessarily invalid.
641                  */
642                 err = 0;
643                 break;
644
645         case FUSE_REMOVEXATTR:
646                 err = (blen == 0) ? 0 : EINVAL;
647                 break;
648
649         case FUSE_FLUSH:
650                 err = (blen == 0) ? 0 : EINVAL;
651                 break;
652
653         case FUSE_INIT:
654                 if (blen == sizeof(struct fuse_init_out) || blen == 8) {
655                         err = 0;
656                 } else {
657                         err = EINVAL;
658                 }
659                 break;
660
661         case FUSE_OPENDIR:
662                 err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL;
663                 break;
664
665         case FUSE_READDIR:
666                 err = (((struct fuse_read_in *)(
667                     (char *)ftick->tk_ms_fiov.base +
668                     sizeof(struct fuse_in_header)
669                     ))->size >= blen) ? 0 : EINVAL;
670                 break;
671
672         case FUSE_RELEASEDIR:
673                 err = (blen == 0) ? 0 : EINVAL;
674                 break;
675
676         case FUSE_FSYNCDIR:
677                 err = (blen == 0) ? 0 : EINVAL;
678                 break;
679
680         case FUSE_GETLK:
681                 panic("FUSE: no response body format check for FUSE_GETLK");
682                 break;
683
684         case FUSE_SETLK:
685                 panic("FUSE: no response body format check for FUSE_SETLK");
686                 break;
687
688         case FUSE_SETLKW:
689                 panic("FUSE: no response body format check for FUSE_SETLKW");
690                 break;
691
692         case FUSE_ACCESS:
693                 err = (blen == 0) ? 0 : EINVAL;
694                 break;
695
696         case FUSE_CREATE:
697                 err = (blen == sizeof(struct fuse_entry_out) +
698                     sizeof(struct fuse_open_out)) ? 0 : EINVAL;
699                 break;
700
701         case FUSE_DESTROY:
702                 err = (blen == 0) ? 0 : EINVAL;
703                 break;
704
705         default:
706                 panic("FUSE: opcodes out of sync (%d)\n", opcode);
707         }
708
709         return err;
710 }
711
712 static inline void
713 fuse_setup_ihead(struct fuse_in_header *ihead, struct fuse_ticket *ftick,
714     uint64_t nid, enum fuse_opcode op, size_t blen, pid_t pid,
715     struct ucred *cred)
716 {
717         ihead->len = sizeof(*ihead) + blen;
718         ihead->unique = ftick->tk_unique;
719         ihead->nodeid = nid;
720         ihead->opcode = op;
721
722         debug_printf("ihead=%p, ftick=%p, nid=%ju, op=%d, blen=%zu\n",
723             ihead, ftick, (uintmax_t)nid, op, blen);
724
725         ihead->pid = pid;
726         ihead->uid = cred->cr_uid;
727         ihead->gid = cred->cr_rgid;
728 }
729
730 /*
731  * fuse_standard_handler just pulls indata and wakes up pretender.
732  * Doesn't try to interpret data, that's left for the pretender.
733  * Though might do a basic size verification before the pull-in takes place
734  */
735
736 static int
737 fuse_standard_handler(struct fuse_ticket *ftick, struct uio *uio)
738 {
739         int err = 0;
740
741         debug_printf("ftick=%p, uio=%p\n", ftick, uio);
742
743         err = fticket_pull(ftick, uio);
744
745         fuse_lck_mtx_lock(ftick->tk_aw_mtx);
746
747         if (!fticket_answered(ftick)) {
748                 fticket_set_answered(ftick);
749                 ftick->tk_aw_errno = err;
750                 wakeup(ftick);
751         }
752         fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
753
754         return err;
755 }
756
757 void
758 fdisp_make_pid(struct fuse_dispatcher *fdip, enum fuse_opcode op,
759     struct mount *mp, uint64_t nid, pid_t pid, struct ucred *cred)
760 {
761         struct fuse_data *data = fuse_get_mpdata(mp);
762
763         debug_printf("fdip=%p, op=%d, mp=%p, nid=%ju\n",
764             fdip, op, mp, (uintmax_t)nid);
765
766         if (fdip->tick) {
767                 fticket_refresh(fdip->tick);
768         } else {
769                 fdip->tick = fuse_ticket_fetch(data);
770         }
771
772         FUSE_DIMALLOC(&fdip->tick->tk_ms_fiov, fdip->finh,
773             fdip->indata, fdip->iosize);
774
775         fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, fdip->iosize, pid, cred);
776 }
777
778 void
779 fdisp_make(struct fuse_dispatcher *fdip, enum fuse_opcode op, struct mount *mp,
780     uint64_t nid, struct thread *td, struct ucred *cred)
781 {
782         RECTIFY_TDCR(td, cred);
783
784         return fdisp_make_pid(fdip, op, mp, nid, td->td_proc->p_pid, cred);
785 }
786
787 void
788 fdisp_make_vp(struct fuse_dispatcher *fdip, enum fuse_opcode op,
789     struct vnode *vp, struct thread *td, struct ucred *cred)
790 {
791         debug_printf("fdip=%p, op=%d, vp=%p\n", fdip, op, vp);
792         RECTIFY_TDCR(td, cred);
793         return fdisp_make_pid(fdip, op, vnode_mount(vp), VTOI(vp),
794             td->td_proc->p_pid, cred);
795 }
796
797 int
798 fdisp_wait_answ(struct fuse_dispatcher *fdip)
799 {
800         int err = 0;
801
802         fdip->answ_stat = 0;
803         fuse_insert_callback(fdip->tick, fuse_standard_handler);
804         fuse_insert_message(fdip->tick);
805
806         if ((err = fticket_wait_answer(fdip->tick))) {
807                 debug_printf("IPC: interrupted, err = %d\n", err);
808
809                 fuse_lck_mtx_lock(fdip->tick->tk_aw_mtx);
810
811                 if (fticket_answered(fdip->tick)) {
812                         /*
813                          * Just between noticing the interrupt and getting here,
814                          * the standard handler has completed his job.
815                          * So we drop the ticket and exit as usual.
816                          */
817                         debug_printf("IPC: already answered\n");
818                         fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx);
819                         goto out;
820                 } else {
821                         /*
822                          * So we were faster than the standard handler.
823                          * Then by setting the answered flag we get *him*
824                          * to drop the ticket.
825                          */
826                         debug_printf("IPC: setting to answered\n");
827                         fticket_set_answered(fdip->tick);
828                         fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx);
829                         return err;
830                 }
831         }
832         debug_printf("IPC: not interrupted, err = %d\n", err);
833
834         if (fdip->tick->tk_aw_errno) {
835                 debug_printf("IPC: explicit EIO-ing, tk_aw_errno = %d\n",
836                     fdip->tick->tk_aw_errno);
837                 err = EIO;
838                 goto out;
839         }
840         if ((err = fdip->tick->tk_aw_ohead.error)) {
841                 debug_printf("IPC: setting status to %d\n",
842                     fdip->tick->tk_aw_ohead.error);
843                 /*
844                  * This means a "proper" fuse syscall error.
845                  * We record this value so the caller will
846                  * be able to know it's not a boring messaging
847                  * failure, if she wishes so (and if not, she can
848                  * just simply propagate the return value of this routine).
849                  * [XXX Maybe a bitflag would do the job too,
850                  * if other flags needed, this will be converted thusly.]
851                  */
852                 fdip->answ_stat = err;
853                 goto out;
854         }
855         fdip->answ = fticket_resp(fdip->tick)->base;
856         fdip->iosize = fticket_resp(fdip->tick)->len;
857
858         debug_printf("IPC: all is well\n");
859
860         return 0;
861
862 out:
863         debug_printf("IPC: dropping ticket, err = %d\n", err);
864
865         return err;
866 }
867
868 void
869 fuse_ipc_init(void)
870 {
871         ticket_zone = uma_zcreate("fuse_ticket", sizeof(struct fuse_ticket),
872             fticket_ctor, fticket_dtor, fticket_init, fticket_fini,
873             UMA_ALIGN_PTR, 0);
874 }
875
876 void
877 fuse_ipc_destroy(void)
878 {
879         uma_zdestroy(ticket_zone);
880 }