xref: /freebsd/sys/fs/fuse/fuse_internal.c (revision 93fecd02)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2007-2009 Google Inc. and Amit Singh
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  *   copyright notice, this list of conditions and the following disclaimer
15  *   in the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of Google Inc. nor the names of its
18  *   contributors may be used to endorse or promote products derived from
19  *   this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Copyright (C) 2005 Csaba Henk.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 #include <sys/cdefs.h>
59 __FBSDID("$FreeBSD$");
60 
61 #include <sys/param.h>
62 #include <sys/module.h>
63 #include <sys/systm.h>
64 #include <sys/errno.h>
65 #include <sys/kernel.h>
66 #include <sys/conf.h>
67 #include <sys/uio.h>
68 #include <sys/malloc.h>
69 #include <sys/queue.h>
70 #include <sys/lock.h>
71 #include <sys/mutex.h>
72 #include <sys/sdt.h>
73 #include <sys/sx.h>
74 #include <sys/proc.h>
75 #include <sys/mount.h>
76 #include <sys/vnode.h>
77 #include <sys/namei.h>
78 #include <sys/stat.h>
79 #include <sys/unistd.h>
80 #include <sys/filedesc.h>
81 #include <sys/file.h>
82 #include <sys/fcntl.h>
83 #include <sys/dirent.h>
84 #include <sys/bio.h>
85 #include <sys/buf.h>
86 #include <sys/sysctl.h>
87 #include <sys/priv.h>
88 
89 #include "fuse.h"
90 #include "fuse_file.h"
91 #include "fuse_internal.h"
92 #include "fuse_ipc.h"
93 #include "fuse_node.h"
94 #include "fuse_file.h"
95 
96 SDT_PROVIDER_DECLARE(fusefs);
97 /*
98  * Fuse trace probe:
99  * arg0: verbosity.  Higher numbers give more verbose messages
100  * arg1: Textual message
101  */
102 SDT_PROBE_DEFINE2(fusefs, , internal, trace, "int", "char*");
103 
104 #ifdef ZERO_PAD_INCOMPLETE_BUFS
105 static int isbzero(void *buf, size_t len);
106 
107 #endif
108 
109 /* Synchronously send a FUSE_ACCESS operation */
110 int
111 fuse_internal_access(struct vnode *vp,
112     accmode_t mode,
113     struct thread *td,
114     struct ucred *cred)
115 {
116 	int err = 0;
117 	uint32_t mask = F_OK;
118 	int dataflags;
119 	int vtype;
120 	struct mount *mp;
121 	struct fuse_dispatcher fdi;
122 	struct fuse_access_in *fai;
123 	struct fuse_data *data;
124 
125 	mp = vnode_mount(vp);
126 	vtype = vnode_vtype(vp);
127 
128 	data = fuse_get_mpdata(mp);
129 	dataflags = data->dataflags;
130 
131 	if (mode == 0)
132 		return 0;
133 
134 	if (mode & VMODIFY_PERMS && vfs_isrdonly(mp)) {
135 		switch (vp->v_type) {
136 		case VDIR:
137 			/* FALLTHROUGH */
138 		case VLNK:
139 			/* FALLTHROUGH */
140 		case VREG:
141 			return EROFS;
142 		default:
143 			break;
144 		}
145 	}
146 
147 	/* Unless explicitly permitted, deny everyone except the fs owner. */
148 	if (!(dataflags & FSESS_DAEMON_CAN_SPY)) {
149 		if (fuse_match_cred(data->daemoncred, cred))
150 			return EPERM;
151 	}
152 
153 	if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
154 		struct vattr va;
155 
156 		fuse_internal_getattr(vp, &va, cred, td);
157 		return vaccess(vp->v_type, va.va_mode, va.va_uid,
158 		    va.va_gid, mode, cred, NULL);
159 	}
160 
161 	if (!fsess_isimpl(mp, FUSE_ACCESS))
162 		return 0;
163 
164 	if ((mode & (VWRITE | VAPPEND | VADMIN)) != 0)
165 		mask |= W_OK;
166 	if ((mode & VREAD) != 0)
167 		mask |= R_OK;
168 	if ((mode & VEXEC) != 0)
169 		mask |= X_OK;
170 
171 	fdisp_init(&fdi, sizeof(*fai));
172 	fdisp_make_vp(&fdi, FUSE_ACCESS, vp, td, cred);
173 
174 	fai = fdi.indata;
175 	fai->mask = mask;
176 
177 	err = fdisp_wait_answ(&fdi);
178 	fdisp_destroy(&fdi);
179 
180 	if (err == ENOSYS) {
181 		fsess_set_notimpl(mp, FUSE_ACCESS);
182 		err = 0;
183 	}
184 	return err;
185 }
186 
187 /*
188  * Cache FUSE attributes from attr, in attribute cache associated with vnode
189  * 'vp'.  Optionally, if argument 'vap' is not NULL, store a copy of the
190  * converted attributes there as well.
191  *
192  * If the nominal attribute cache TTL is zero, do not cache on the 'vp' (but do
193  * return the result to the caller).
194  */
195 void
196 fuse_internal_cache_attrs(struct vnode *vp, struct ucred *cred,
197 	struct fuse_attr *attr, uint64_t attr_valid, uint32_t attr_valid_nsec,
198 	struct vattr *vap)
199 {
200 	struct mount *mp;
201 	struct fuse_vnode_data *fvdat;
202 	struct fuse_data *data;
203 	struct vattr *vp_cache_at;
204 
205 	mp = vnode_mount(vp);
206 	fvdat = VTOFUD(vp);
207 	data = fuse_get_mpdata(mp);
208 	if (!cred)
209 		cred = curthread->td_ucred;
210 
211 	ASSERT_VOP_ELOCKED(vp, "fuse_internal_cache_attrs");
212 
213 	fuse_validity_2_bintime(attr_valid, attr_valid_nsec,
214 		&fvdat->attr_cache_timeout);
215 
216 	/* Fix our buffers if the filesize changed without us knowing */
217 	if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size) {
218 		(void)fuse_vnode_setsize(vp, cred, attr->size);
219 		fvdat->cached_attrs.va_size = attr->size;
220 	}
221 
222 	if (attr_valid > 0 || attr_valid_nsec > 0)
223 		vp_cache_at = &(fvdat->cached_attrs);
224 	else if (vap != NULL)
225 		vp_cache_at = vap;
226 	else
227 		return;
228 
229 	vattr_null(vp_cache_at);
230 	vp_cache_at->va_fsid = mp->mnt_stat.f_fsid.val[0];
231 	vp_cache_at->va_fileid = attr->ino;
232 	vp_cache_at->va_mode = attr->mode & ~S_IFMT;
233 	vp_cache_at->va_nlink     = attr->nlink;
234 	vp_cache_at->va_uid       = attr->uid;
235 	vp_cache_at->va_gid       = attr->gid;
236 	vp_cache_at->va_rdev      = attr->rdev;
237 	vp_cache_at->va_size      = attr->size;
238 	/* XXX on i386, seconds are truncated to 32 bits */
239 	vp_cache_at->va_atime.tv_sec  = attr->atime;
240 	vp_cache_at->va_atime.tv_nsec = attr->atimensec;
241 	vp_cache_at->va_mtime.tv_sec  = attr->mtime;
242 	vp_cache_at->va_mtime.tv_nsec = attr->mtimensec;
243 	vp_cache_at->va_ctime.tv_sec  = attr->ctime;
244 	vp_cache_at->va_ctime.tv_nsec = attr->ctimensec;
245 	if (fuse_libabi_geq(data, 7, 9) && attr->blksize > 0)
246 		vp_cache_at->va_blocksize = attr->blksize;
247 	else
248 		vp_cache_at->va_blocksize = PAGE_SIZE;
249 	vp_cache_at->va_type = IFTOVT(attr->mode);
250 	vp_cache_at->va_bytes = attr->blocks * S_BLKSIZE;
251 	vp_cache_at->va_flags = 0;
252 
253 	if (vap != vp_cache_at && vap != NULL)
254 		memcpy(vap, vp_cache_at, sizeof(*vap));
255 }
256 
257 
258 /* fsync */
259 
260 int
261 fuse_internal_fsync_callback(struct fuse_ticket *tick, struct uio *uio)
262 {
263 	if (tick->tk_aw_ohead.error == ENOSYS) {
264 		fsess_set_notimpl(tick->tk_data->mp, fticket_opcode(tick));
265 	}
266 	return 0;
267 }
268 
269 int
270 fuse_internal_fsync(struct vnode *vp,
271     struct thread *td,
272     int waitfor,
273     bool datasync)
274 {
275 	struct fuse_fsync_in *ffsi = NULL;
276 	struct fuse_dispatcher fdi;
277 	struct fuse_filehandle *fufh;
278 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
279 	struct mount *mp = vnode_mount(vp);
280 	int op = FUSE_FSYNC;
281 	int err = 0;
282 
283 	if (!fsess_isimpl(vnode_mount(vp),
284 	    (vnode_vtype(vp) == VDIR ? FUSE_FSYNCDIR : FUSE_FSYNC))) {
285 		return 0;
286 	}
287 	if (vnode_isdir(vp))
288 		op = FUSE_FSYNCDIR;
289 
290 	if (!fsess_isimpl(mp, op))
291 		return 0;
292 
293 	fdisp_init(&fdi, sizeof(*ffsi));
294 	/*
295 	 * fsync every open file handle for this file, because we can't be sure
296 	 * which file handle the caller is really referring to.
297 	 */
298 	LIST_FOREACH(fufh, &fvdat->handles, next) {
299 		if (ffsi == NULL)
300 			fdisp_make_vp(&fdi, op, vp, td, NULL);
301 		else
302 			fdisp_refresh_vp(&fdi, op, vp, td, NULL);
303 		ffsi = fdi.indata;
304 		ffsi->fh = fufh->fh_id;
305 		ffsi->fsync_flags = 0;
306 
307 		if (datasync)
308 			ffsi->fsync_flags = 1;
309 
310 		if (waitfor == MNT_WAIT) {
311 			err = fdisp_wait_answ(&fdi);
312 		} else {
313 			fuse_insert_callback(fdi.tick,
314 				fuse_internal_fsync_callback);
315 			fuse_insert_message(fdi.tick, false);
316 		}
317 		if (err == ENOSYS) {
318 			/* ENOSYS means "success, and don't call again" */
319 			fsess_set_notimpl(mp, op);
320 			err = 0;
321 			break;
322 		}
323 	}
324 	fdisp_destroy(&fdi);
325 
326 	return err;
327 }
328 
329 /* mknod */
330 int
331 fuse_internal_mknod(struct vnode *dvp, struct vnode **vpp,
332 	struct componentname *cnp, struct vattr *vap)
333 {
334 	struct fuse_mknod_in fmni;
335 
336 	fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode);
337 	fmni.rdev = vap->va_rdev;
338 	return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni,
339 	    sizeof(fmni), vap->va_type));
340 }
341 
342 /* readdir */
343 
344 int
345 fuse_internal_readdir(struct vnode *vp,
346     struct uio *uio,
347     off_t startoff,
348     struct fuse_filehandle *fufh,
349     struct fuse_iov *cookediov,
350     int *ncookies,
351     u_long *cookies)
352 {
353 	int err = 0;
354 	struct fuse_dispatcher fdi;
355 	struct fuse_read_in *fri = NULL;
356 	int fnd_start;
357 
358 	if (uio_resid(uio) == 0)
359 		return 0;
360 	fdisp_init(&fdi, 0);
361 
362 	/*
363 	 * Note that we DO NOT have a UIO_SYSSPACE here (so no need for p2p
364 	 * I/O).
365 	 */
366 
367 	/*
368 	 * fnd_start is set non-zero once the offset in the directory gets
369 	 * to the startoff.  This is done because directories must be read
370 	 * from the beginning (offset == 0) when fuse_vnop_readdir() needs
371 	 * to do an open of the directory.
372 	 * If it is not set non-zero here, it will be set non-zero in
373 	 * fuse_internal_readdir_processdata() when uio_offset == startoff.
374 	 */
375 	fnd_start = 0;
376 	if (uio->uio_offset == startoff)
377 		fnd_start = 1;
378 	while (uio_resid(uio) > 0) {
379 		fdi.iosize = sizeof(*fri);
380 		if (fri == NULL)
381 			fdisp_make_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
382 		else
383 			fdisp_refresh_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
384 
385 		fri = fdi.indata;
386 		fri->fh = fufh->fh_id;
387 		fri->offset = uio_offset(uio);
388 		fri->size = MIN(uio->uio_resid,
389 		    fuse_get_mpdata(vp->v_mount)->max_read);
390 
391 		if ((err = fdisp_wait_answ(&fdi)))
392 			break;
393 		if ((err = fuse_internal_readdir_processdata(uio, startoff,
394 		    &fnd_start, fri->size, fdi.answ, fdi.iosize, cookediov,
395 		    ncookies, &cookies)))
396 			break;
397 	}
398 
399 	fdisp_destroy(&fdi);
400 	return ((err == -1) ? 0 : err);
401 }
402 
403 /*
404  * Return -1 to indicate that this readdir is finished, 0 if it copied
405  * all the directory data read in and it may be possible to read more
406  * and greater than 0 for a failure.
407  */
408 int
409 fuse_internal_readdir_processdata(struct uio *uio,
410     off_t startoff,
411     int *fnd_start,
412     size_t reqsize,
413     void *buf,
414     size_t bufsize,
415     struct fuse_iov *cookediov,
416     int *ncookies,
417     u_long **cookiesp)
418 {
419 	int err = 0;
420 	int bytesavail;
421 	size_t freclen;
422 
423 	struct dirent *de;
424 	struct fuse_dirent *fudge;
425 	u_long *cookies;
426 
427 	cookies = *cookiesp;
428 	if (bufsize < FUSE_NAME_OFFSET)
429 		return -1;
430 	for (;;) {
431 		if (bufsize < FUSE_NAME_OFFSET) {
432 			err = -1;
433 			break;
434 		}
435 		fudge = (struct fuse_dirent *)buf;
436 		freclen = FUSE_DIRENT_SIZE(fudge);
437 
438 		if (bufsize < freclen) {
439 			/*
440 			 * This indicates a partial directory entry at the
441 			 * end of the directory data.
442 			 */
443 			err = -1;
444 			break;
445 		}
446 #ifdef ZERO_PAD_INCOMPLETE_BUFS
447 		if (isbzero(buf, FUSE_NAME_OFFSET)) {
448 			err = -1;
449 			break;
450 		}
451 #endif
452 
453 		if (!fudge->namelen || fudge->namelen > MAXNAMLEN) {
454 			err = EINVAL;
455 			break;
456 		}
457 		bytesavail = GENERIC_DIRSIZ((struct pseudo_dirent *)
458 					    &fudge->namelen);
459 
460 		if (bytesavail > uio_resid(uio)) {
461 			/* Out of space for the dir so we are done. */
462 			err = -1;
463 			break;
464 		}
465 		/*
466 		 * Don't start to copy the directory entries out until
467 		 * the requested offset in the directory is found.
468 		 */
469 		if (*fnd_start != 0) {
470 			fiov_adjust(cookediov, bytesavail);
471 			bzero(cookediov->base, bytesavail);
472 
473 			de = (struct dirent *)cookediov->base;
474 			de->d_fileno = fudge->ino;
475 			de->d_reclen = bytesavail;
476 			de->d_type = fudge->type;
477 			de->d_namlen = fudge->namelen;
478 			memcpy((char *)cookediov->base + sizeof(struct dirent) -
479 			       MAXNAMLEN - 1,
480 			       (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
481 			dirent_terminate(de);
482 
483 			err = uiomove(cookediov->base, cookediov->len, uio);
484 			if (err)
485 				break;
486 			if (cookies != NULL) {
487 				if (*ncookies == 0) {
488 					err = -1;
489 					break;
490 				}
491 				*cookies = fudge->off;
492 				cookies++;
493 				(*ncookies)--;
494 			}
495 		} else if (startoff == fudge->off)
496 			*fnd_start = 1;
497 		buf = (char *)buf + freclen;
498 		bufsize -= freclen;
499 		uio_setoffset(uio, fudge->off);
500 	}
501 	*cookiesp = cookies;
502 
503 	return err;
504 }
505 
506 /* remove */
507 
508 int
509 fuse_internal_remove(struct vnode *dvp,
510     struct vnode *vp,
511     struct componentname *cnp,
512     enum fuse_opcode op)
513 {
514 	struct fuse_dispatcher fdi;
515 	int err = 0;
516 
517 	fdisp_init(&fdi, cnp->cn_namelen + 1);
518 	fdisp_make_vp(&fdi, op, dvp, cnp->cn_thread, cnp->cn_cred);
519 
520 	memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
521 	((char *)fdi.indata)[cnp->cn_namelen] = '\0';
522 
523 	err = fdisp_wait_answ(&fdi);
524 	fdisp_destroy(&fdi);
525 	return err;
526 }
527 
528 /* rename */
529 
530 int
531 fuse_internal_rename(struct vnode *fdvp,
532     struct componentname *fcnp,
533     struct vnode *tdvp,
534     struct componentname *tcnp)
535 {
536 	struct fuse_dispatcher fdi;
537 	struct fuse_rename_in *fri;
538 	int err = 0;
539 
540 	fdisp_init(&fdi, sizeof(*fri) + fcnp->cn_namelen + tcnp->cn_namelen + 2);
541 	fdisp_make_vp(&fdi, FUSE_RENAME, fdvp, tcnp->cn_thread, tcnp->cn_cred);
542 
543 	fri = fdi.indata;
544 	fri->newdir = VTOI(tdvp);
545 	memcpy((char *)fdi.indata + sizeof(*fri), fcnp->cn_nameptr,
546 	    fcnp->cn_namelen);
547 	((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen] = '\0';
548 	memcpy((char *)fdi.indata + sizeof(*fri) + fcnp->cn_namelen + 1,
549 	    tcnp->cn_nameptr, tcnp->cn_namelen);
550 	((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen +
551 	    tcnp->cn_namelen + 1] = '\0';
552 
553 	err = fdisp_wait_answ(&fdi);
554 	fdisp_destroy(&fdi);
555 	return err;
556 }
557 
558 /* strategy */
559 
560 /* entity creation */
561 
562 void
563 fuse_internal_newentry_makerequest(struct mount *mp,
564     uint64_t dnid,
565     struct componentname *cnp,
566     enum fuse_opcode op,
567     void *buf,
568     size_t bufsize,
569     struct fuse_dispatcher *fdip)
570 {
571 	fdip->iosize = bufsize + cnp->cn_namelen + 1;
572 
573 	fdisp_make(fdip, op, mp, dnid, cnp->cn_thread, cnp->cn_cred);
574 	memcpy(fdip->indata, buf, bufsize);
575 	memcpy((char *)fdip->indata + bufsize, cnp->cn_nameptr, cnp->cn_namelen);
576 	((char *)fdip->indata)[bufsize + cnp->cn_namelen] = '\0';
577 }
578 
579 int
580 fuse_internal_newentry_core(struct vnode *dvp,
581     struct vnode **vpp,
582     struct componentname *cnp,
583     enum vtype vtyp,
584     struct fuse_dispatcher *fdip)
585 {
586 	int err = 0;
587 	struct fuse_entry_out *feo;
588 	struct mount *mp = vnode_mount(dvp);
589 
590 	if ((err = fdisp_wait_answ(fdip))) {
591 		return err;
592 	}
593 	feo = fdip->answ;
594 
595 	if ((err = fuse_internal_checkentry(feo, vtyp))) {
596 		return err;
597 	}
598 	err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, vtyp);
599 	if (err) {
600 		fuse_internal_forget_send(mp, cnp->cn_thread, cnp->cn_cred,
601 		    feo->nodeid, 1);
602 		return err;
603 	}
604 
605 	/*
606 	 * Purge the parent's attribute cache because the daemon should've
607 	 * updated its mtime and ctime
608 	 */
609 	fuse_vnode_clear_attr_cache(dvp);
610 
611 	fuse_internal_cache_attrs(*vpp, NULL, &feo->attr, feo->attr_valid,
612 		feo->attr_valid_nsec, NULL);
613 
614 	return err;
615 }
616 
617 int
618 fuse_internal_newentry(struct vnode *dvp,
619     struct vnode **vpp,
620     struct componentname *cnp,
621     enum fuse_opcode op,
622     void *buf,
623     size_t bufsize,
624     enum vtype vtype)
625 {
626 	int err;
627 	struct fuse_dispatcher fdi;
628 	struct mount *mp = vnode_mount(dvp);
629 
630 	fdisp_init(&fdi, 0);
631 	fuse_internal_newentry_makerequest(mp, VTOI(dvp), cnp, op, buf,
632 	    bufsize, &fdi);
633 	err = fuse_internal_newentry_core(dvp, vpp, cnp, vtype, &fdi);
634 	fdisp_destroy(&fdi);
635 
636 	return err;
637 }
638 
639 /* entity destruction */
640 
641 int
642 fuse_internal_forget_callback(struct fuse_ticket *ftick, struct uio *uio)
643 {
644 	fuse_internal_forget_send(ftick->tk_data->mp, curthread, NULL,
645 	    ((struct fuse_in_header *)ftick->tk_ms_fiov.base)->nodeid, 1);
646 
647 	return 0;
648 }
649 
650 void
651 fuse_internal_forget_send(struct mount *mp,
652     struct thread *td,
653     struct ucred *cred,
654     uint64_t nodeid,
655     uint64_t nlookup)
656 {
657 
658 	struct fuse_dispatcher fdi;
659 	struct fuse_forget_in *ffi;
660 
661 	/*
662          * KASSERT(nlookup > 0, ("zero-times forget for vp #%llu",
663          *         (long long unsigned) nodeid));
664          */
665 
666 	fdisp_init(&fdi, sizeof(*ffi));
667 	fdisp_make(&fdi, FUSE_FORGET, mp, nodeid, td, cred);
668 
669 	ffi = fdi.indata;
670 	ffi->nlookup = nlookup;
671 
672 	fuse_insert_message(fdi.tick, false);
673 	fdisp_destroy(&fdi);
674 }
675 
676 /* Fetch the vnode's attributes from the daemon*/
677 int
678 fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap,
679 	struct ucred *cred, struct thread *td)
680 {
681 	struct fuse_dispatcher fdi;
682 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
683 	struct fuse_attr_out *fao;
684 	off_t old_filesize = fvdat->cached_attrs.va_size;
685 	enum vtype vtyp;
686 	int err;
687 
688 	fdisp_init(&fdi, 0);
689 	if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, td, cred))) {
690 		if (err == ENOENT)
691 			fuse_internal_vnode_disappear(vp);
692 		goto out;
693 	}
694 
695 	fao = (struct fuse_attr_out *)fdi.answ;
696 	vtyp = IFTOVT(fao->attr.mode);
697 	if (fvdat->flag & FN_SIZECHANGE)
698 		fao->attr.size = old_filesize;
699 	fuse_internal_cache_attrs(vp, NULL, &fao->attr, fao->attr_valid,
700 		fao->attr_valid_nsec, vap);
701 	if (vtyp != vnode_vtype(vp)) {
702 		fuse_internal_vnode_disappear(vp);
703 		err = ENOENT;
704 	}
705 
706 out:
707 	fdisp_destroy(&fdi);
708 	return err;
709 }
710 
711 /* Read a vnode's attributes from cache or fetch them from the fuse daemon */
712 int
713 fuse_internal_getattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
714 	struct thread *td)
715 {
716 	struct vattr *attrs;
717 
718 	if ((attrs = VTOVA(vp)) != NULL) {
719 		*vap = *attrs;	/* struct copy */
720 		return 0;
721 	}
722 
723 	return fuse_internal_do_getattr(vp, vap, cred, td);
724 }
725 
726 void
727 fuse_internal_vnode_disappear(struct vnode *vp)
728 {
729 	struct fuse_vnode_data *fvdat = VTOFUD(vp);
730 
731 	ASSERT_VOP_ELOCKED(vp, "fuse_internal_vnode_disappear");
732 	fvdat->flag |= FN_REVOKED;
733 	bintime_clear(&fvdat->attr_cache_timeout);
734 	cache_purge(vp);
735 }
736 
737 /* fuse start/stop */
738 
739 int
740 fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
741 {
742 	int err = 0;
743 	struct fuse_data *data = tick->tk_data;
744 	struct fuse_init_out *fiio;
745 
746 	if ((err = tick->tk_aw_ohead.error)) {
747 		goto out;
748 	}
749 	if ((err = fticket_pull(tick, uio))) {
750 		goto out;
751 	}
752 	fiio = fticket_resp(tick)->base;
753 
754 	/* XXX: Do we want to check anything further besides this? */
755 	if (fiio->major < 7) {
756 		SDT_PROBE2(fusefs, , internal, trace, 1,
757 			"userpace version too low");
758 		err = EPROTONOSUPPORT;
759 		goto out;
760 	}
761 	data->fuse_libabi_major = fiio->major;
762 	data->fuse_libabi_minor = fiio->minor;
763 
764 	if (fuse_libabi_geq(data, 7, 5)) {
765 		if (fticket_resp(tick)->len == sizeof(struct fuse_init_out)) {
766 			data->max_write = fiio->max_write;
767 			if (fiio->flags & FUSE_ASYNC_READ)
768 				data->dataflags |= FSESS_ASYNC_READ;
769 			if (fiio->flags & FUSE_POSIX_LOCKS)
770 				data->dataflags |= FSESS_POSIX_LOCKS;
771 			if (fiio->flags & FUSE_EXPORT_SUPPORT)
772 				data->dataflags |= FSESS_EXPORT_SUPPORT;
773 		} else {
774 			err = EINVAL;
775 		}
776 	} else {
777 		/* Old fix values */
778 		data->max_write = 4096;
779 	}
780 
781 out:
782 	if (err) {
783 		fdata_set_dead(data);
784 	}
785 	FUSE_LOCK();
786 	data->dataflags |= FSESS_INITED;
787 	wakeup(&data->ticketer);
788 	FUSE_UNLOCK();
789 
790 	return 0;
791 }
792 
793 void
794 fuse_internal_send_init(struct fuse_data *data, struct thread *td)
795 {
796 	struct fuse_init_in *fiii;
797 	struct fuse_dispatcher fdi;
798 
799 	fdisp_init(&fdi, sizeof(*fiii));
800 	fdisp_make(&fdi, FUSE_INIT, data->mp, 0, td, NULL);
801 	fiii = fdi.indata;
802 	fiii->major = FUSE_KERNEL_VERSION;
803 	fiii->minor = FUSE_KERNEL_MINOR_VERSION;
804 	/*
805 	 * fusefs currently doesn't do any readahead other than fetching whole
806 	 * buffer cache block sized regions at once.  So the max readahead is
807 	 * the size of a buffer cache block.
808 	 */
809 	fiii->max_readahead = maxbcachebuf;
810 	fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT ;
811 
812 	fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
813 	fuse_insert_message(fdi.tick, false);
814 	fdisp_destroy(&fdi);
815 }
816 
817 /*
818  * Send a FUSE_SETATTR operation with no permissions checks.  If cred is NULL,
819  * send the request with root credentials
820  */
821 int fuse_internal_setattr(struct vnode *vp, struct vattr *vap,
822 	struct thread *td, struct ucred *cred)
823 {
824 	struct fuse_dispatcher fdi;
825 	struct fuse_setattr_in *fsai;
826 	struct mount *mp;
827 	pid_t pid = td->td_proc->p_pid;
828 	struct fuse_data *data;
829 	int dataflags;
830 	int err = 0;
831 	enum vtype vtyp;
832 	int sizechanged = -1;
833 	uint64_t newsize = 0;
834 
835 	mp = vnode_mount(vp);
836 	data = fuse_get_mpdata(mp);
837 	dataflags = data->dataflags;
838 
839 	fdisp_init(&fdi, sizeof(*fsai));
840 	fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
841 	if (!cred) {
842 		fdi.finh->uid = 0;
843 		fdi.finh->gid = 0;
844 	}
845 	fsai = fdi.indata;
846 	fsai->valid = 0;
847 
848 	if (vap->va_uid != (uid_t)VNOVAL) {
849 		fsai->uid = vap->va_uid;
850 		fsai->valid |= FATTR_UID;
851 	}
852 	if (vap->va_gid != (gid_t)VNOVAL) {
853 		fsai->gid = vap->va_gid;
854 		fsai->valid |= FATTR_GID;
855 	}
856 	if (vap->va_size != VNOVAL) {
857 		struct fuse_filehandle *fufh = NULL;
858 
859 		/*Truncate to a new value. */
860 		fsai->size = vap->va_size;
861 		sizechanged = 1;
862 		newsize = vap->va_size;
863 		fsai->valid |= FATTR_SIZE;
864 
865 		fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
866 		if (fufh) {
867 			fsai->fh = fufh->fh_id;
868 			fsai->valid |= FATTR_FH;
869 		}
870 		VTOFUD(vp)->flag &= ~FN_SIZECHANGE;
871 	}
872 	if (vap->va_atime.tv_sec != VNOVAL) {
873 		fsai->atime = vap->va_atime.tv_sec;
874 		fsai->atimensec = vap->va_atime.tv_nsec;
875 		fsai->valid |= FATTR_ATIME;
876 		if (vap->va_vaflags & VA_UTIMES_NULL)
877 			fsai->valid |= FATTR_ATIME_NOW;
878 	}
879 	if (vap->va_mtime.tv_sec != VNOVAL) {
880 		fsai->mtime = vap->va_mtime.tv_sec;
881 		fsai->mtimensec = vap->va_mtime.tv_nsec;
882 		fsai->valid |= FATTR_MTIME;
883 		if (vap->va_vaflags & VA_UTIMES_NULL)
884 			fsai->valid |= FATTR_MTIME_NOW;
885 	}
886 	if (vap->va_mode != (mode_t)VNOVAL) {
887 		fsai->mode = vap->va_mode & ALLPERMS;
888 		fsai->valid |= FATTR_MODE;
889 	}
890 	if (!fsai->valid) {
891 		goto out;
892 	}
893 
894 	if ((err = fdisp_wait_answ(&fdi)))
895 		goto out;
896 	vtyp = IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode);
897 
898 	if (vnode_vtype(vp) != vtyp) {
899 		if (vnode_vtype(vp) == VNON && vtyp != VNON) {
900 			SDT_PROBE2(fusefs, , internal, trace, 1, "FUSE: Dang! "
901 				"vnode_vtype is VNON and vtype isn't.");
902 		} else {
903 			/*
904 	                 * STALE vnode, ditch
905 	                 *
906 			 * The vnode has changed its type "behind our back".
907 			 * There's nothing really we can do, so let us just
908 			 * force an internal revocation and tell the caller to
909 			 * try again, if interested.
910 	                 */
911 			fuse_internal_vnode_disappear(vp);
912 			err = EAGAIN;
913 		}
914 	}
915 	if (err == 0) {
916 		struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
917 		fuse_internal_cache_attrs(vp, cred, &fao->attr, fao->attr_valid,
918 			fao->attr_valid_nsec, NULL);
919 	}
920 
921 out:
922 	fdisp_destroy(&fdi);
923 	return err;
924 }
925 
926 #ifdef ZERO_PAD_INCOMPLETE_BUFS
927 static int
928 isbzero(void *buf, size_t len)
929 {
930 	int i;
931 
932 	for (i = 0; i < len; i++) {
933 		if (((char *)buf)[i])
934 			return (0);
935 	}
936 
937 	return (1);
938 }
939 
940 #endif
941