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