]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/fuse/fuse_vnops.c
fusefs: implement VOP_BMAP
[FreeBSD/FreeBSD.git] / sys / fs / fuse / fuse_vnops.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/rwlock.h>
72 #include <sys/sx.h>
73 #include <sys/proc.h>
74 #include <sys/mount.h>
75 #include <sys/vnode.h>
76 #include <sys/namei.h>
77 #include <sys/extattr.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/vmmeter.h>
88
89 #include <vm/vm.h>
90 #include <vm/vm_extern.h>
91 #include <vm/pmap.h>
92 #include <vm/vm_map.h>
93 #include <vm/vm_page.h>
94 #include <vm/vm_param.h>
95 #include <vm/vm_object.h>
96 #include <vm/vm_pager.h>
97 #include <vm/vnode_pager.h>
98 #include <vm/vm_object.h>
99
100 #include "fuse.h"
101 #include "fuse_file.h"
102 #include "fuse_internal.h"
103 #include "fuse_ipc.h"
104 #include "fuse_node.h"
105 #include "fuse_io.h"
106
107 #include <sys/priv.h>
108
109 /* Maximum number of hardlinks to a single FUSE file */
110 #define FUSE_LINK_MAX                      UINT32_MAX
111
112 SDT_PROVIDER_DECLARE(fusefs);
113 /* 
114  * Fuse trace probe:
115  * arg0: verbosity.  Higher numbers give more verbose messages
116  * arg1: Textual message
117  */
118 SDT_PROBE_DEFINE2(fusefs, , vnops, trace, "int", "char*");
119
120 /* vnode ops */
121 static vop_access_t fuse_vnop_access;
122 static vop_advlock_t fuse_vnop_advlock;
123 static vop_bmap_t fuse_vnop_bmap;
124 static vop_close_t fuse_fifo_close;
125 static vop_close_t fuse_vnop_close;
126 static vop_create_t fuse_vnop_create;
127 static vop_deleteextattr_t fuse_vnop_deleteextattr;
128 static vop_fdatasync_t fuse_vnop_fdatasync;
129 static vop_fsync_t fuse_vnop_fsync;
130 static vop_getattr_t fuse_vnop_getattr;
131 static vop_getextattr_t fuse_vnop_getextattr;
132 static vop_inactive_t fuse_vnop_inactive;
133 static vop_link_t fuse_vnop_link;
134 static vop_listextattr_t fuse_vnop_listextattr;
135 static vop_lookup_t fuse_vnop_lookup;
136 static vop_mkdir_t fuse_vnop_mkdir;
137 static vop_mknod_t fuse_vnop_mknod;
138 static vop_open_t fuse_vnop_open;
139 static vop_pathconf_t fuse_vnop_pathconf;
140 static vop_read_t fuse_vnop_read;
141 static vop_readdir_t fuse_vnop_readdir;
142 static vop_readlink_t fuse_vnop_readlink;
143 static vop_reclaim_t fuse_vnop_reclaim;
144 static vop_remove_t fuse_vnop_remove;
145 static vop_rename_t fuse_vnop_rename;
146 static vop_rmdir_t fuse_vnop_rmdir;
147 static vop_setattr_t fuse_vnop_setattr;
148 static vop_setextattr_t fuse_vnop_setextattr;
149 static vop_strategy_t fuse_vnop_strategy;
150 static vop_symlink_t fuse_vnop_symlink;
151 static vop_write_t fuse_vnop_write;
152 static vop_getpages_t fuse_vnop_getpages;
153 static vop_putpages_t fuse_vnop_putpages;
154 static vop_print_t fuse_vnop_print;
155 static vop_vptofh_t fuse_vnop_vptofh;
156
157 struct vop_vector fuse_fifoops = {
158         .vop_default =          &fifo_specops,
159         .vop_access =           fuse_vnop_access,
160         .vop_close =            fuse_fifo_close,
161         .vop_fsync =            fuse_vnop_fsync,
162         .vop_getattr =          fuse_vnop_getattr,
163         .vop_inactive =         fuse_vnop_inactive,
164         .vop_pathconf =         fuse_vnop_pathconf,
165         .vop_print =            fuse_vnop_print,
166         .vop_read =             VOP_PANIC,
167         .vop_reclaim =          fuse_vnop_reclaim,
168         .vop_setattr =          fuse_vnop_setattr,
169         .vop_write =            VOP_PANIC,
170         .vop_vptofh =           fuse_vnop_vptofh,
171 };
172
173 struct vop_vector fuse_vnops = {
174         .vop_allocate = VOP_EINVAL,
175         .vop_default = &default_vnodeops,
176         .vop_access = fuse_vnop_access,
177         .vop_advlock = fuse_vnop_advlock,
178         .vop_bmap = fuse_vnop_bmap,
179         .vop_close = fuse_vnop_close,
180         .vop_create = fuse_vnop_create,
181         .vop_deleteextattr = fuse_vnop_deleteextattr,
182         .vop_fsync = fuse_vnop_fsync,
183         .vop_fdatasync = fuse_vnop_fdatasync,
184         .vop_getattr = fuse_vnop_getattr,
185         .vop_getextattr = fuse_vnop_getextattr,
186         .vop_inactive = fuse_vnop_inactive,
187         /*
188          * TODO: implement vop_ioctl after upgrading to protocol 7.16.
189          * FUSE_IOCTL was added in 7.11, but 32-bit compat is broken until
190          * 7.16.
191          */
192         .vop_link = fuse_vnop_link,
193         .vop_listextattr = fuse_vnop_listextattr,
194         .vop_lookup = fuse_vnop_lookup,
195         .vop_mkdir = fuse_vnop_mkdir,
196         .vop_mknod = fuse_vnop_mknod,
197         .vop_open = fuse_vnop_open,
198         .vop_pathconf = fuse_vnop_pathconf,
199         /*
200          * TODO: implement vop_poll after upgrading to protocol 7.21.
201          * FUSE_POLL was added in protocol 7.11, but it's kind of broken until
202          * 7.21, which adds the ability for the client to choose which poll
203          * events it wants, and for a client to deregister a file handle
204          */
205         .vop_read = fuse_vnop_read,
206         .vop_readdir = fuse_vnop_readdir,
207         .vop_readlink = fuse_vnop_readlink,
208         .vop_reclaim = fuse_vnop_reclaim,
209         .vop_remove = fuse_vnop_remove,
210         .vop_rename = fuse_vnop_rename,
211         .vop_rmdir = fuse_vnop_rmdir,
212         .vop_setattr = fuse_vnop_setattr,
213         .vop_setextattr = fuse_vnop_setextattr,
214         .vop_strategy = fuse_vnop_strategy,
215         .vop_symlink = fuse_vnop_symlink,
216         .vop_write = fuse_vnop_write,
217         .vop_getpages = fuse_vnop_getpages,
218         .vop_putpages = fuse_vnop_putpages,
219         .vop_print = fuse_vnop_print,
220         .vop_vptofh = fuse_vnop_vptofh,
221 };
222
223 u_long fuse_lookup_cache_hits = 0;
224
225 SYSCTL_ULONG(_vfs_fusefs, OID_AUTO, lookup_cache_hits, CTLFLAG_RD,
226     &fuse_lookup_cache_hits, 0, "number of positive cache hits in lookup");
227
228 u_long fuse_lookup_cache_misses = 0;
229
230 SYSCTL_ULONG(_vfs_fusefs, OID_AUTO, lookup_cache_misses, CTLFLAG_RD,
231     &fuse_lookup_cache_misses, 0, "number of cache misses in lookup");
232
233 /*
234  * XXX: This feature is highly experimental and can bring to instabilities,
235  * needs revisiting before to be enabled by default.
236  */
237 static int fuse_reclaim_revoked = 0;
238
239 SYSCTL_INT(_vfs_fusefs, OID_AUTO, reclaim_revoked, CTLFLAG_RW,
240     &fuse_reclaim_revoked, 0, "");
241
242 uma_zone_t fuse_pbuf_zone;
243
244 #define fuse_vm_page_lock(m)            vm_page_lock((m));
245 #define fuse_vm_page_unlock(m)          vm_page_unlock((m));
246 #define fuse_vm_page_lock_queues()      ((void)0)
247 #define fuse_vm_page_unlock_queues()    ((void)0)
248
249 /* Check permission for extattr operations, much like extattr_check_cred */
250 static int
251 fuse_extattr_check_cred(struct vnode *vp, int ns, struct ucred *cred,
252         struct thread *td, accmode_t accmode)
253 {
254         struct mount *mp = vnode_mount(vp);
255         struct fuse_data *data = fuse_get_mpdata(mp);
256
257         /*
258          * Kernel-invoked always succeeds.
259          */
260         if (cred == NOCRED)
261                 return (0);
262
263         /*
264          * Do not allow privileged processes in jail to directly manipulate
265          * system attributes.
266          */
267         switch (ns) {
268         case EXTATTR_NAMESPACE_SYSTEM:
269                 if (data->dataflags & FSESS_DEFAULT_PERMISSIONS) {
270                         return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM));
271                 }
272                 /* FALLTHROUGH */
273         case EXTATTR_NAMESPACE_USER:
274                 return (fuse_internal_access(vp, accmode, td, cred));
275         default:
276                 return (EPERM);
277         }
278 }
279
280 /* Get a filehandle for a directory */
281 static int
282 fuse_filehandle_get_dir(struct vnode *vp, struct fuse_filehandle **fufhp,
283         struct ucred *cred, pid_t pid)
284 {
285         if (fuse_filehandle_get(vp, FREAD, fufhp, cred, pid) == 0)
286                 return 0;
287         return fuse_filehandle_get(vp, FEXEC, fufhp, cred, pid);
288 }
289
290 /* Send FUSE_FLUSH for this vnode */
291 static int
292 fuse_flush(struct vnode *vp, struct ucred *cred, pid_t pid, int fflag)
293 {
294         struct fuse_flush_in *ffi;
295         struct fuse_filehandle *fufh;
296         struct fuse_dispatcher fdi;
297         struct thread *td = curthread;
298         struct mount *mp = vnode_mount(vp);
299         int err;
300
301         if (!fsess_isimpl(vnode_mount(vp), FUSE_FLUSH))
302                 return 0;
303
304         err = fuse_filehandle_getrw(vp, fflag, &fufh, cred, pid);
305         if (err)
306                 return err;
307
308         fdisp_init(&fdi, sizeof(*ffi));
309         fdisp_make_vp(&fdi, FUSE_FLUSH, vp, td, cred);
310         ffi = fdi.indata;
311         ffi->fh = fufh->fh_id;
312         /* 
313          * If the file has a POSIX lock then we're supposed to set lock_owner.
314          * If not, then lock_owner is undefined.  So we may as well always set
315          * it.
316          */
317         ffi->lock_owner = td->td_proc->p_pid;
318
319         err = fdisp_wait_answ(&fdi);
320         if (err == ENOSYS) {
321                 fsess_set_notimpl(mp, FUSE_FLUSH);
322                 err = 0;
323         }
324         fdisp_destroy(&fdi);
325         return err;
326 }
327
328 /* Close wrapper for fifos.  */
329 static int
330 fuse_fifo_close(struct vop_close_args *ap)
331 {
332         return (fifo_specops.vop_close(ap));
333 }
334
335 /*
336     struct vnop_access_args {
337         struct vnode *a_vp;
338 #if VOP_ACCESS_TAKES_ACCMODE_T
339         accmode_t a_accmode;
340 #else
341         int a_mode;
342 #endif
343         struct ucred *a_cred;
344         struct thread *a_td;
345     };
346 */
347 static int
348 fuse_vnop_access(struct vop_access_args *ap)
349 {
350         struct vnode *vp = ap->a_vp;
351         int accmode = ap->a_accmode;
352         struct ucred *cred = ap->a_cred;
353
354         struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
355
356         int err;
357
358         if (fuse_isdeadfs(vp)) {
359                 if (vnode_isvroot(vp)) {
360                         return 0;
361                 }
362                 return ENXIO;
363         }
364         if (!(data->dataflags & FSESS_INITED)) {
365                 if (vnode_isvroot(vp)) {
366                         if (priv_check_cred(cred, PRIV_VFS_ADMIN) ||
367                             (fuse_match_cred(data->daemoncred, cred) == 0)) {
368                                 return 0;
369                         }
370                 }
371                 return EBADF;
372         }
373         if (vnode_islnk(vp)) {
374                 return 0;
375         }
376
377         err = fuse_internal_access(vp, accmode, ap->a_td, ap->a_cred);
378         return err;
379 }
380
381 /*
382  * struct vop_advlock_args {
383  *      struct vop_generic_args a_gen;
384  *      struct vnode *a_vp;
385  *      void *a_id;
386  *      int a_op;
387  *      struct flock *a_fl;
388  *      int a_flags;
389  * }
390  */
391 static int
392 fuse_vnop_advlock(struct vop_advlock_args *ap)
393 {
394         struct vnode *vp = ap->a_vp;
395         struct flock *fl = ap->a_fl;
396         struct thread *td = curthread;
397         struct ucred *cred = td->td_ucred;
398         pid_t pid = td->td_proc->p_pid;
399         struct fuse_filehandle *fufh;
400         struct fuse_dispatcher fdi;
401         struct fuse_lk_in *fli;
402         struct fuse_lk_out *flo;
403         enum fuse_opcode op;
404         int dataflags, err;
405         int flags = ap->a_flags;
406
407         dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags;
408
409         if (fuse_isdeadfs(vp)) {
410                 return ENXIO;
411         }
412
413         if (!(dataflags & FSESS_POSIX_LOCKS))
414                 return vop_stdadvlock(ap);
415         /* FUSE doesn't properly support flock until protocol 7.17 */
416         if (flags & F_FLOCK)
417                 return vop_stdadvlock(ap);
418
419         err = fuse_filehandle_get_anyflags(vp, &fufh, cred, pid);
420         if (err)
421                 return err;
422
423         fdisp_init(&fdi, sizeof(*fli));
424
425         switch(ap->a_op) {
426         case F_GETLK:
427                 op = FUSE_GETLK;
428                 break;
429         case F_SETLK:
430                 op = FUSE_SETLK;
431                 break;
432         case F_SETLKW:
433                 op = FUSE_SETLKW;
434                 break;
435         default:
436                 return EINVAL;
437         }
438
439         fdisp_make_vp(&fdi, op, vp, td, cred);
440         fli = fdi.indata;
441         fli->fh = fufh->fh_id;
442         fli->owner = fl->l_pid;
443         fli->lk.start = fl->l_start;
444         if (fl->l_len != 0)
445                 fli->lk.end = fl->l_start + fl->l_len - 1;
446         else
447                 fli->lk.end = INT64_MAX;
448         fli->lk.type = fl->l_type;
449         fli->lk.pid = fl->l_pid;
450
451         err = fdisp_wait_answ(&fdi);
452         fdisp_destroy(&fdi);
453
454         if (err == 0 && op == FUSE_GETLK) {
455                 flo = fdi.answ;
456                 fl->l_type = flo->lk.type;
457                 fl->l_pid = flo->lk.pid;
458                 if (flo->lk.type != F_UNLCK) {
459                         fl->l_start = flo->lk.start;
460                         if (flo->lk.end == INT64_MAX)
461                                 fl->l_len = 0;
462                         else
463                                 fl->l_len = flo->lk.end - flo->lk.start + 1;
464                         fl->l_start = flo->lk.start;
465                 }
466         }
467
468         return err;
469 }
470
471 /* {
472         struct vnode *a_vp;
473         daddr_t a_bn;
474         struct bufobj **a_bop;
475         daddr_t *a_bnp;
476         int *a_runp;
477         int *a_runb;
478 } */
479 static int
480 fuse_vnop_bmap(struct vop_bmap_args *ap)
481 {
482         struct vnode *vp = ap->a_vp;
483         struct bufobj **bo = ap->a_bop;
484         struct thread *td = curthread;
485         struct mount *mp;
486         struct fuse_dispatcher fdi;
487         struct fuse_bmap_in *fbi;
488         struct fuse_bmap_out *fbo;
489         struct fuse_data *data;
490         uint64_t biosize;
491         off_t filesize;
492         daddr_t lbn = ap->a_bn;
493         daddr_t *pbn = ap->a_bnp;
494         int *runp = ap->a_runp;
495         int *runb = ap->a_runb;
496         int error = 0;
497         int maxrun;
498
499         if (fuse_isdeadfs(vp)) {
500                 return ENXIO;
501         }
502
503         mp = vnode_mount(vp);
504         data = fuse_get_mpdata(mp);
505         biosize = fuse_iosize(vp);
506         maxrun = MIN(vp->v_mount->mnt_iosize_max / biosize - 1,
507                 data->max_readahead_blocks);
508
509         if (bo != NULL)
510                 *bo = &vp->v_bufobj;
511
512         /*
513          * The FUSE_BMAP operation does not include the runp and runb
514          * variables, so we must guess.  Report nonzero contiguous runs so
515          * cluster_read will combine adjacent reads.  It's worthwhile to reduce
516          * upcalls even if we don't know the true physical layout of the file.
517          * 
518          * FUSE file systems may opt out of read clustering in two ways:
519          * * mounting with -onoclusterr
520          * * Setting max_readahead <= maxbcachebuf during FUSE_INIT
521          */
522         if (runb != NULL)
523                 *runb = MIN(lbn, maxrun);
524         if (runp != NULL) {
525                 error = fuse_vnode_size(vp, &filesize, td->td_ucred, td);
526                 if (error == 0)
527                         *runp = MIN(MAX(0, filesize / biosize - lbn - 1),
528                                     maxrun);
529                 else
530                         *runp = 0;
531         }
532
533         if (fsess_isimpl(mp, FUSE_BMAP)) {
534                 fdisp_init(&fdi, sizeof(*fbi));
535                 fdisp_make_vp(&fdi, FUSE_BMAP, vp, td, td->td_ucred);
536                 fbi = fdi.indata;
537                 fbi->block = lbn;
538                 fbi->blocksize = biosize;
539                 error = fdisp_wait_answ(&fdi);
540                 if (error == ENOSYS) {
541                         fsess_set_notimpl(mp, FUSE_BMAP);
542                         error = 0;
543                 } else {
544                         fbo = fdi.answ;
545                         if (error == 0 && pbn != NULL)
546                                 *pbn = fbo->block;
547                         return error;
548                 }
549         }
550
551         /* If the daemon doesn't support BMAP, make up a sensible default */
552         if (pbn != NULL)
553                 *pbn = lbn * btodb(biosize);
554         return (error);
555 }
556
557 /*
558     struct vop_close_args {
559         struct vnode *a_vp;
560         int  a_fflag;
561         struct ucred *a_cred;
562         struct thread *a_td;
563     };
564 */
565 static int
566 fuse_vnop_close(struct vop_close_args *ap)
567 {
568         struct vnode *vp = ap->a_vp;
569         struct ucred *cred = ap->a_cred;
570         int fflag = ap->a_fflag;
571         struct thread *td = ap->a_td;
572         pid_t pid = td->td_proc->p_pid;
573         int err = 0;
574
575         if (fuse_isdeadfs(vp))
576                 return 0;
577         if (vnode_isdir(vp))
578                 return 0;
579         if (fflag & IO_NDELAY)
580                 return 0;
581
582         err = fuse_flush(vp, cred, pid, fflag);
583         /* TODO: close the file handle, if we're sure it's no longer used */
584         if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
585                 fuse_vnode_savesize(vp, cred, td->td_proc->p_pid);
586         }
587         return err;
588 }
589
590 static void
591 fdisp_make_mknod_for_fallback(
592         struct fuse_dispatcher *fdip,
593         struct componentname *cnp,
594         struct vnode *dvp,
595         uint64_t parentnid,
596         struct thread *td,
597         struct ucred *cred,
598         mode_t mode,
599         enum fuse_opcode *op)
600 {
601         struct fuse_mknod_in *fmni;
602
603         fdisp_init(fdip, sizeof(*fmni) + cnp->cn_namelen + 1);
604         *op = FUSE_MKNOD;
605         fdisp_make(fdip, *op, vnode_mount(dvp), parentnid, td, cred);
606         fmni = fdip->indata;
607         fmni->mode = mode;
608         fmni->rdev = 0;
609         memcpy((char *)fdip->indata + sizeof(*fmni), cnp->cn_nameptr,
610             cnp->cn_namelen);
611         ((char *)fdip->indata)[sizeof(*fmni) + cnp->cn_namelen] = '\0';
612 }
613 /*
614     struct vnop_create_args {
615         struct vnode *a_dvp;
616         struct vnode **a_vpp;
617         struct componentname *a_cnp;
618         struct vattr *a_vap;
619     };
620 */
621 static int
622 fuse_vnop_create(struct vop_create_args *ap)
623 {
624         struct vnode *dvp = ap->a_dvp;
625         struct vnode **vpp = ap->a_vpp;
626         struct componentname *cnp = ap->a_cnp;
627         struct vattr *vap = ap->a_vap;
628         struct thread *td = cnp->cn_thread;
629         struct ucred *cred = cnp->cn_cred;
630
631         struct fuse_data *data;
632         struct fuse_create_in *fci;
633         struct fuse_entry_out *feo;
634         struct fuse_open_out *foo;
635         struct fuse_dispatcher fdi, fdi2;
636         struct fuse_dispatcher *fdip = &fdi;
637         struct fuse_dispatcher *fdip2 = NULL;
638
639         int err;
640
641         struct mount *mp = vnode_mount(dvp);
642         data = fuse_get_mpdata(mp);
643         uint64_t parentnid = VTOFUD(dvp)->nid;
644         mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
645         enum fuse_opcode op;
646         int flags;
647
648         if (fuse_isdeadfs(dvp))
649                 return ENXIO;
650
651         /* FUSE expects sockets to be created with FUSE_MKNOD */
652         if (vap->va_type == VSOCK)
653                 return fuse_internal_mknod(dvp, vpp, cnp, vap);
654
655         /* 
656          * VOP_CREATE doesn't tell us the open(2) flags, so we guess.  Only a
657          * writable mode makes sense, and we might as well include readability
658          * too.
659          */
660         flags = O_RDWR;
661
662         bzero(&fdi, sizeof(fdi));
663
664         if (vap->va_type != VREG)
665                 return (EINVAL);
666
667         if (!fsess_isimpl(mp, FUSE_CREATE) || vap->va_type == VSOCK) {
668                 /* Fallback to FUSE_MKNOD/FUSE_OPEN */
669                 fdisp_make_mknod_for_fallback(fdip, cnp, dvp, parentnid, td,
670                         cred, mode, &op);
671         } else {
672                 /* Use FUSE_CREATE */
673                 size_t insize;
674
675                 op = FUSE_CREATE;
676                 fdisp_init(fdip, sizeof(*fci) + cnp->cn_namelen + 1);
677                 fdisp_make(fdip, op, vnode_mount(dvp), parentnid, td, cred);
678                 fci = fdip->indata;
679                 fci->mode = mode;
680                 fci->flags = O_CREAT | flags;
681                 if (fuse_libabi_geq(data, 7, 12)) {
682                         insize = sizeof(*fci);
683                         fci->umask = td->td_proc->p_fd->fd_cmask;
684                 } else {
685                         insize = sizeof(struct fuse_open_in);
686                 }
687
688                 memcpy((char *)fdip->indata + insize, cnp->cn_nameptr,
689                     cnp->cn_namelen);
690                 ((char *)fdip->indata)[insize + cnp->cn_namelen] = '\0';
691         }
692
693         err = fdisp_wait_answ(fdip);
694
695         if (err) {
696                 if (err == ENOSYS && op == FUSE_CREATE) {
697                         fsess_set_notimpl(mp, FUSE_CREATE);
698                         fdisp_make_mknod_for_fallback(fdip, cnp, dvp,
699                                 parentnid, td, cred, mode, &op);
700                         err = fdisp_wait_answ(fdip);
701                 }
702                 if (err)
703                         goto out;
704         }
705
706         feo = fdip->answ;
707
708         if ((err = fuse_internal_checkentry(feo, vap->va_type))) {
709                 goto out;
710         }
711
712         if (op == FUSE_CREATE) {
713                 foo = (struct fuse_open_out*)(feo + 1);
714         } else {
715                 /* Issue a separate FUSE_OPEN */
716                 struct fuse_open_in *foi;
717
718                 fdip2 = &fdi2;
719                 fdisp_init(fdip2, sizeof(*foi));
720                 fdisp_make(fdip2, FUSE_OPEN, vnode_mount(dvp), feo->nodeid, td,
721                         cred);
722                 foi = fdip2->indata;
723                 foi->flags = flags;
724                 err = fdisp_wait_answ(fdip2);
725                 if (err)
726                         goto out;
727                 foo = fdip2->answ;
728         }
729         err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, vap->va_type);
730         if (err) {
731                 struct fuse_release_in *fri;
732                 uint64_t nodeid = feo->nodeid;
733                 uint64_t fh_id = foo->fh;
734
735                 fdisp_init(fdip, sizeof(*fri));
736                 fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, td, cred);
737                 fri = fdip->indata;
738                 fri->fh = fh_id;
739                 fri->flags = flags;
740                 fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
741                 fuse_insert_message(fdip->tick, false);
742                 goto out;
743         }
744         ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
745         fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
746                 feo->attr_valid_nsec, NULL);
747
748         fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
749         fuse_vnode_open(*vpp, foo->open_flags, td);
750         /* 
751          * Purge the parent's attribute cache because the daemon should've
752          * updated its mtime and ctime
753          */
754         fuse_vnode_clear_attr_cache(dvp);
755         cache_purge_negative(dvp);
756
757 out:
758         if (fdip2)
759                 fdisp_destroy(fdip2);
760         fdisp_destroy(fdip);
761         return err;
762 }
763
764 /*
765     struct vnop_fdatasync_args {
766         struct vop_generic_args a_gen;
767         struct vnode * a_vp;
768         struct thread * a_td;
769     };
770 */
771 static int
772 fuse_vnop_fdatasync(struct vop_fdatasync_args *ap)
773 {
774         struct vnode *vp = ap->a_vp;
775         struct thread *td = ap->a_td;
776         int waitfor = MNT_WAIT;
777
778         int err = 0;
779
780         if (fuse_isdeadfs(vp)) {
781                 return 0;
782         }
783         if ((err = vop_stdfdatasync_buf(ap)))
784                 return err;
785
786         return fuse_internal_fsync(vp, td, waitfor, true);
787 }
788
789 /*
790     struct vnop_fsync_args {
791         struct vop_generic_args a_gen;
792         struct vnode * a_vp;
793         int  a_waitfor;
794         struct thread * a_td;
795     };
796 */
797 static int
798 fuse_vnop_fsync(struct vop_fsync_args *ap)
799 {
800         struct vnode *vp = ap->a_vp;
801         struct thread *td = ap->a_td;
802         int waitfor = ap->a_waitfor;
803         int err = 0;
804
805         if (fuse_isdeadfs(vp)) {
806                 return 0;
807         }
808         if ((err = vop_stdfsync(ap)))
809                 return err;
810
811         return fuse_internal_fsync(vp, td, waitfor, false);
812 }
813
814 /*
815     struct vnop_getattr_args {
816         struct vnode *a_vp;
817         struct vattr *a_vap;
818         struct ucred *a_cred;
819         struct thread *a_td;
820     };
821 */
822 static int
823 fuse_vnop_getattr(struct vop_getattr_args *ap)
824 {
825         struct vnode *vp = ap->a_vp;
826         struct vattr *vap = ap->a_vap;
827         struct ucred *cred = ap->a_cred;
828         struct thread *td = curthread;
829
830         int err = 0;
831         int dataflags;
832
833         dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags;
834
835         /* Note that we are not bailing out on a dead file system just yet. */
836
837         if (!(dataflags & FSESS_INITED)) {
838                 if (!vnode_isvroot(vp)) {
839                         fdata_set_dead(fuse_get_mpdata(vnode_mount(vp)));
840                         err = ENOTCONN;
841                         return err;
842                 } else {
843                         goto fake;
844                 }
845         }
846         err = fuse_internal_getattr(vp, vap, cred, td);
847         if (err == ENOTCONN && vnode_isvroot(vp)) {
848                 /* see comment in fuse_vfsop_statfs() */
849                 goto fake;
850         } else {
851                 return err;
852         }
853
854 fake:
855         bzero(vap, sizeof(*vap));
856         vap->va_type = vnode_vtype(vp);
857
858         return 0;
859 }
860
861 /*
862     struct vnop_inactive_args {
863         struct vnode *a_vp;
864         struct thread *a_td;
865     };
866 */
867 static int
868 fuse_vnop_inactive(struct vop_inactive_args *ap)
869 {
870         struct vnode *vp = ap->a_vp;
871         struct thread *td = ap->a_td;
872
873         struct fuse_vnode_data *fvdat = VTOFUD(vp);
874         struct fuse_filehandle *fufh, *fufh_tmp;
875
876         int need_flush = 1;
877
878         LIST_FOREACH_SAFE(fufh, &fvdat->handles, next, fufh_tmp) {
879                 if (need_flush && vp->v_type == VREG) {
880                         if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
881                                 fuse_vnode_savesize(vp, NULL, 0);
882                         }
883                         if ((fvdat->flag & FN_REVOKED) != 0)
884                                 fuse_io_invalbuf(vp, td);
885                         else
886                                 fuse_io_flushbuf(vp, MNT_WAIT, td);
887                         need_flush = 0;
888                 }
889                 fuse_filehandle_close(vp, fufh, td, NULL);
890         }
891
892         if ((fvdat->flag & FN_REVOKED) != 0 && fuse_reclaim_revoked) {
893                 vrecycle(vp);
894         }
895         return 0;
896 }
897
898 /*
899     struct vnop_link_args {
900         struct vnode *a_tdvp;
901         struct vnode *a_vp;
902         struct componentname *a_cnp;
903     };
904 */
905 static int
906 fuse_vnop_link(struct vop_link_args *ap)
907 {
908         struct vnode *vp = ap->a_vp;
909         struct vnode *tdvp = ap->a_tdvp;
910         struct componentname *cnp = ap->a_cnp;
911
912         struct vattr *vap = VTOVA(vp);
913
914         struct fuse_dispatcher fdi;
915         struct fuse_entry_out *feo;
916         struct fuse_link_in fli;
917
918         int err;
919
920         if (fuse_isdeadfs(vp)) {
921                 return ENXIO;
922         }
923         if (vnode_mount(tdvp) != vnode_mount(vp)) {
924                 return EXDEV;
925         }
926
927         /*
928          * This is a seatbelt check to protect naive userspace filesystems from
929          * themselves and the limitations of the FUSE IPC protocol.  If a
930          * filesystem does not allow attribute caching, assume it is capable of
931          * validating that nlink does not overflow.
932          */
933         if (vap != NULL && vap->va_nlink >= FUSE_LINK_MAX)
934                 return EMLINK;
935         fli.oldnodeid = VTOI(vp);
936
937         fdisp_init(&fdi, 0);
938         fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp,
939             FUSE_LINK, &fli, sizeof(fli), &fdi);
940         if ((err = fdisp_wait_answ(&fdi))) {
941                 goto out;
942         }
943         feo = fdi.answ;
944
945         err = fuse_internal_checkentry(feo, vnode_vtype(vp));
946         if (!err) {
947                 /* 
948                  * Purge the parent's attribute cache because the daemon
949                  * should've updated its mtime and ctime
950                  */
951                 fuse_vnode_clear_attr_cache(tdvp);
952                 fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid,
953                         feo->attr_valid_nsec, NULL);
954         }
955 out:
956         fdisp_destroy(&fdi);
957         return err;
958 }
959
960 struct fuse_lookup_alloc_arg {
961         struct fuse_entry_out *feo;
962         struct componentname *cnp;
963         uint64_t nid;
964         enum vtype vtyp;
965 };
966
967 /* Callback for vn_get_ino */
968 static int
969 fuse_lookup_alloc(struct mount *mp, void *arg, int lkflags, struct vnode **vpp)
970 {
971         struct fuse_lookup_alloc_arg *flaa = arg;
972
973         return fuse_vnode_get(mp, flaa->feo, flaa->nid, NULL, vpp, flaa->cnp,
974                 flaa->vtyp);
975 }
976
977 SDT_PROBE_DEFINE3(fusefs, , vnops, cache_lookup,
978         "int", "struct timespec*", "struct timespec*");
979 /*
980     struct vnop_lookup_args {
981         struct vnodeop_desc *a_desc;
982         struct vnode *a_dvp;
983         struct vnode **a_vpp;
984         struct componentname *a_cnp;
985     };
986 */
987 int
988 fuse_vnop_lookup(struct vop_lookup_args *ap)
989 {
990         struct vnode *dvp = ap->a_dvp;
991         struct vnode **vpp = ap->a_vpp;
992         struct componentname *cnp = ap->a_cnp;
993         struct thread *td = cnp->cn_thread;
994         struct ucred *cred = cnp->cn_cred;
995
996         int nameiop = cnp->cn_nameiop;
997         int flags = cnp->cn_flags;
998         int wantparent = flags & (LOCKPARENT | WANTPARENT);
999         int islastcn = flags & ISLASTCN;
1000         struct mount *mp = vnode_mount(dvp);
1001
1002         int err = 0;
1003         int lookup_err = 0;
1004         struct vnode *vp = NULL;
1005
1006         struct fuse_dispatcher fdi;
1007         bool did_lookup = false;
1008         struct fuse_entry_out *feo = NULL;
1009         enum vtype vtyp;        /* vnode type of target */
1010         off_t filesize;         /* filesize of target */
1011
1012         uint64_t nid;
1013
1014         if (fuse_isdeadfs(dvp)) {
1015                 *vpp = NULL;
1016                 return ENXIO;
1017         }
1018         if (!vnode_isdir(dvp))
1019                 return ENOTDIR;
1020
1021         if (islastcn && vfs_isrdonly(mp) && (nameiop != LOOKUP))
1022                 return EROFS;
1023
1024         if ((err = fuse_internal_access(dvp, VEXEC, td, cred)))
1025                 return err;
1026
1027         if (flags & ISDOTDOT) {
1028                 KASSERT(VTOFUD(dvp)->flag & FN_PARENT_NID,
1029                         ("Looking up .. is TODO"));
1030                 nid = VTOFUD(dvp)->parent_nid;
1031                 if (nid == 0)
1032                         return ENOENT;
1033                 /* .. is obviously a directory */
1034                 vtyp = VDIR;
1035                 filesize = 0;
1036         } else if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') {
1037                 nid = VTOI(dvp);
1038                 /* . is obviously a directory */
1039                 vtyp = VDIR;
1040                 filesize = 0;
1041         } else {
1042                 struct timespec now, timeout;
1043
1044                 err = cache_lookup(dvp, vpp, cnp, &timeout, NULL);
1045                 getnanouptime(&now);
1046                 SDT_PROBE3(fusefs, , vnops, cache_lookup, err, &timeout, &now);
1047                 switch (err) {
1048                 case -1:                /* positive match */
1049                         if (timespeccmp(&timeout, &now, >)) {
1050                                 atomic_add_acq_long(&fuse_lookup_cache_hits, 1);
1051                         } else {
1052                                 /* Cache timeout */
1053                                 atomic_add_acq_long(&fuse_lookup_cache_misses,
1054                                         1);
1055                                 bintime_clear(
1056                                         &VTOFUD(*vpp)->entry_cache_timeout);
1057                                 cache_purge(*vpp);
1058                                 if (dvp != *vpp)
1059                                         vput(*vpp);
1060                                 else 
1061                                         vrele(*vpp);
1062                                 *vpp = NULL;
1063                                 break;
1064                         }
1065                         return 0;
1066
1067                 case 0:         /* no match in cache */
1068                         atomic_add_acq_long(&fuse_lookup_cache_misses, 1);
1069                         break;
1070
1071                 case ENOENT:            /* negative match */
1072                         getnanouptime(&now);
1073                         if (timespeccmp(&timeout, &now, <=)) {
1074                                 /* Cache timeout */
1075                                 cache_purge_negative(dvp);
1076                                 break;
1077                         }
1078                         /* fall through */
1079                 default:
1080                         return err;
1081                 }
1082
1083                 nid = VTOI(dvp);
1084                 fdisp_init(&fdi, cnp->cn_namelen + 1);
1085                 fdisp_make(&fdi, FUSE_LOOKUP, mp, nid, td, cred);
1086
1087                 memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
1088                 ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
1089                 lookup_err = fdisp_wait_answ(&fdi);
1090                 did_lookup = true;
1091
1092                 if (!lookup_err) {
1093                         /* lookup call succeeded */
1094                         feo = (struct fuse_entry_out *)fdi.answ;
1095                         nid = feo->nodeid;
1096                         if (nid == 0) {
1097                                 /* zero nodeid means ENOENT and cache it */
1098                                 struct timespec timeout;
1099
1100                                 fdi.answ_stat = ENOENT;
1101                                 lookup_err = ENOENT;
1102                                 if (cnp->cn_flags & MAKEENTRY) {
1103                                         fuse_validity_2_timespec(feo, &timeout);
1104                                         cache_enter_time(dvp, *vpp, cnp,
1105                                                 &timeout, NULL);
1106                                 }
1107                         } else if (nid == FUSE_ROOT_ID) {
1108                                 lookup_err = EINVAL;
1109                         }
1110                         vtyp = IFTOVT(feo->attr.mode);
1111                         filesize = feo->attr.size;
1112                 }
1113                 if (lookup_err && (!fdi.answ_stat || lookup_err != ENOENT)) {
1114                         fdisp_destroy(&fdi);
1115                         return lookup_err;
1116                 }
1117         }
1118         /* lookup_err, if non-zero, must be ENOENT at this point */
1119
1120         if (lookup_err) {
1121                 /* Entry not found */
1122                 if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1123                         err = fuse_internal_access(dvp, VWRITE, td, cred);
1124                         if (!err) {
1125                                 /*
1126                                  * Set the SAVENAME flag to hold onto the
1127                                  * pathname for use later in VOP_CREATE or
1128                                  * VOP_RENAME.
1129                                  */
1130                                 cnp->cn_flags |= SAVENAME;
1131
1132                                 err = EJUSTRETURN;
1133                         }
1134                 } else {
1135                         err = ENOENT;
1136                 }
1137         } else {
1138                 /* Entry was found */
1139                 if (flags & ISDOTDOT) {
1140                         struct fuse_lookup_alloc_arg flaa;
1141
1142                         flaa.nid = nid;
1143                         flaa.feo = feo;
1144                         flaa.cnp = cnp;
1145                         flaa.vtyp = vtyp;
1146                         err = vn_vget_ino_gen(dvp, fuse_lookup_alloc, &flaa, 0,
1147                                 &vp);
1148                         *vpp = vp;
1149                 } else if (nid == VTOI(dvp)) {
1150                         vref(dvp);
1151                         *vpp = dvp;
1152                 } else {
1153                         struct fuse_vnode_data *fvdat;
1154
1155                         err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
1156                             &vp, cnp, vtyp);
1157                         if (err)
1158                                 goto out;
1159                         *vpp = vp;
1160
1161                         /*
1162                          * In the case where we are looking up a FUSE node
1163                          * represented by an existing cached vnode, and the
1164                          * true size reported by FUSE_LOOKUP doesn't match
1165                          * the vnode's cached size, then any cached writes
1166                          * beyond the file's current size are lost.
1167                          *
1168                          * We can get here:
1169                          * * following attribute cache expiration, or
1170                          * * due a bug in the daemon, or
1171                          */
1172                         fvdat = VTOFUD(vp);
1173                         if (vnode_isreg(vp) &&
1174                             filesize != fvdat->cached_attrs.va_size &&
1175                             fvdat->flag & FN_SIZECHANGE) {
1176                                 /*
1177                                  * The FN_SIZECHANGE flag reflects a dirty
1178                                  * append.  If userspace lets us know our cache
1179                                  * is invalid, that write was lost.  (Dirty
1180                                  * writes that do not cause append are also
1181                                  * lost, but we don't detect them here.)
1182                                  *
1183                                  * XXX: Maybe disable WB caching on this mount.
1184                                  */
1185                                 printf("%s: WB cache incoherent on %s!\n",
1186                                     __func__,
1187                                     vnode_mount(vp)->mnt_stat.f_mntonname);
1188
1189                                 fvdat->flag &= ~FN_SIZECHANGE;
1190                         }
1191
1192                         MPASS(feo != NULL);
1193                         fuse_internal_cache_attrs(*vpp, &feo->attr,
1194                                 feo->attr_valid, feo->attr_valid_nsec, NULL);
1195                         fuse_validity_2_bintime(feo->entry_valid,
1196                                 feo->entry_valid_nsec,
1197                                 &fvdat->entry_cache_timeout);
1198
1199                         if ((nameiop == DELETE || nameiop == RENAME) &&
1200                                 islastcn)
1201                         {
1202                                 struct vattr dvattr;
1203
1204                                 err = fuse_internal_access(dvp, VWRITE, td,
1205                                         cred);
1206                                 if (err != 0)
1207                                         goto out;
1208                                 /* 
1209                                  * if the parent's sticky bit is set, check
1210                                  * whether we're allowed to remove the file.
1211                                  * Need to figure out the vnode locking to make
1212                                  * this work.
1213                                  */
1214                                 fuse_internal_getattr(dvp, &dvattr, cred, td);
1215                                 if ((dvattr.va_mode & S_ISTXT) &&
1216                                         fuse_internal_access(dvp, VADMIN, td,
1217                                                 cred) &&
1218                                         fuse_internal_access(*vpp, VADMIN, td,
1219                                                 cred)) {
1220                                         err = EPERM;
1221                                         goto out;
1222                                 }
1223                         }
1224
1225                         if (islastcn && (
1226                                 (nameiop == DELETE) ||
1227                                 (nameiop == RENAME && wantparent))) {
1228                                 cnp->cn_flags |= SAVENAME;
1229                         }
1230
1231                 }
1232         }
1233 out:
1234         if (err) {
1235                 if (vp != NULL && dvp != vp)
1236                         vput(vp);
1237                 else if (vp != NULL)
1238                         vrele(vp);
1239                 *vpp = NULL;
1240         }
1241         if (did_lookup)
1242                 fdisp_destroy(&fdi);
1243
1244         return err;
1245 }
1246
1247 /*
1248     struct vnop_mkdir_args {
1249         struct vnode *a_dvp;
1250         struct vnode **a_vpp;
1251         struct componentname *a_cnp;
1252         struct vattr *a_vap;
1253     };
1254 */
1255 static int
1256 fuse_vnop_mkdir(struct vop_mkdir_args *ap)
1257 {
1258         struct vnode *dvp = ap->a_dvp;
1259         struct vnode **vpp = ap->a_vpp;
1260         struct componentname *cnp = ap->a_cnp;
1261         struct vattr *vap = ap->a_vap;
1262
1263         struct fuse_mkdir_in fmdi;
1264
1265         if (fuse_isdeadfs(dvp)) {
1266                 return ENXIO;
1267         }
1268         fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
1269         fmdi.umask = curthread->td_proc->p_fd->fd_cmask;
1270
1271         return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
1272             sizeof(fmdi), VDIR));
1273 }
1274
1275 /*
1276     struct vnop_mknod_args {
1277         struct vnode *a_dvp;
1278         struct vnode **a_vpp;
1279         struct componentname *a_cnp;
1280         struct vattr *a_vap;
1281     };
1282 */
1283 static int
1284 fuse_vnop_mknod(struct vop_mknod_args *ap)
1285 {
1286
1287         struct vnode *dvp = ap->a_dvp;
1288         struct vnode **vpp = ap->a_vpp;
1289         struct componentname *cnp = ap->a_cnp;
1290         struct vattr *vap = ap->a_vap;
1291
1292         if (fuse_isdeadfs(dvp))
1293                 return ENXIO;
1294
1295         return fuse_internal_mknod(dvp, vpp, cnp, vap);
1296 }
1297
1298 /*
1299     struct vop_open_args {
1300         struct vnode *a_vp;
1301         int  a_mode;
1302         struct ucred *a_cred;
1303         struct thread *a_td;
1304         int a_fdidx; / struct file *a_fp;
1305     };
1306 */
1307 static int
1308 fuse_vnop_open(struct vop_open_args *ap)
1309 {
1310         struct vnode *vp = ap->a_vp;
1311         int a_mode = ap->a_mode;
1312         struct thread *td = ap->a_td;
1313         struct ucred *cred = ap->a_cred;
1314         pid_t pid = td->td_proc->p_pid;
1315         struct fuse_vnode_data *fvdat;
1316
1317         if (fuse_isdeadfs(vp))
1318                 return ENXIO;
1319         if (vp->v_type == VCHR || vp->v_type == VBLK || vp->v_type == VFIFO)
1320                 return (EOPNOTSUPP);
1321         if ((a_mode & (FREAD | FWRITE | FEXEC)) == 0)
1322                 return EINVAL;
1323
1324         fvdat = VTOFUD(vp);
1325
1326         if (fuse_filehandle_validrw(vp, a_mode, cred, pid)) {
1327                 fuse_vnode_open(vp, 0, td);
1328                 return 0;
1329         }
1330
1331         return fuse_filehandle_open(vp, a_mode, NULL, td, cred);
1332 }
1333
1334 static int
1335 fuse_vnop_pathconf(struct vop_pathconf_args *ap)
1336 {
1337
1338         switch (ap->a_name) {
1339         case _PC_FILESIZEBITS:
1340                 *ap->a_retval = 64;
1341                 return (0);
1342         case _PC_NAME_MAX:
1343                 *ap->a_retval = NAME_MAX;
1344                 return (0);
1345         case _PC_LINK_MAX:
1346                 *ap->a_retval = MIN(LONG_MAX, FUSE_LINK_MAX);
1347                 return (0);
1348         case _PC_SYMLINK_MAX:
1349                 *ap->a_retval = MAXPATHLEN;
1350                 return (0);
1351         case _PC_NO_TRUNC:
1352                 *ap->a_retval = 1;
1353                 return (0);
1354         default:
1355                 return (vop_stdpathconf(ap));
1356         }
1357 }
1358
1359 /*
1360     struct vnop_read_args {
1361         struct vnode *a_vp;
1362         struct uio *a_uio;
1363         int  a_ioflag;
1364         struct ucred *a_cred;
1365     };
1366 */
1367 static int
1368 fuse_vnop_read(struct vop_read_args *ap)
1369 {
1370         struct vnode *vp = ap->a_vp;
1371         struct uio *uio = ap->a_uio;
1372         int ioflag = ap->a_ioflag;
1373         struct ucred *cred = ap->a_cred;
1374         pid_t pid = curthread->td_proc->p_pid;
1375
1376         if (fuse_isdeadfs(vp)) {
1377                 return ENXIO;
1378         }
1379
1380         if (VTOFUD(vp)->flag & FN_DIRECTIO) {
1381                 ioflag |= IO_DIRECT;
1382         }
1383
1384         return fuse_io_dispatch(vp, uio, ioflag, false, cred, pid);
1385 }
1386
1387 /*
1388     struct vnop_readdir_args {
1389         struct vnode *a_vp;
1390         struct uio *a_uio;
1391         struct ucred *a_cred;
1392         int *a_eofflag;
1393         int *a_ncookies;
1394         u_long **a_cookies;
1395     };
1396 */
1397 static int
1398 fuse_vnop_readdir(struct vop_readdir_args *ap)
1399 {
1400         struct vnode *vp = ap->a_vp;
1401         struct uio *uio = ap->a_uio;
1402         struct ucred *cred = ap->a_cred;
1403         struct fuse_filehandle *fufh = NULL;
1404         struct fuse_iov cookediov;
1405         int err = 0;
1406         u_long *cookies;
1407         off_t startoff;
1408         ssize_t tresid;
1409         int ncookies;
1410         bool closefufh = false;
1411         pid_t pid = curthread->td_proc->p_pid;
1412
1413         if (ap->a_eofflag)
1414                 *ap->a_eofflag = 0;
1415         if (fuse_isdeadfs(vp)) {
1416                 return ENXIO;
1417         }
1418         if (                            /* XXXIP ((uio_iovcnt(uio) > 1)) || */
1419             (uio_resid(uio) < sizeof(struct dirent))) {
1420                 return EINVAL;
1421         }
1422
1423         tresid = uio->uio_resid;
1424         startoff = uio->uio_offset;
1425         err = fuse_filehandle_get_dir(vp, &fufh, cred, pid);
1426         if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
1427                 /* 
1428                  * nfsd will do VOP_READDIR without first doing VOP_OPEN.  We
1429                  * must implicitly open the directory here
1430                  */
1431                 err = fuse_filehandle_open(vp, FREAD, &fufh, curthread, cred);
1432                 if (err == 0) {
1433                         /*
1434                          * When a directory is opened, it must be read from
1435                          * the beginning.  Hopefully, the "startoff" still
1436                          * exists as an offset cookie for the directory.
1437                          * If not, it will read the entire directory without
1438                          * returning any entries and just return eof.
1439                          */
1440                         uio->uio_offset = 0;
1441                 }
1442                 closefufh = true;
1443         }
1444         if (err)
1445                 return (err);
1446         if (ap->a_ncookies != NULL) {
1447                 ncookies = uio->uio_resid /
1448                         (offsetof(struct dirent, d_name) + 4) + 1;
1449                 cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
1450                 *ap->a_ncookies = ncookies;
1451                 *ap->a_cookies = cookies;
1452         } else {
1453                 ncookies = 0;
1454                 cookies = NULL;
1455         }
1456 #define DIRCOOKEDSIZE FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + MAXNAMLEN + 1)
1457         fiov_init(&cookediov, DIRCOOKEDSIZE);
1458
1459         err = fuse_internal_readdir(vp, uio, startoff, fufh, &cookediov,
1460                 &ncookies, cookies);
1461
1462         fiov_teardown(&cookediov);
1463         if (closefufh)
1464                 fuse_filehandle_close(vp, fufh, curthread, cred);
1465
1466         if (ap->a_ncookies != NULL) {
1467                 if (err == 0) {
1468                         *ap->a_ncookies -= ncookies;
1469                 } else {
1470                         free(*ap->a_cookies, M_TEMP);
1471                         *ap->a_ncookies = 0;
1472                         *ap->a_cookies = NULL;
1473                 }
1474         }
1475         if (err == 0 && tresid == uio->uio_resid)
1476                 *ap->a_eofflag = 1;
1477
1478         return err;
1479 }
1480
1481 /*
1482     struct vnop_readlink_args {
1483         struct vnode *a_vp;
1484         struct uio *a_uio;
1485         struct ucred *a_cred;
1486     };
1487 */
1488 static int
1489 fuse_vnop_readlink(struct vop_readlink_args *ap)
1490 {
1491         struct vnode *vp = ap->a_vp;
1492         struct uio *uio = ap->a_uio;
1493         struct ucred *cred = ap->a_cred;
1494
1495         struct fuse_dispatcher fdi;
1496         int err;
1497
1498         if (fuse_isdeadfs(vp)) {
1499                 return ENXIO;
1500         }
1501         if (!vnode_islnk(vp)) {
1502                 return EINVAL;
1503         }
1504         fdisp_init(&fdi, 0);
1505         err = fdisp_simple_putget_vp(&fdi, FUSE_READLINK, vp, curthread, cred);
1506         if (err) {
1507                 goto out;
1508         }
1509         if (((char *)fdi.answ)[0] == '/' &&
1510             fuse_get_mpdata(vnode_mount(vp))->dataflags & FSESS_PUSH_SYMLINKS_IN) {
1511                 char *mpth = vnode_mount(vp)->mnt_stat.f_mntonname;
1512
1513                 err = uiomove(mpth, strlen(mpth), uio);
1514         }
1515         if (!err) {
1516                 err = uiomove(fdi.answ, fdi.iosize, uio);
1517         }
1518 out:
1519         fdisp_destroy(&fdi);
1520         return err;
1521 }
1522
1523 /*
1524     struct vnop_reclaim_args {
1525         struct vnode *a_vp;
1526         struct thread *a_td;
1527     };
1528 */
1529 static int
1530 fuse_vnop_reclaim(struct vop_reclaim_args *ap)
1531 {
1532         struct vnode *vp = ap->a_vp;
1533         struct thread *td = ap->a_td;
1534         struct fuse_vnode_data *fvdat = VTOFUD(vp);
1535         struct fuse_filehandle *fufh, *fufh_tmp;
1536
1537         if (!fvdat) {
1538                 panic("FUSE: no vnode data during recycling");
1539         }
1540         LIST_FOREACH_SAFE(fufh, &fvdat->handles, next, fufh_tmp) {
1541                 printf("FUSE: vnode being reclaimed with open fufh "
1542                         "(type=%#x)", fufh->fufh_type);
1543                 fuse_filehandle_close(vp, fufh, td, NULL);
1544         }
1545
1546         if ((!fuse_isdeadfs(vp)) && (fvdat->nlookup)) {
1547                 fuse_internal_forget_send(vnode_mount(vp), td, NULL, VTOI(vp),
1548                     fvdat->nlookup);
1549         }
1550         fuse_vnode_setparent(vp, NULL);
1551         cache_purge(vp);
1552         vfs_hash_remove(vp);
1553         vnode_destroy_vobject(vp);
1554         fuse_vnode_destroy(vp);
1555
1556         return 0;
1557 }
1558
1559 /*
1560     struct vnop_remove_args {
1561         struct vnode *a_dvp;
1562         struct vnode *a_vp;
1563         struct componentname *a_cnp;
1564     };
1565 */
1566 static int
1567 fuse_vnop_remove(struct vop_remove_args *ap)
1568 {
1569         struct vnode *dvp = ap->a_dvp;
1570         struct vnode *vp = ap->a_vp;
1571         struct componentname *cnp = ap->a_cnp;
1572
1573         int err;
1574
1575         if (fuse_isdeadfs(vp)) {
1576                 return ENXIO;
1577         }
1578         if (vnode_isdir(vp)) {
1579                 return EPERM;
1580         }
1581         cache_purge(vp);
1582
1583         err = fuse_internal_remove(dvp, vp, cnp, FUSE_UNLINK);
1584
1585         if (err == 0) {
1586                 fuse_internal_vnode_disappear(vp);
1587                 /* 
1588                  * Purge the parent's attribute cache because the daemon
1589                  * should've updated its mtime and ctime
1590                  */
1591                 fuse_vnode_clear_attr_cache(dvp);
1592         }
1593         return err;
1594 }
1595
1596 /*
1597     struct vnop_rename_args {
1598         struct vnode *a_fdvp;
1599         struct vnode *a_fvp;
1600         struct componentname *a_fcnp;
1601         struct vnode *a_tdvp;
1602         struct vnode *a_tvp;
1603         struct componentname *a_tcnp;
1604     };
1605 */
1606 static int
1607 fuse_vnop_rename(struct vop_rename_args *ap)
1608 {
1609         struct vnode *fdvp = ap->a_fdvp;
1610         struct vnode *fvp = ap->a_fvp;
1611         struct componentname *fcnp = ap->a_fcnp;
1612         struct vnode *tdvp = ap->a_tdvp;
1613         struct vnode *tvp = ap->a_tvp;
1614         struct componentname *tcnp = ap->a_tcnp;
1615         struct fuse_data *data;
1616         bool newparent = fdvp != tdvp;
1617         bool isdir = fvp->v_type == VDIR;
1618         int err = 0;
1619
1620         if (fuse_isdeadfs(fdvp)) {
1621                 return ENXIO;
1622         }
1623         if (fvp->v_mount != tdvp->v_mount ||
1624             (tvp && fvp->v_mount != tvp->v_mount)) {
1625                 SDT_PROBE2(fusefs, , vnops, trace, 1, "cross-device rename");
1626                 err = EXDEV;
1627                 goto out;
1628         }
1629         cache_purge(fvp);
1630
1631         /*
1632          * FUSE library is expected to check if target directory is not
1633          * under the source directory in the file system tree.
1634          * Linux performs this check at VFS level.
1635          */
1636         /* 
1637          * If source is a directory, and it will get a new parent, user must
1638          * have write permission to it, so ".." can be modified.
1639          */
1640         data = fuse_get_mpdata(vnode_mount(tdvp));
1641         if (data->dataflags & FSESS_DEFAULT_PERMISSIONS && isdir && newparent) {
1642                 err = fuse_internal_access(fvp, VWRITE,
1643                         tcnp->cn_thread, tcnp->cn_cred);
1644                 if (err)
1645                         goto out;
1646         }
1647         sx_xlock(&data->rename_lock);
1648         err = fuse_internal_rename(fdvp, fcnp, tdvp, tcnp);
1649         if (err == 0) {
1650                 if (tdvp != fdvp)
1651                         fuse_vnode_setparent(fvp, tdvp);
1652                 if (tvp != NULL)
1653                         fuse_vnode_setparent(tvp, NULL);
1654         }
1655         sx_unlock(&data->rename_lock);
1656
1657         if (tvp != NULL && tvp != fvp) {
1658                 cache_purge(tvp);
1659         }
1660         if (vnode_isdir(fvp)) {
1661                 if ((tvp != NULL) && vnode_isdir(tvp)) {
1662                         cache_purge(tdvp);
1663                 }
1664                 cache_purge(fdvp);
1665         }
1666 out:
1667         if (tdvp == tvp) {
1668                 vrele(tdvp);
1669         } else {
1670                 vput(tdvp);
1671         }
1672         if (tvp != NULL) {
1673                 vput(tvp);
1674         }
1675         vrele(fdvp);
1676         vrele(fvp);
1677
1678         return err;
1679 }
1680
1681 /*
1682     struct vnop_rmdir_args {
1683             struct vnode *a_dvp;
1684             struct vnode *a_vp;
1685             struct componentname *a_cnp;
1686     } *ap;
1687 */
1688 static int
1689 fuse_vnop_rmdir(struct vop_rmdir_args *ap)
1690 {
1691         struct vnode *dvp = ap->a_dvp;
1692         struct vnode *vp = ap->a_vp;
1693
1694         int err;
1695
1696         if (fuse_isdeadfs(vp)) {
1697                 return ENXIO;
1698         }
1699         if (VTOFUD(vp) == VTOFUD(dvp)) {
1700                 return EINVAL;
1701         }
1702         err = fuse_internal_remove(dvp, vp, ap->a_cnp, FUSE_RMDIR);
1703
1704         if (err == 0) {
1705                 fuse_internal_vnode_disappear(vp);
1706                 /* 
1707                  * Purge the parent's attribute cache because the daemon
1708                  * should've updated its mtime and ctime
1709                  */
1710                 fuse_vnode_clear_attr_cache(dvp);
1711         }
1712         return err;
1713 }
1714
1715 /*
1716     struct vnop_setattr_args {
1717         struct vnode *a_vp;
1718         struct vattr *a_vap;
1719         struct ucred *a_cred;
1720         struct thread *a_td;
1721     };
1722 */
1723 static int
1724 fuse_vnop_setattr(struct vop_setattr_args *ap)
1725 {
1726         struct vnode *vp = ap->a_vp;
1727         struct vattr *vap = ap->a_vap;
1728         struct ucred *cred = ap->a_cred;
1729         struct thread *td = curthread;
1730         struct mount *mp;
1731         struct fuse_data *data;
1732         struct vattr old_va;
1733         int dataflags;
1734         int err = 0, err2;
1735         accmode_t accmode = 0;
1736         bool checkperm;
1737         bool drop_suid = false;
1738         gid_t cr_gid;
1739
1740         mp = vnode_mount(vp);
1741         data = fuse_get_mpdata(mp);
1742         dataflags = data->dataflags;
1743         checkperm = dataflags & FSESS_DEFAULT_PERMISSIONS;
1744         if (cred->cr_ngroups > 0)
1745                 cr_gid = cred->cr_groups[0];
1746         else
1747                 cr_gid = 0;
1748
1749         if (fuse_isdeadfs(vp)) {
1750                 return ENXIO;
1751         }
1752
1753         if (vap->va_uid != (uid_t)VNOVAL) {
1754                 if (checkperm) {
1755                         /* Only root may change a file's owner */
1756                         err = priv_check_cred(cred, PRIV_VFS_CHOWN);
1757                         if (err) {
1758                                 /* As a special case, allow the null chown */
1759                                 err2 = fuse_internal_getattr(vp, &old_va, cred,
1760                                         td);
1761                                 if (err2)
1762                                         return (err2);
1763                                 if (vap->va_uid != old_va.va_uid)
1764                                         return err;
1765                                 else
1766                                         accmode |= VADMIN;
1767                                 drop_suid = true;
1768                         } else
1769                                 accmode |= VADMIN;
1770                 } else
1771                         accmode |= VADMIN;
1772         }
1773         if (vap->va_gid != (gid_t)VNOVAL) {
1774                 if (checkperm && priv_check_cred(cred, PRIV_VFS_CHOWN))
1775                         drop_suid = true;
1776                 if (checkperm && !groupmember(vap->va_gid, cred))
1777                 {
1778                         /*
1779                          * Non-root users may only chgrp to one of their own
1780                          * groups 
1781                          */
1782                         err = priv_check_cred(cred, PRIV_VFS_CHOWN);
1783                         if (err) {
1784                                 /* As a special case, allow the null chgrp */
1785                                 err2 = fuse_internal_getattr(vp, &old_va, cred,
1786                                         td);
1787                                 if (err2)
1788                                         return (err2);
1789                                 if (vap->va_gid != old_va.va_gid)
1790                                         return err;
1791                                 accmode |= VADMIN;
1792                         } else
1793                                 accmode |= VADMIN;
1794                 } else
1795                         accmode |= VADMIN;
1796         }
1797         if (vap->va_size != VNOVAL) {
1798                 switch (vp->v_type) {
1799                 case VDIR:
1800                         return (EISDIR);
1801                 case VLNK:
1802                 case VREG:
1803                         if (vfs_isrdonly(mp))
1804                                 return (EROFS);
1805                         break;
1806                 default:
1807                         /*
1808                          * According to POSIX, the result is unspecified
1809                          * for file types other than regular files,
1810                          * directories and shared memory objects.  We
1811                          * don't support shared memory objects in the file
1812                          * system, and have dubious support for truncating
1813                          * symlinks.  Just ignore the request in other cases.
1814                          */
1815                         return (0);
1816                 }
1817                 /* Don't set accmode.  Permission to trunc is checked upstack */
1818         }
1819         if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
1820                 if (vap->va_vaflags & VA_UTIMES_NULL)
1821                         accmode |= VWRITE;
1822                 else
1823                         accmode |= VADMIN;
1824         }
1825         if (drop_suid) {
1826                 if (vap->va_mode != (mode_t)VNOVAL)
1827                         vap->va_mode &= ~(S_ISUID | S_ISGID);
1828                 else {
1829                         err = fuse_internal_getattr(vp, &old_va, cred, td);
1830                         if (err)
1831                                 return (err);
1832                         vap->va_mode = old_va.va_mode & ~(S_ISUID | S_ISGID);
1833                 }
1834         }
1835         if (vap->va_mode != (mode_t)VNOVAL) {
1836                 /* Only root may set the sticky bit on non-directories */
1837                 if (checkperm && vp->v_type != VDIR && (vap->va_mode & S_ISTXT)
1838                     && priv_check_cred(cred, PRIV_VFS_STICKYFILE))
1839                         return EFTYPE;
1840                 if (checkperm && (vap->va_mode & S_ISGID)) {
1841                         err = fuse_internal_getattr(vp, &old_va, cred, td);
1842                         if (err)
1843                                 return (err);
1844                         if (!groupmember(old_va.va_gid, cred)) {
1845                                 err = priv_check_cred(cred, PRIV_VFS_SETGID);
1846                                 if (err)
1847                                         return (err);
1848                         }
1849                 }
1850                 accmode |= VADMIN;
1851         }
1852
1853         if (vfs_isrdonly(mp))
1854                 return EROFS;
1855
1856         err = fuse_internal_access(vp, accmode, td, cred);
1857         if (err)
1858                 return err;
1859         else
1860                 return fuse_internal_setattr(vp, vap, td, cred);
1861 }
1862
1863 /*
1864     struct vnop_strategy_args {
1865         struct vnode *a_vp;
1866         struct buf *a_bp;
1867     };
1868 */
1869 static int
1870 fuse_vnop_strategy(struct vop_strategy_args *ap)
1871 {
1872         struct vnode *vp = ap->a_vp;
1873         struct buf *bp = ap->a_bp;
1874
1875         if (!vp || fuse_isdeadfs(vp)) {
1876                 bp->b_ioflags |= BIO_ERROR;
1877                 bp->b_error = ENXIO;
1878                 bufdone(bp);
1879                 return 0;
1880         }
1881
1882         /*
1883          * VOP_STRATEGY always returns zero and signals error via bp->b_ioflags.
1884          * fuse_io_strategy sets bp's error fields
1885          */
1886         (void)fuse_io_strategy(vp, bp);
1887
1888         return 0;
1889 }
1890
1891
1892 /*
1893     struct vnop_symlink_args {
1894         struct vnode *a_dvp;
1895         struct vnode **a_vpp;
1896         struct componentname *a_cnp;
1897         struct vattr *a_vap;
1898         char *a_target;
1899     };
1900 */
1901 static int
1902 fuse_vnop_symlink(struct vop_symlink_args *ap)
1903 {
1904         struct vnode *dvp = ap->a_dvp;
1905         struct vnode **vpp = ap->a_vpp;
1906         struct componentname *cnp = ap->a_cnp;
1907         const char *target = ap->a_target;
1908
1909         struct fuse_dispatcher fdi;
1910
1911         int err;
1912         size_t len;
1913
1914         if (fuse_isdeadfs(dvp)) {
1915                 return ENXIO;
1916         }
1917         /*
1918          * Unlike the other creator type calls, here we have to create a message
1919          * where the name of the new entry comes first, and the data describing
1920          * the entry comes second.
1921          * Hence we can't rely on our handy fuse_internal_newentry() routine,
1922          * but put together the message manually and just call the core part.
1923          */
1924
1925         len = strlen(target) + 1;
1926         fdisp_init(&fdi, len + cnp->cn_namelen + 1);
1927         fdisp_make_vp(&fdi, FUSE_SYMLINK, dvp, curthread, NULL);
1928
1929         memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
1930         ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
1931         memcpy((char *)fdi.indata + cnp->cn_namelen + 1, target, len);
1932
1933         err = fuse_internal_newentry_core(dvp, vpp, cnp, VLNK, &fdi);
1934         fdisp_destroy(&fdi);
1935         return err;
1936 }
1937
1938 /*
1939     struct vnop_write_args {
1940         struct vnode *a_vp;
1941         struct uio *a_uio;
1942         int  a_ioflag;
1943         struct ucred *a_cred;
1944     };
1945 */
1946 static int
1947 fuse_vnop_write(struct vop_write_args *ap)
1948 {
1949         struct vnode *vp = ap->a_vp;
1950         struct uio *uio = ap->a_uio;
1951         int ioflag = ap->a_ioflag;
1952         struct ucred *cred = ap->a_cred;
1953         pid_t pid = curthread->td_proc->p_pid;
1954
1955         if (fuse_isdeadfs(vp)) {
1956                 return ENXIO;
1957         }
1958
1959         if (VTOFUD(vp)->flag & FN_DIRECTIO) {
1960                 ioflag |= IO_DIRECT;
1961         }
1962
1963         return fuse_io_dispatch(vp, uio, ioflag, false, cred, pid);
1964 }
1965
1966 SDT_PROBE_DEFINE1(fusefs, , vnops, vnop_getpages_error, "int");
1967 /*
1968     struct vnop_getpages_args {
1969         struct vnode *a_vp;
1970         vm_page_t *a_m;
1971         int a_count;
1972         int a_reqpage;
1973     };
1974 */
1975 static int
1976 fuse_vnop_getpages(struct vop_getpages_args *ap)
1977 {
1978         int i, error, nextoff, size, toff, count, npages;
1979         struct uio uio;
1980         struct iovec iov;
1981         vm_offset_t kva;
1982         struct buf *bp;
1983         struct vnode *vp;
1984         struct thread *td;
1985         struct ucred *cred;
1986         vm_page_t *pages;
1987         pid_t pid = curthread->td_proc->p_pid;
1988
1989         vp = ap->a_vp;
1990         KASSERT(vp->v_object, ("objectless vp passed to getpages"));
1991         td = curthread;                 /* XXX */
1992         cred = curthread->td_ucred;     /* XXX */
1993         pages = ap->a_m;
1994         npages = ap->a_count;
1995
1996         if (!fsess_opt_mmap(vnode_mount(vp))) {
1997                 SDT_PROBE2(fusefs, , vnops, trace, 1,
1998                         "called on non-cacheable vnode??\n");
1999                 return (VM_PAGER_ERROR);
2000         }
2001
2002         /*
2003          * If the last page is partially valid, just return it and allow
2004          * the pager to zero-out the blanks.  Partially valid pages can
2005          * only occur at the file EOF.
2006          *
2007          * XXXGL: is that true for FUSE, which is a local filesystem,
2008          * but still somewhat disconnected from the kernel?
2009          */
2010         VM_OBJECT_WLOCK(vp->v_object);
2011         if (pages[npages - 1]->valid != 0 && --npages == 0)
2012                 goto out;
2013         VM_OBJECT_WUNLOCK(vp->v_object);
2014
2015         /*
2016          * We use only the kva address for the buffer, but this is extremely
2017          * convenient and fast.
2018          */
2019         bp = uma_zalloc(fuse_pbuf_zone, M_WAITOK);
2020
2021         kva = (vm_offset_t)bp->b_data;
2022         pmap_qenter(kva, pages, npages);
2023         VM_CNT_INC(v_vnodein);
2024         VM_CNT_ADD(v_vnodepgsin, npages);
2025
2026         count = npages << PAGE_SHIFT;
2027         iov.iov_base = (caddr_t)kva;
2028         iov.iov_len = count;
2029         uio.uio_iov = &iov;
2030         uio.uio_iovcnt = 1;
2031         uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
2032         uio.uio_resid = count;
2033         uio.uio_segflg = UIO_SYSSPACE;
2034         uio.uio_rw = UIO_READ;
2035         uio.uio_td = td;
2036
2037         error = fuse_io_dispatch(vp, &uio, IO_DIRECT, true, cred, pid);
2038         pmap_qremove(kva, npages);
2039
2040         uma_zfree(fuse_pbuf_zone, bp);
2041
2042         if (error && (uio.uio_resid == count)) {
2043                 SDT_PROBE1(fusefs, , vnops, vnop_getpages_error, error);
2044                 return VM_PAGER_ERROR;
2045         }
2046         /*
2047          * Calculate the number of bytes read and validate only that number
2048          * of bytes.  Note that due to pending writes, size may be 0.  This
2049          * does not mean that the remaining data is invalid!
2050          */
2051
2052         size = count - uio.uio_resid;
2053         VM_OBJECT_WLOCK(vp->v_object);
2054         fuse_vm_page_lock_queues();
2055         for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
2056                 vm_page_t m;
2057
2058                 nextoff = toff + PAGE_SIZE;
2059                 m = pages[i];
2060
2061                 if (nextoff <= size) {
2062                         /*
2063                          * Read operation filled an entire page
2064                          */
2065                         m->valid = VM_PAGE_BITS_ALL;
2066                         KASSERT(m->dirty == 0,
2067                             ("fuse_getpages: page %p is dirty", m));
2068                 } else if (size > toff) {
2069                         /*
2070                          * Read operation filled a partial page.
2071                          */
2072                         m->valid = 0;
2073                         vm_page_set_valid_range(m, 0, size - toff);
2074                         KASSERT(m->dirty == 0,
2075                             ("fuse_getpages: page %p is dirty", m));
2076                 } else {
2077                         /*
2078                          * Read operation was short.  If no error occurred
2079                          * we may have hit a zero-fill section.   We simply
2080                          * leave valid set to 0.
2081                          */
2082                         ;
2083                 }
2084         }
2085         fuse_vm_page_unlock_queues();
2086 out:
2087         VM_OBJECT_WUNLOCK(vp->v_object);
2088         if (ap->a_rbehind)
2089                 *ap->a_rbehind = 0;
2090         if (ap->a_rahead)
2091                 *ap->a_rahead = 0;
2092         return (VM_PAGER_OK);
2093 }
2094
2095 /*
2096     struct vnop_putpages_args {
2097         struct vnode *a_vp;
2098         vm_page_t *a_m;
2099         int a_count;
2100         int a_sync;
2101         int *a_rtvals;
2102         vm_ooffset_t a_offset;
2103     };
2104 */
2105 static int
2106 fuse_vnop_putpages(struct vop_putpages_args *ap)
2107 {
2108         struct uio uio;
2109         struct iovec iov;
2110         vm_offset_t kva;
2111         struct buf *bp;
2112         int i, error, npages, count;
2113         off_t offset;
2114         int *rtvals;
2115         struct vnode *vp;
2116         struct thread *td;
2117         struct ucred *cred;
2118         vm_page_t *pages;
2119         vm_ooffset_t fsize;
2120         pid_t pid = curthread->td_proc->p_pid;
2121
2122         vp = ap->a_vp;
2123         KASSERT(vp->v_object, ("objectless vp passed to putpages"));
2124         fsize = vp->v_object->un_pager.vnp.vnp_size;
2125         td = curthread;                 /* XXX */
2126         cred = curthread->td_ucred;     /* XXX */
2127         pages = ap->a_m;
2128         count = ap->a_count;
2129         rtvals = ap->a_rtvals;
2130         npages = btoc(count);
2131         offset = IDX_TO_OFF(pages[0]->pindex);
2132
2133         if (!fsess_opt_mmap(vnode_mount(vp))) {
2134                 SDT_PROBE2(fusefs, , vnops, trace, 1,
2135                         "called on non-cacheable vnode??\n");
2136         }
2137         for (i = 0; i < npages; i++)
2138                 rtvals[i] = VM_PAGER_AGAIN;
2139
2140         /*
2141          * When putting pages, do not extend file past EOF.
2142          */
2143
2144         if (offset + count > fsize) {
2145                 count = fsize - offset;
2146                 if (count < 0)
2147                         count = 0;
2148         }
2149         /*
2150          * We use only the kva address for the buffer, but this is extremely
2151          * convenient and fast.
2152          */
2153         bp = uma_zalloc(fuse_pbuf_zone, M_WAITOK);
2154
2155         kva = (vm_offset_t)bp->b_data;
2156         pmap_qenter(kva, pages, npages);
2157         VM_CNT_INC(v_vnodeout);
2158         VM_CNT_ADD(v_vnodepgsout, count);
2159
2160         iov.iov_base = (caddr_t)kva;
2161         iov.iov_len = count;
2162         uio.uio_iov = &iov;
2163         uio.uio_iovcnt = 1;
2164         uio.uio_offset = offset;
2165         uio.uio_resid = count;
2166         uio.uio_segflg = UIO_SYSSPACE;
2167         uio.uio_rw = UIO_WRITE;
2168         uio.uio_td = td;
2169
2170         error = fuse_io_dispatch(vp, &uio, IO_DIRECT, true, cred, pid);
2171
2172         pmap_qremove(kva, npages);
2173         uma_zfree(fuse_pbuf_zone, bp);
2174
2175         if (!error) {
2176                 int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE;
2177
2178                 for (i = 0; i < nwritten; i++) {
2179                         rtvals[i] = VM_PAGER_OK;
2180                         VM_OBJECT_WLOCK(pages[i]->object);
2181                         vm_page_undirty(pages[i]);
2182                         VM_OBJECT_WUNLOCK(pages[i]->object);
2183                 }
2184         }
2185         return rtvals[0];
2186 }
2187
2188 static const char extattr_namespace_separator = '.';
2189
2190 /*
2191     struct vop_getextattr_args {
2192         struct vop_generic_args a_gen;
2193         struct vnode *a_vp;
2194         int a_attrnamespace;
2195         const char *a_name;
2196         struct uio *a_uio;
2197         size_t *a_size;
2198         struct ucred *a_cred;
2199         struct thread *a_td;
2200     };
2201 */
2202 static int
2203 fuse_vnop_getextattr(struct vop_getextattr_args *ap)
2204 {
2205         struct vnode *vp = ap->a_vp;
2206         struct uio *uio = ap->a_uio;
2207         struct fuse_dispatcher fdi;
2208         struct fuse_getxattr_in *get_xattr_in;
2209         struct fuse_getxattr_out *get_xattr_out;
2210         struct mount *mp = vnode_mount(vp);
2211         struct thread *td = ap->a_td;
2212         struct ucred *cred = ap->a_cred;
2213         char *prefix;
2214         char *attr_str;
2215         size_t len;
2216         int err;
2217
2218         if (fuse_isdeadfs(vp))
2219                 return (ENXIO);
2220
2221         if (!fsess_isimpl(mp, FUSE_GETXATTR))
2222                 return EOPNOTSUPP;
2223
2224         err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
2225         if (err)
2226                 return err;
2227
2228         /* Default to looking for user attributes. */
2229         if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
2230                 prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
2231         else
2232                 prefix = EXTATTR_NAMESPACE_USER_STRING;
2233
2234         len = strlen(prefix) + sizeof(extattr_namespace_separator) +
2235             strlen(ap->a_name) + 1;
2236
2237         fdisp_init(&fdi, len + sizeof(*get_xattr_in));
2238         fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, td, cred);
2239
2240         get_xattr_in = fdi.indata;
2241         /*
2242          * Check to see whether we're querying the available size or
2243          * issuing the actual request.  If we pass in 0, we get back struct
2244          * fuse_getxattr_out.  If we pass in a non-zero size, we get back
2245          * that much data, without the struct fuse_getxattr_out header.
2246          */
2247         if (uio == NULL)
2248                 get_xattr_in->size = 0;
2249         else
2250                 get_xattr_in->size = uio->uio_resid;
2251
2252         attr_str = (char *)fdi.indata + sizeof(*get_xattr_in);
2253         snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
2254             ap->a_name);
2255
2256         err = fdisp_wait_answ(&fdi);
2257         if (err != 0) {
2258                 if (err == ENOSYS) {
2259                         fsess_set_notimpl(mp, FUSE_GETXATTR);
2260                         err = EOPNOTSUPP;
2261                 }
2262                 goto out;
2263         }
2264
2265         get_xattr_out = fdi.answ;
2266
2267         if (ap->a_size != NULL)
2268                 *ap->a_size = get_xattr_out->size;
2269
2270         if (uio != NULL)
2271                 err = uiomove(fdi.answ, fdi.iosize, uio);
2272
2273 out:
2274         fdisp_destroy(&fdi);
2275         return (err);
2276 }
2277
2278 /*
2279     struct vop_setextattr_args {
2280         struct vop_generic_args a_gen;
2281         struct vnode *a_vp;
2282         int a_attrnamespace;
2283         const char *a_name;
2284         struct uio *a_uio;
2285         struct ucred *a_cred;
2286         struct thread *a_td;
2287     };
2288 */
2289 static int
2290 fuse_vnop_setextattr(struct vop_setextattr_args *ap)
2291 {
2292         struct vnode *vp = ap->a_vp;
2293         struct uio *uio = ap->a_uio;
2294         struct fuse_dispatcher fdi;
2295         struct fuse_setxattr_in *set_xattr_in;
2296         struct mount *mp = vnode_mount(vp);
2297         struct thread *td = ap->a_td;
2298         struct ucred *cred = ap->a_cred;
2299         char *prefix;
2300         size_t len;
2301         char *attr_str;
2302         int err;
2303         
2304         if (fuse_isdeadfs(vp))
2305                 return (ENXIO);
2306
2307         if (!fsess_isimpl(mp, FUSE_SETXATTR))
2308                 return EOPNOTSUPP;
2309
2310         if (vfs_isrdonly(mp))
2311                 return EROFS;
2312
2313         /* Deleting xattrs must use VOP_DELETEEXTATTR instead */
2314         if (ap->a_uio == NULL) {
2315                 /*
2316                  * If we got here as fallback from VOP_DELETEEXTATTR, then
2317                  * return EOPNOTSUPP.
2318                  */
2319                 if (!fsess_isimpl(mp, FUSE_REMOVEXATTR))
2320                         return (EOPNOTSUPP);
2321                 else
2322                         return (EINVAL);
2323         }
2324
2325         err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td,
2326                 VWRITE);
2327         if (err)
2328                 return err;
2329
2330         /* Default to looking for user attributes. */
2331         if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
2332                 prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
2333         else
2334                 prefix = EXTATTR_NAMESPACE_USER_STRING;
2335
2336         len = strlen(prefix) + sizeof(extattr_namespace_separator) +
2337             strlen(ap->a_name) + 1;
2338
2339         fdisp_init(&fdi, len + sizeof(*set_xattr_in) + uio->uio_resid);
2340         fdisp_make_vp(&fdi, FUSE_SETXATTR, vp, td, cred);
2341
2342         set_xattr_in = fdi.indata;
2343         set_xattr_in->size = uio->uio_resid;
2344
2345         attr_str = (char *)fdi.indata + sizeof(*set_xattr_in);
2346         snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
2347             ap->a_name);
2348
2349         err = uiomove((char *)fdi.indata + sizeof(*set_xattr_in) + len,
2350             uio->uio_resid, uio);
2351         if (err != 0) {
2352                 goto out;
2353         }
2354
2355         err = fdisp_wait_answ(&fdi);
2356
2357         if (err == ENOSYS) {
2358                 fsess_set_notimpl(mp, FUSE_SETXATTR);
2359                 err = EOPNOTSUPP;
2360         }
2361         if (err == ERESTART) {
2362                 /* Can't restart after calling uiomove */
2363                 err = EINTR;
2364         }
2365
2366 out:
2367         fdisp_destroy(&fdi);
2368         return (err);
2369 }
2370
2371 /*
2372  * The Linux / FUSE extended attribute list is simply a collection of
2373  * NUL-terminated strings.  The FreeBSD extended attribute list is a single
2374  * byte length followed by a non-NUL terminated string.  So, this allows
2375  * conversion of the Linux / FUSE format to the FreeBSD format in place.
2376  * Linux attribute names are reported with the namespace as a prefix (e.g.
2377  * "user.attribute_name"), but in FreeBSD they are reported without the
2378  * namespace prefix (e.g. "attribute_name").  So, we're going from:
2379  *
2380  * user.attr_name1\0user.attr_name2\0
2381  *
2382  * to:
2383  *
2384  * <num>attr_name1<num>attr_name2
2385  *
2386  * Where "<num>" is a single byte number of characters in the attribute name.
2387  * 
2388  * Args:
2389  * prefix - exattr namespace prefix string
2390  * list, list_len - input list with namespace prefixes
2391  * bsd_list, bsd_list_len - output list compatible with bsd vfs
2392  */
2393 static int
2394 fuse_xattrlist_convert(char *prefix, const char *list, int list_len,
2395     char *bsd_list, int *bsd_list_len)
2396 {
2397         int len, pos, dist_to_next, prefix_len;
2398
2399         pos = 0;
2400         *bsd_list_len = 0;
2401         prefix_len = strlen(prefix);
2402
2403         while (pos < list_len && list[pos] != '\0') {
2404                 dist_to_next = strlen(&list[pos]) + 1;
2405                 if (bcmp(&list[pos], prefix, prefix_len) == 0 &&
2406                     list[pos + prefix_len] == extattr_namespace_separator) {
2407                         len = dist_to_next -
2408                             (prefix_len + sizeof(extattr_namespace_separator)) - 1;
2409                         if (len >= EXTATTR_MAXNAMELEN)
2410                                 return (ENAMETOOLONG);
2411
2412                         bsd_list[*bsd_list_len] = len;
2413                         memcpy(&bsd_list[*bsd_list_len + 1],
2414                             &list[pos + prefix_len +
2415                             sizeof(extattr_namespace_separator)], len);
2416
2417                         *bsd_list_len += len + 1;
2418                 }
2419
2420                 pos += dist_to_next;
2421         }
2422
2423         return (0);
2424 }
2425
2426 /*
2427     struct vop_listextattr_args {
2428         struct vop_generic_args a_gen;
2429         struct vnode *a_vp;
2430         int a_attrnamespace;
2431         struct uio *a_uio;
2432         size_t *a_size;
2433         struct ucred *a_cred;
2434         struct thread *a_td;
2435     };
2436 */
2437 static int
2438 fuse_vnop_listextattr(struct vop_listextattr_args *ap)
2439 {
2440         struct vnode *vp = ap->a_vp;
2441         struct uio *uio = ap->a_uio;
2442         struct fuse_dispatcher fdi;
2443         struct fuse_listxattr_in *list_xattr_in;
2444         struct fuse_listxattr_out *list_xattr_out;
2445         struct mount *mp = vnode_mount(vp);
2446         struct thread *td = ap->a_td;
2447         struct ucred *cred = ap->a_cred;
2448         size_t len;
2449         char *prefix;
2450         char *attr_str;
2451         char *bsd_list = NULL;
2452         char *linux_list;
2453         int bsd_list_len;
2454         int linux_list_len;
2455         int err;
2456
2457         if (fuse_isdeadfs(vp))
2458                 return (ENXIO);
2459
2460         if (!fsess_isimpl(mp, FUSE_LISTXATTR))
2461                 return EOPNOTSUPP;
2462
2463         err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
2464         if (err)
2465                 return err;
2466
2467         /*
2468          * Add space for a NUL and the period separator if enabled.
2469          * Default to looking for user attributes.
2470          */
2471         if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
2472                 prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
2473         else
2474                 prefix = EXTATTR_NAMESPACE_USER_STRING;
2475
2476         len = strlen(prefix) + sizeof(extattr_namespace_separator) + 1;
2477
2478         fdisp_init(&fdi, sizeof(*list_xattr_in) + len);
2479         fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
2480
2481         /*
2482          * Retrieve Linux / FUSE compatible list size.
2483          */
2484         list_xattr_in = fdi.indata;
2485         list_xattr_in->size = 0;
2486         attr_str = (char *)fdi.indata + sizeof(*list_xattr_in);
2487         snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator);
2488
2489         err = fdisp_wait_answ(&fdi);
2490         if (err != 0) {
2491                 if (err == ENOSYS) {
2492                         fsess_set_notimpl(mp, FUSE_LISTXATTR);
2493                         err = EOPNOTSUPP;
2494                 }
2495                 goto out;
2496         }
2497
2498         list_xattr_out = fdi.answ;
2499         linux_list_len = list_xattr_out->size;
2500         if (linux_list_len == 0) {
2501                 if (ap->a_size != NULL)
2502                         *ap->a_size = linux_list_len;
2503                 goto out;
2504         }
2505
2506         /*
2507          * Retrieve Linux / FUSE compatible list values.
2508          */
2509         fdisp_refresh_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
2510         list_xattr_in = fdi.indata;
2511         list_xattr_in->size = linux_list_len + sizeof(*list_xattr_out);
2512         attr_str = (char *)fdi.indata + sizeof(*list_xattr_in);
2513         snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator);
2514
2515         err = fdisp_wait_answ(&fdi);
2516         if (err != 0)
2517                 goto out;
2518
2519         linux_list = fdi.answ;
2520         linux_list_len = fdi.iosize;
2521
2522         /*
2523          * Retrieve the BSD compatible list values.
2524          * The Linux / FUSE attribute list format isn't the same
2525          * as FreeBSD's format. So we need to transform it into
2526          * FreeBSD's format before giving it to the user.
2527          */
2528         bsd_list = malloc(linux_list_len, M_TEMP, M_WAITOK);
2529         err = fuse_xattrlist_convert(prefix, linux_list, linux_list_len,
2530             bsd_list, &bsd_list_len);
2531         if (err != 0)
2532                 goto out;
2533
2534         if (ap->a_size != NULL)
2535                 *ap->a_size = bsd_list_len;
2536
2537         if (uio != NULL)
2538                 err = uiomove(bsd_list, bsd_list_len, uio);
2539
2540 out:
2541         free(bsd_list, M_TEMP);
2542         fdisp_destroy(&fdi);
2543         return (err);
2544 }
2545
2546 /*
2547     struct vop_deleteextattr_args {
2548         struct vop_generic_args a_gen;
2549         struct vnode *a_vp;
2550         int a_attrnamespace;
2551         const char *a_name;
2552         struct ucred *a_cred;
2553         struct thread *a_td;
2554     };
2555 */
2556 static int
2557 fuse_vnop_deleteextattr(struct vop_deleteextattr_args *ap)
2558 {
2559         struct vnode *vp = ap->a_vp;
2560         struct fuse_dispatcher fdi;
2561         struct mount *mp = vnode_mount(vp);
2562         struct thread *td = ap->a_td;
2563         struct ucred *cred = ap->a_cred;
2564         char *prefix;
2565         size_t len;
2566         char *attr_str;
2567         int err;
2568
2569         if (fuse_isdeadfs(vp))
2570                 return (ENXIO);
2571
2572         if (!fsess_isimpl(mp, FUSE_REMOVEXATTR))
2573                 return EOPNOTSUPP;
2574
2575         if (vfs_isrdonly(mp))
2576                 return EROFS;
2577
2578         err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td,
2579                 VWRITE);
2580         if (err)
2581                 return err;
2582
2583         /* Default to looking for user attributes. */
2584         if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
2585                 prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
2586         else
2587                 prefix = EXTATTR_NAMESPACE_USER_STRING;
2588
2589         len = strlen(prefix) + sizeof(extattr_namespace_separator) +
2590             strlen(ap->a_name) + 1;
2591
2592         fdisp_init(&fdi, len);
2593         fdisp_make_vp(&fdi, FUSE_REMOVEXATTR, vp, td, cred);
2594
2595         attr_str = fdi.indata;
2596         snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
2597             ap->a_name);
2598
2599         err = fdisp_wait_answ(&fdi);
2600         if (err == ENOSYS) {
2601                 fsess_set_notimpl(mp, FUSE_REMOVEXATTR);
2602                 err = EOPNOTSUPP;
2603         }
2604
2605         fdisp_destroy(&fdi);
2606         return (err);
2607 }
2608
2609 /*
2610     struct vnop_print_args {
2611         struct vnode *a_vp;
2612     };
2613 */
2614 static int
2615 fuse_vnop_print(struct vop_print_args *ap)
2616 {
2617         struct fuse_vnode_data *fvdat = VTOFUD(ap->a_vp);
2618
2619         printf("nodeid: %ju, parent nodeid: %ju, nlookup: %ju, flag: %#x\n",
2620             (uintmax_t)VTOILLU(ap->a_vp), (uintmax_t)fvdat->parent_nid,
2621             (uintmax_t)fvdat->nlookup,
2622             fvdat->flag);
2623
2624         return 0;
2625 }
2626         
2627 /*
2628  * Get an NFS filehandle for a FUSE file.
2629  *
2630  * This will only work for FUSE file systems that guarantee the uniqueness of
2631  * nodeid:generation, which most don't.
2632  */
2633 /*
2634 vop_vptofh {
2635         IN struct vnode *a_vp;
2636         IN struct fid *a_fhp;
2637 };
2638 */
2639 static int
2640 fuse_vnop_vptofh(struct vop_vptofh_args *ap)
2641 {
2642         struct vnode *vp = ap->a_vp;
2643         struct fuse_vnode_data *fvdat = VTOFUD(vp);
2644         struct fuse_fid *fhp = (struct fuse_fid *)(ap->a_fhp);
2645         _Static_assert(sizeof(struct fuse_fid) <= sizeof(struct fid),
2646                 "FUSE fid type is too big");
2647         struct mount *mp = vnode_mount(vp);
2648         struct fuse_data *data = fuse_get_mpdata(mp);
2649         struct vattr va;
2650         int err;
2651
2652         if (!(data->dataflags & FSESS_EXPORT_SUPPORT))
2653                 return EOPNOTSUPP;
2654
2655         err = fuse_internal_getattr(vp, &va, curthread->td_ucred, curthread);
2656         if (err)
2657                 return err;
2658
2659         /*ip = VTOI(ap->a_vp);*/
2660         /*ufhp = (struct ufid *)ap->a_fhp;*/
2661         fhp->len = sizeof(struct fuse_fid);
2662         fhp->nid = fvdat->nid;
2663         if (fvdat->generation <= UINT32_MAX)
2664                 fhp->gen = fvdat->generation;
2665         else
2666                 return EOVERFLOW;
2667         return (0);
2668 }
2669
2670