]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/fuse/fuse_internal.c
fusefs: cache negative lookups
[FreeBSD/FreeBSD.git] / sys / fs / fuse / fuse_internal.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/param.h>
62 #include <sys/module.h>
63 #include <sys/systm.h>
64 #include <sys/errno.h>
65 #include <sys/kernel.h>
66 #include <sys/conf.h>
67 #include <sys/uio.h>
68 #include <sys/malloc.h>
69 #include <sys/queue.h>
70 #include <sys/lock.h>
71 #include <sys/mutex.h>
72 #include <sys/sdt.h>
73 #include <sys/sx.h>
74 #include <sys/proc.h>
75 #include <sys/mount.h>
76 #include <sys/vnode.h>
77 #include <sys/namei.h>
78 #include <sys/stat.h>
79 #include <sys/unistd.h>
80 #include <sys/filedesc.h>
81 #include <sys/file.h>
82 #include <sys/fcntl.h>
83 #include <sys/dirent.h>
84 #include <sys/bio.h>
85 #include <sys/buf.h>
86 #include <sys/sysctl.h>
87 #include <sys/priv.h>
88
89 #include "fuse.h"
90 #include "fuse_file.h"
91 #include "fuse_internal.h"
92 #include "fuse_ipc.h"
93 #include "fuse_node.h"
94 #include "fuse_file.h"
95
96 SDT_PROVIDER_DECLARE(fuse);
97 /* 
98  * Fuse trace probe:
99  * arg0: verbosity.  Higher numbers give more verbose messages
100  * arg1: Textual message
101  */
102 SDT_PROBE_DEFINE2(fuse, , internal, trace, "int", "char*");
103
104 #ifdef ZERO_PAD_INCOMPLETE_BUFS
105 static int isbzero(void *buf, size_t len);
106
107 #endif
108
109 /* Synchronously send a FUSE_ACCESS operation */
110 int
111 fuse_internal_access(struct vnode *vp,
112     mode_t mode,
113     struct fuse_access_param *facp,
114     struct thread *td,
115     struct ucred *cred)
116 {
117         int err = 0;
118         uint32_t mask = F_OK;
119         int dataflags;
120         int vtype;
121         struct mount *mp;
122         struct fuse_dispatcher fdi;
123         struct fuse_access_in *fai;
124         struct fuse_data *data;
125
126         mp = vnode_mount(vp);
127         vtype = vnode_vtype(vp);
128
129         data = fuse_get_mpdata(mp);
130         dataflags = data->dataflags;
131
132         if ((mode & VWRITE) && vfs_isrdonly(mp)) {
133                 return EROFS;
134         }
135
136         /* Unless explicitly permitted, deny everyone except the fs owner. */
137         if (!(facp->facc_flags)) {
138                 if (!(dataflags & FSESS_DAEMON_CAN_SPY)) {
139                         int denied = fuse_match_cred(data->daemoncred, cred);
140
141                         if (denied)
142                                 return EPERM;
143                 }
144         }
145
146         if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
147                 /* TODO: Implement me!  Bug 216391 */
148                 return 0;
149         }
150
151         if (!fsess_isimpl(mp, FUSE_ACCESS))
152                 return 0;
153
154         if ((mode & (VWRITE | VAPPEND | VADMIN)) != 0)
155                 mask |= W_OK;
156         if ((mode & VREAD) != 0)
157                 mask |= R_OK;
158         if ((mode & VEXEC) != 0)
159                 mask |= X_OK;
160
161         fdisp_init(&fdi, sizeof(*fai));
162         fdisp_make_vp(&fdi, FUSE_ACCESS, vp, td, cred);
163
164         fai = fdi.indata;
165         fai->mask = mask;
166
167         err = fdisp_wait_answ(&fdi);
168         fdisp_destroy(&fdi);
169
170         if (err == ENOSYS) {
171                 fsess_set_notimpl(mp, FUSE_ACCESS);
172                 err = 0;
173         }
174         return err;
175 }
176
177 /*
178  * Cache FUSE attributes from attr, in attribute cache associated with vnode
179  * 'vp'.  Optionally, if argument 'vap' is not NULL, store a copy of the
180  * converted attributes there as well.
181  *
182  * If the nominal attribute cache TTL is zero, do not cache on the 'vp' (but do
183  * return the result to the caller).
184  */
185 void
186 fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
187         uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap)
188 {
189         struct mount *mp;
190         struct fuse_vnode_data *fvdat;
191         struct vattr *vp_cache_at;
192
193         mp = vnode_mount(vp);
194         fvdat = VTOFUD(vp);
195
196         fuse_validity_2_bintime(attr_valid, attr_valid_nsec,
197                 &fvdat->attr_cache_timeout);
198
199         vp_cache_at = VTOVA(vp);
200
201         if (vap == NULL && vp_cache_at == NULL)
202                 return;
203
204         if (vap == NULL)
205                 vap = vp_cache_at;
206
207         vattr_null(vap);
208
209         vap->va_fsid = mp->mnt_stat.f_fsid.val[0];
210         vap->va_fileid = attr->ino;
211         vap->va_mode = attr->mode & ~S_IFMT;
212         vap->va_nlink     = attr->nlink;
213         vap->va_uid       = attr->uid;
214         vap->va_gid       = attr->gid;
215         vap->va_rdev      = attr->rdev;
216         vap->va_size      = attr->size;
217         /* XXX on i386, seconds are truncated to 32 bits */
218         vap->va_atime.tv_sec  = attr->atime;
219         vap->va_atime.tv_nsec = attr->atimensec;
220         vap->va_mtime.tv_sec  = attr->mtime;
221         vap->va_mtime.tv_nsec = attr->mtimensec;
222         vap->va_ctime.tv_sec  = attr->ctime;
223         vap->va_ctime.tv_nsec = attr->ctimensec;
224         vap->va_blocksize = PAGE_SIZE;
225         vap->va_type = IFTOVT(attr->mode);
226         vap->va_bytes = attr->blocks * S_BLKSIZE;
227         vap->va_flags = 0;
228
229         if (vap != vp_cache_at && vp_cache_at != NULL)
230                 memcpy(vp_cache_at, vap, sizeof(*vap));
231 }
232
233
234 /* fsync */
235
236 int
237 fuse_internal_fsync_callback(struct fuse_ticket *tick, struct uio *uio)
238 {
239         if (tick->tk_aw_ohead.error == ENOSYS) {
240                 fsess_set_notimpl(tick->tk_data->mp, fticket_opcode(tick));
241         }
242         return 0;
243 }
244
245 int
246 fuse_internal_fsync(struct vnode *vp,
247     struct thread *td,
248     int waitfor,
249     bool datasync)
250 {
251         struct fuse_fsync_in *ffsi = NULL;
252         struct fuse_dispatcher fdi;
253         struct fuse_filehandle *fufh;
254         struct fuse_vnode_data *fvdat = VTOFUD(vp);
255         int op = FUSE_FSYNC;
256         int err = 0;
257
258         if (!fsess_isimpl(vnode_mount(vp),
259             (vnode_vtype(vp) == VDIR ? FUSE_FSYNCDIR : FUSE_FSYNC))) {
260                 return 0;
261         }
262         if (vnode_isdir(vp))
263                 op = FUSE_FSYNCDIR;
264
265         fdisp_init(&fdi, sizeof(*ffsi));
266         /*
267          * fsync every open file handle for this file, because we can't be sure
268          * which file handle the caller is really referring to.
269          */
270         LIST_FOREACH(fufh, &fvdat->handles, next) {
271                 if (ffsi == NULL)
272                         fdisp_make_vp(&fdi, op, vp, td, NULL);
273                 else
274                         fdisp_refresh_vp(&fdi, op, vp, td, NULL);
275                 ffsi = fdi.indata;
276                 ffsi->fh = fufh->fh_id;
277                 ffsi->fsync_flags = 0;
278
279                 if (datasync)
280                         ffsi->fsync_flags = 1;
281
282                 if (waitfor == MNT_WAIT) {
283                         err = fdisp_wait_answ(&fdi);
284                 } else {
285                         fuse_insert_callback(fdi.tick,
286                                 fuse_internal_fsync_callback);
287                         fuse_insert_message(fdi.tick);
288                 }
289         }
290         fdisp_destroy(&fdi);
291
292         return err;
293 }
294
295 /* readdir */
296
297 int
298 fuse_internal_readdir(struct vnode *vp,
299     struct uio *uio,
300     struct fuse_filehandle *fufh,
301     struct fuse_iov *cookediov)
302 {
303         int err = 0;
304         struct fuse_dispatcher fdi;
305         struct fuse_read_in *fri = NULL;
306
307         if (uio_resid(uio) == 0) {
308                 return 0;
309         }
310         fdisp_init(&fdi, 0);
311
312         /*
313          * Note that we DO NOT have a UIO_SYSSPACE here (so no need for p2p
314          * I/O).
315          */
316
317         while (uio_resid(uio) > 0) {
318                 fdi.iosize = sizeof(*fri);
319                 if (fri == NULL)
320                         fdisp_make_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
321                 else
322                         fdisp_refresh_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
323
324                 fri = fdi.indata;
325                 fri->fh = fufh->fh_id;
326                 fri->offset = uio_offset(uio);
327                 fri->size = MIN(uio->uio_resid,
328                     fuse_get_mpdata(vp->v_mount)->max_read);
329
330                     if ((err = fdisp_wait_answ(&fdi))) {
331                         break;
332                 }
333                 if ((err = fuse_internal_readdir_processdata(uio, fri->size, fdi.answ,
334                     fdi.iosize, cookediov))) {
335                         break;
336                 }
337         }
338
339         fdisp_destroy(&fdi);
340         return ((err == -1) ? 0 : err);
341 }
342
343 int
344 fuse_internal_readdir_processdata(struct uio *uio,
345     size_t reqsize,
346     void *buf,
347     size_t bufsize,
348     void *param)
349 {
350         int err = 0;
351         int cou = 0;
352         int bytesavail;
353         size_t freclen;
354
355         struct dirent *de;
356         struct fuse_dirent *fudge;
357         struct fuse_iov *cookediov = param;
358
359         if (bufsize < FUSE_NAME_OFFSET) {
360                 return -1;
361         }
362         for (;;) {
363
364                 if (bufsize < FUSE_NAME_OFFSET) {
365                         err = -1;
366                         break;
367                 }
368                 fudge = (struct fuse_dirent *)buf;
369                 freclen = FUSE_DIRENT_SIZE(fudge);
370
371                 cou++;
372
373                 if (bufsize < freclen) {
374                         err = ((cou == 1) ? -1 : 0);
375                         break;
376                 }
377 #ifdef ZERO_PAD_INCOMPLETE_BUFS
378                 if (isbzero(buf, FUSE_NAME_OFFSET)) {
379                         err = -1;
380                         break;
381                 }
382 #endif
383
384                 if (!fudge->namelen || fudge->namelen > MAXNAMLEN) {
385                         err = EINVAL;
386                         break;
387                 }
388                 bytesavail = GENERIC_DIRSIZ((struct pseudo_dirent *)
389                                             &fudge->namelen);
390
391                 if (bytesavail > uio_resid(uio)) {
392                         err = -1;
393                         break;
394                 }
395                 fiov_adjust(cookediov, bytesavail);
396                 bzero(cookediov->base, bytesavail);
397
398                 de = (struct dirent *)cookediov->base;
399                 de->d_fileno = fudge->ino;
400                 de->d_reclen = bytesavail;
401                 de->d_type = fudge->type;
402                 de->d_namlen = fudge->namelen;
403                 memcpy((char *)cookediov->base + sizeof(struct dirent) - 
404                        MAXNAMLEN - 1,
405                        (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
406                 dirent_terminate(de);
407
408                 err = uiomove(cookediov->base, cookediov->len, uio);
409                 if (err) {
410                         break;
411                 }
412                 buf = (char *)buf + freclen;
413                 bufsize -= freclen;
414                 uio_setoffset(uio, fudge->off);
415         }
416
417         return err;
418 }
419
420 /* remove */
421
422 int
423 fuse_internal_remove(struct vnode *dvp,
424     struct vnode *vp,
425     struct componentname *cnp,
426     enum fuse_opcode op)
427 {
428         struct fuse_dispatcher fdi;
429         int err = 0;
430
431         fdisp_init(&fdi, cnp->cn_namelen + 1);
432         fdisp_make_vp(&fdi, op, dvp, cnp->cn_thread, cnp->cn_cred);
433
434         memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
435         ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
436
437         err = fdisp_wait_answ(&fdi);
438         fdisp_destroy(&fdi);
439         return err;
440 }
441
442 /* rename */
443
444 int
445 fuse_internal_rename(struct vnode *fdvp,
446     struct componentname *fcnp,
447     struct vnode *tdvp,
448     struct componentname *tcnp)
449 {
450         struct fuse_dispatcher fdi;
451         struct fuse_rename_in *fri;
452         int err = 0;
453
454         fdisp_init(&fdi, sizeof(*fri) + fcnp->cn_namelen + tcnp->cn_namelen + 2);
455         fdisp_make_vp(&fdi, FUSE_RENAME, fdvp, tcnp->cn_thread, tcnp->cn_cred);
456
457         fri = fdi.indata;
458         fri->newdir = VTOI(tdvp);
459         memcpy((char *)fdi.indata + sizeof(*fri), fcnp->cn_nameptr,
460             fcnp->cn_namelen);
461         ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen] = '\0';
462         memcpy((char *)fdi.indata + sizeof(*fri) + fcnp->cn_namelen + 1,
463             tcnp->cn_nameptr, tcnp->cn_namelen);
464         ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen +
465             tcnp->cn_namelen + 1] = '\0';
466
467         err = fdisp_wait_answ(&fdi);
468         fdisp_destroy(&fdi);
469         return err;
470 }
471
472 /* strategy */
473
474 /* entity creation */
475
476 void
477 fuse_internal_newentry_makerequest(struct mount *mp,
478     uint64_t dnid,
479     struct componentname *cnp,
480     enum fuse_opcode op,
481     void *buf,
482     size_t bufsize,
483     struct fuse_dispatcher *fdip)
484 {
485         fdip->iosize = bufsize + cnp->cn_namelen + 1;
486
487         fdisp_make(fdip, op, mp, dnid, cnp->cn_thread, cnp->cn_cred);
488         memcpy(fdip->indata, buf, bufsize);
489         memcpy((char *)fdip->indata + bufsize, cnp->cn_nameptr, cnp->cn_namelen);
490         ((char *)fdip->indata)[bufsize + cnp->cn_namelen] = '\0';
491 }
492
493 int
494 fuse_internal_newentry_core(struct vnode *dvp,
495     struct vnode **vpp,
496     struct componentname *cnp,
497     enum vtype vtyp,
498     struct fuse_dispatcher *fdip)
499 {
500         int err = 0;
501         struct fuse_entry_out *feo;
502         struct mount *mp = vnode_mount(dvp);
503
504         if ((err = fdisp_wait_answ(fdip))) {
505                 return err;
506         }
507         feo = fdip->answ;
508
509         if ((err = fuse_internal_checkentry(feo, vtyp))) {
510                 return err;
511         }
512         err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, vtyp);
513         if (err) {
514                 fuse_internal_forget_send(mp, cnp->cn_thread, cnp->cn_cred,
515                     feo->nodeid, 1);
516                 return err;
517         }
518         fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
519                 feo->attr_valid_nsec, NULL);
520
521         return err;
522 }
523
524 int
525 fuse_internal_newentry(struct vnode *dvp,
526     struct vnode **vpp,
527     struct componentname *cnp,
528     enum fuse_opcode op,
529     void *buf,
530     size_t bufsize,
531     enum vtype vtype)
532 {
533         int err;
534         struct fuse_dispatcher fdi;
535         struct mount *mp = vnode_mount(dvp);
536
537         fdisp_init(&fdi, 0);
538         fuse_internal_newentry_makerequest(mp, VTOI(dvp), cnp, op, buf,
539             bufsize, &fdi);
540         err = fuse_internal_newentry_core(dvp, vpp, cnp, vtype, &fdi);
541         fdisp_destroy(&fdi);
542
543         return err;
544 }
545
546 /* entity destruction */
547
548 int
549 fuse_internal_forget_callback(struct fuse_ticket *ftick, struct uio *uio)
550 {
551         fuse_internal_forget_send(ftick->tk_data->mp, curthread, NULL,
552             ((struct fuse_in_header *)ftick->tk_ms_fiov.base)->nodeid, 1);
553
554         return 0;
555 }
556
557 void
558 fuse_internal_forget_send(struct mount *mp,
559     struct thread *td,
560     struct ucred *cred,
561     uint64_t nodeid,
562     uint64_t nlookup)
563 {
564
565         struct fuse_dispatcher fdi;
566         struct fuse_forget_in *ffi;
567
568         /*
569          * KASSERT(nlookup > 0, ("zero-times forget for vp #%llu",
570          *         (long long unsigned) nodeid));
571          */
572
573         fdisp_init(&fdi, sizeof(*ffi));
574         fdisp_make(&fdi, FUSE_FORGET, mp, nodeid, td, cred);
575
576         ffi = fdi.indata;
577         ffi->nlookup = nlookup;
578
579         fuse_insert_message(fdi.tick);
580         fdisp_destroy(&fdi);
581 }
582
583 /* Read a vnode's attributes from cache or fetch them from the fuse daemon */
584 int
585 fuse_internal_getattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
586         struct thread *td)
587 {
588         struct fuse_dispatcher fdi;
589         struct fuse_vnode_data *fvdat = VTOFUD(vp);
590         struct vattr *attrs;
591         struct fuse_attr_out *fao;
592         int err = 0;
593
594         if ((attrs = VTOVA(vp)) != NULL) {
595                 /* struct copy */
596                 *vap = *attrs;
597                 if ((fvdat->flag & FN_SIZECHANGE) != 0)
598                         vap->va_size = fvdat->filesize;
599                 return 0;
600         }
601
602         fdisp_init(&fdi, 0);
603         if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, td, cred))) {
604                 if (err == ENOENT) {
605                         fuse_internal_vnode_disappear(vp);
606                 }
607                 goto out;
608         }
609
610         fao = (struct fuse_attr_out *)fdi.answ;
611         fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
612                 fao->attr_valid_nsec, vap);
613         if (vap->va_type != vnode_vtype(vp)) {
614                 fuse_internal_vnode_disappear(vp);
615                 err = ENOENT;
616                 goto out;
617         }
618         if ((fvdat->flag & FN_SIZECHANGE) != 0)
619                 vap->va_size = fvdat->filesize;
620
621         if (vnode_isreg(vp) && (fvdat->flag & FN_SIZECHANGE) == 0) {
622                 /*
623                  * This is for those cases when the file size changed without us
624                  * knowing, and we want to catch up.
625                  */
626                 off_t new_filesize = fao->attr.size;
627
628                 if (fvdat->filesize != new_filesize) {
629                         fuse_vnode_setsize(vp, cred, new_filesize);
630                         fvdat->flag &= ~FN_SIZECHANGE;
631                 }
632         }
633
634 out:
635         fdisp_destroy(&fdi);
636         return err;
637 }
638
639 void
640 fuse_internal_vnode_disappear(struct vnode *vp)
641 {
642         struct fuse_vnode_data *fvdat = VTOFUD(vp);
643
644         ASSERT_VOP_ELOCKED(vp, "fuse_internal_vnode_disappear");
645         fvdat->flag |= FN_REVOKED;
646         bintime_clear(&fvdat->attr_cache_timeout);
647         cache_purge(vp);
648 }
649
650 /* fuse start/stop */
651
652 int
653 fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
654 {
655         int err = 0;
656         struct fuse_data *data = tick->tk_data;
657         struct fuse_init_out *fiio;
658
659         if ((err = tick->tk_aw_ohead.error)) {
660                 goto out;
661         }
662         if ((err = fticket_pull(tick, uio))) {
663                 goto out;
664         }
665         fiio = fticket_resp(tick)->base;
666
667         /* XXX: Do we want to check anything further besides this? */
668         if (fiio->major < 7) {
669                 SDT_PROBE2(fuse, , internal, trace, 1,
670                         "userpace version too low");
671                 err = EPROTONOSUPPORT;
672                 goto out;
673         }
674         data->fuse_libabi_major = fiio->major;
675         data->fuse_libabi_minor = fiio->minor;
676
677         if (fuse_libabi_geq(data, 7, 5)) {
678                 if (fticket_resp(tick)->len == sizeof(struct fuse_init_out)) {
679                         data->max_write = fiio->max_write;
680                 } else {
681                         err = EINVAL;
682                 }
683         } else {
684                 /* Old fix values */
685                 data->max_write = 4096;
686         }
687
688 out:
689         if (err) {
690                 fdata_set_dead(data);
691         }
692         FUSE_LOCK();
693         data->dataflags |= FSESS_INITED;
694         wakeup(&data->ticketer);
695         FUSE_UNLOCK();
696
697         return 0;
698 }
699
700 void
701 fuse_internal_send_init(struct fuse_data *data, struct thread *td)
702 {
703         struct fuse_init_in *fiii;
704         struct fuse_dispatcher fdi;
705
706         fdisp_init(&fdi, sizeof(*fiii));
707         fdisp_make(&fdi, FUSE_INIT, data->mp, 0, td, NULL);
708         fiii = fdi.indata;
709         fiii->major = FUSE_KERNEL_VERSION;
710         fiii->minor = FUSE_KERNEL_MINOR_VERSION;
711         /* 
712          * fusefs currently doesn't do any readahead other than fetching whole
713          * buffer cache block sized regions at once.  So the max readahead is
714          * the size of a buffer cache block.
715          */
716         fiii->max_readahead = maxbcachebuf;
717         fiii->flags = 0;
718
719         fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
720         fuse_insert_message(fdi.tick);
721         fdisp_destroy(&fdi);
722 }
723
724 #ifdef ZERO_PAD_INCOMPLETE_BUFS
725 static int
726 isbzero(void *buf, size_t len)
727 {
728         int i;
729
730         for (i = 0; i < len; i++) {
731                 if (((char *)buf)[i])
732                         return (0);
733         }
734
735         return (1);
736 }
737
738 #endif