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: stable/12/sys/fs/fuse/fuse_internal.c 372632 2022-10-19 03:21:04Z asomers $");
65
66 #include <sys/param.h>
67 #include <sys/systm.h>
68 #include <sys/counter.h>
69 #include <sys/module.h>
70 #include <sys/errno.h>
71 #include <sys/kernel.h>
72 #include <sys/conf.h>
73 #include <sys/uio.h>
74 #include <sys/malloc.h>
75 #include <sys/queue.h>
76 #include <sys/lock.h>
77 #include <sys/mutex.h>
78 #include <sys/sdt.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/stat.h>
85 #include <sys/unistd.h>
86 #include <sys/filedesc.h>
87 #include <sys/file.h>
88 #include <sys/fcntl.h>
89 #include <sys/dirent.h>
90 #include <sys/bio.h>
91 #include <sys/buf.h>
92 #include <sys/sysctl.h>
93 #include <sys/priv.h>
94
95 #include "fuse.h"
96 #include "fuse_file.h"
97 #include "fuse_internal.h"
98 #include "fuse_io.h"
99 #include "fuse_ipc.h"
100 #include "fuse_node.h"
101 #include "fuse_file.h"
102
103 SDT_PROVIDER_DECLARE(fusefs);
104 /*
105 * Fuse trace probe:
106 * arg0: verbosity. Higher numbers give more verbose messages
107 * arg1: Textual message
108 */
109 SDT_PROBE_DEFINE2(fusefs, , internal, trace, "int", "char*");
110
111 #ifdef ZERO_PAD_INCOMPLETE_BUFS
112 static int isbzero(void *buf, size_t len);
113
114 #endif
115
116 counter_u64_t fuse_lookup_cache_hits;
117 counter_u64_t fuse_lookup_cache_misses;
118
119 SYSCTL_COUNTER_U64(_vfs_fusefs_stats, OID_AUTO, lookup_cache_hits, CTLFLAG_RD,
120 &fuse_lookup_cache_hits, "number of positive cache hits in lookup");
121
122 SYSCTL_COUNTER_U64(_vfs_fusefs_stats, OID_AUTO, lookup_cache_misses, CTLFLAG_RD,
123 &fuse_lookup_cache_misses, "number of cache misses in lookup");
124
125 int
fuse_internal_get_cached_vnode(struct mount * mp,ino_t ino,int flags,struct vnode ** vpp)126 fuse_internal_get_cached_vnode(struct mount* mp, ino_t ino, int flags,
127 struct vnode **vpp)
128 {
129 struct bintime now;
130 struct thread *td = curthread;
131 uint64_t nodeid = ino;
132 int error;
133
134 *vpp = NULL;
135
136 error = vfs_hash_get(mp, fuse_vnode_hash(nodeid), flags, td, vpp,
137 fuse_vnode_cmp, &nodeid);
138 if (error)
139 return error;
140 /*
141 * Check the entry cache timeout. We have to do this within fusefs
142 * instead of by using cache_enter_time/cache_lookup because those
143 * routines are only intended to work with pathnames, not inodes
144 */
145 if (*vpp != NULL) {
146 getbinuptime(&now);
147 if (bintime_cmp(&(VTOFUD(*vpp)->entry_cache_timeout), &now, >)){
148 counter_u64_add(fuse_lookup_cache_hits, 1);
149 return 0;
150 } else {
151 /* Entry cache timeout */
152 counter_u64_add(fuse_lookup_cache_misses, 1);
153 cache_purge(*vpp);
154 vput(*vpp);
155 *vpp = NULL;
156 }
157 }
158 return 0;
159 }
160
161 SDT_PROBE_DEFINE0(fusefs, , internal, access_vadmin);
162 /* Synchronously send a FUSE_ACCESS operation */
163 int
fuse_internal_access(struct vnode * vp,accmode_t mode,struct thread * td,struct ucred * cred)164 fuse_internal_access(struct vnode *vp,
165 accmode_t mode,
166 struct thread *td,
167 struct ucred *cred)
168 {
169 int err = 0;
170 uint32_t mask = F_OK;
171 int dataflags;
172 int vtype;
173 struct mount *mp;
174 struct fuse_dispatcher fdi;
175 struct fuse_access_in *fai;
176 struct fuse_data *data;
177
178 mp = vnode_mount(vp);
179 vtype = vnode_vtype(vp);
180
181 data = fuse_get_mpdata(mp);
182 dataflags = data->dataflags;
183
184 if (mode == 0)
185 return 0;
186
187 if (mode & VMODIFY_PERMS && vfs_isrdonly(mp)) {
188 switch (vp->v_type) {
189 case VDIR:
190 /* FALLTHROUGH */
191 case VLNK:
192 /* FALLTHROUGH */
193 case VREG:
194 return EROFS;
195 default:
196 break;
197 }
198 }
199
200 /* Unless explicitly permitted, deny everyone except the fs owner. */
201 if (!(dataflags & FSESS_DAEMON_CAN_SPY)) {
202 if (fuse_match_cred(data->daemoncred, cred))
203 return EPERM;
204 }
205
206 if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
207 struct vattr va;
208
209 fuse_internal_getattr(vp, &va, cred, td);
210 return vaccess(vp->v_type, va.va_mode, va.va_uid,
211 va.va_gid, mode, cred, NULL);
212 }
213
214 if (mode & VADMIN) {
215 /*
216 * The FUSE protocol doesn't have an equivalent of VADMIN, so
217 * it's a bug if we ever reach this point with that bit set.
218 */
219 SDT_PROBE0(fusefs, , internal, access_vadmin);
220 }
221
222 if (!fsess_isimpl(mp, FUSE_ACCESS))
223 return 0;
224
225 if ((mode & (VWRITE | VAPPEND)) != 0)
226 mask |= W_OK;
227 if ((mode & VREAD) != 0)
228 mask |= R_OK;
229 if ((mode & VEXEC) != 0)
230 mask |= X_OK;
231
232 fdisp_init(&fdi, sizeof(*fai));
233 fdisp_make_vp(&fdi, FUSE_ACCESS, vp, td, cred);
234
235 fai = fdi.indata;
236 fai->mask = mask;
237
238 err = fdisp_wait_answ(&fdi);
239 fdisp_destroy(&fdi);
240
241 if (err == ENOSYS) {
242 fsess_set_notimpl(mp, FUSE_ACCESS);
243 err = 0;
244 }
245 return err;
246 }
247
248 /*
249 * Cache FUSE attributes from attr, in attribute cache associated with vnode
250 * 'vp'. Optionally, if argument 'vap' is not NULL, store a copy of the
251 * converted attributes there as well.
252 *
253 * If the nominal attribute cache TTL is zero, do not cache on the 'vp' (but do
254 * return the result to the caller).
255 */
256 void
fuse_internal_cache_attrs(struct vnode * vp,struct fuse_attr * attr,uint64_t attr_valid,uint32_t attr_valid_nsec,struct vattr * vap,bool from_server)257 fuse_internal_cache_attrs(struct vnode *vp, struct fuse_attr *attr,
258 uint64_t attr_valid, uint32_t attr_valid_nsec, struct vattr *vap,
259 bool from_server)
260 {
261 struct mount *mp;
262 struct fuse_vnode_data *fvdat;
263 struct fuse_data *data;
264 struct vattr *vp_cache_at;
265
266 mp = vnode_mount(vp);
267 fvdat = VTOFUD(vp);
268 data = fuse_get_mpdata(mp);
269
270 ASSERT_VOP_ELOCKED(vp, "fuse_internal_cache_attrs");
271
272 fuse_validity_2_bintime(attr_valid, attr_valid_nsec,
273 &fvdat->attr_cache_timeout);
274
275 if (vnode_isreg(vp) &&
276 fvdat->cached_attrs.va_size != VNOVAL &&
277 attr->size != fvdat->cached_attrs.va_size)
278 {
279 if ( data->cache_mode == FUSE_CACHE_WB &&
280 fvdat->flag & FN_SIZECHANGE)
281 {
282 const char *msg;
283
284 /*
285 * The server changed the file's size even though we're
286 * using writeback cacheing and and we have outstanding
287 * dirty writes! That's a server bug.
288 */
289 if (fuse_libabi_geq(data, 7, 23)) {
290 msg = "writeback cache incoherent!."
291 "To prevent data corruption, disable "
292 "the writeback cache according to your "
293 "FUSE server's documentation.";
294 } else {
295 msg = "writeback cache incoherent!."
296 "To prevent data corruption, disable "
297 "the writeback cache by setting "
298 "vfs.fusefs.data_cache_mode to 0 or 1.";
299 }
300 fuse_warn(data, FSESS_WARN_WB_CACHE_INCOHERENT, msg);
301 }
302 if (fuse_vnode_attr_cache_valid(vp) &&
303 data->cache_mode != FUSE_CACHE_UC)
304 {
305 /*
306 * The server changed the file's size even though we
307 * have it cached and our cache has not yet expired.
308 * That's a bug.
309 */
310 fuse_warn(data, FSESS_WARN_CACHE_INCOHERENT,
311 "cache incoherent! "
312 "To prevent "
313 "data corruption, disable the data cache "
314 "by mounting with -o direct_io, or as "
315 "directed otherwise by your FUSE server's "
316 "documentation.");
317 }
318 }
319
320 /* Fix our buffers if the filesize changed without us knowing */
321 if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size) {
322 (void)fuse_vnode_setsize(vp, attr->size, from_server);
323 fvdat->cached_attrs.va_size = attr->size;
324 }
325
326 if (attr_valid > 0 || attr_valid_nsec > 0)
327 vp_cache_at = &(fvdat->cached_attrs);
328 else if (vap != NULL)
329 vp_cache_at = vap;
330 else
331 return;
332
333 vattr_null(vp_cache_at);
334 vp_cache_at->va_fsid = mp->mnt_stat.f_fsid.val[0];
335 vp_cache_at->va_fileid = attr->ino;
336 vp_cache_at->va_mode = attr->mode & ~S_IFMT;
337 vp_cache_at->va_nlink = attr->nlink;
338 vp_cache_at->va_uid = attr->uid;
339 vp_cache_at->va_gid = attr->gid;
340 vp_cache_at->va_rdev = attr->rdev;
341 vp_cache_at->va_size = attr->size;
342 /* XXX on i386, seconds are truncated to 32 bits */
343 vp_cache_at->va_atime.tv_sec = attr->atime;
344 vp_cache_at->va_atime.tv_nsec = attr->atimensec;
345 vp_cache_at->va_mtime.tv_sec = attr->mtime;
346 vp_cache_at->va_mtime.tv_nsec = attr->mtimensec;
347 vp_cache_at->va_ctime.tv_sec = attr->ctime;
348 vp_cache_at->va_ctime.tv_nsec = attr->ctimensec;
349 if (fuse_libabi_geq(data, 7, 9) && attr->blksize > 0)
350 vp_cache_at->va_blocksize = attr->blksize;
351 else
352 vp_cache_at->va_blocksize = PAGE_SIZE;
353 vp_cache_at->va_type = IFTOVT(attr->mode);
354 vp_cache_at->va_bytes = attr->blocks * S_BLKSIZE;
355 vp_cache_at->va_flags = 0;
356
357 if (vap != vp_cache_at && vap != NULL)
358 memcpy(vap, vp_cache_at, sizeof(*vap));
359 }
360
361
362 /* fsync */
363
364 int
fuse_internal_fsync_callback(struct fuse_ticket * tick,struct uio * uio)365 fuse_internal_fsync_callback(struct fuse_ticket *tick, struct uio *uio)
366 {
367 if (tick->tk_aw_ohead.error == ENOSYS) {
368 fsess_set_notimpl(tick->tk_data->mp, fticket_opcode(tick));
369 }
370 return 0;
371 }
372
373 int
fuse_internal_fsync(struct vnode * vp,struct thread * td,int waitfor,bool datasync)374 fuse_internal_fsync(struct vnode *vp,
375 struct thread *td,
376 int waitfor,
377 bool datasync)
378 {
379 struct fuse_fsync_in *ffsi = NULL;
380 struct fuse_dispatcher fdi;
381 struct fuse_filehandle *fufh;
382 struct fuse_vnode_data *fvdat = VTOFUD(vp);
383 struct mount *mp = vnode_mount(vp);
384 int op = FUSE_FSYNC;
385 int err = 0;
386
387 if (!fsess_isimpl(vnode_mount(vp),
388 (vnode_vtype(vp) == VDIR ? FUSE_FSYNCDIR : FUSE_FSYNC))) {
389 return 0;
390 }
391 if (vnode_isdir(vp))
392 op = FUSE_FSYNCDIR;
393
394 if (!fsess_isimpl(mp, op))
395 return 0;
396
397 fdisp_init(&fdi, sizeof(*ffsi));
398 /*
399 * fsync every open file handle for this file, because we can't be sure
400 * which file handle the caller is really referring to.
401 */
402 LIST_FOREACH(fufh, &fvdat->handles, next) {
403 fdi.iosize = sizeof(*ffsi);
404 if (ffsi == NULL)
405 fdisp_make_vp(&fdi, op, vp, td, NULL);
406 else
407 fdisp_refresh_vp(&fdi, op, vp, td, NULL);
408 ffsi = fdi.indata;
409 ffsi->fh = fufh->fh_id;
410 ffsi->fsync_flags = 0;
411
412 if (datasync)
413 ffsi->fsync_flags = 1;
414
415 if (waitfor == MNT_WAIT) {
416 err = fdisp_wait_answ(&fdi);
417 } else {
418 fuse_insert_callback(fdi.tick,
419 fuse_internal_fsync_callback);
420 fuse_insert_message(fdi.tick, false);
421 }
422 if (err == ENOSYS) {
423 /* ENOSYS means "success, and don't call again" */
424 fsess_set_notimpl(mp, op);
425 err = 0;
426 break;
427 }
428 }
429 fdisp_destroy(&fdi);
430
431 return err;
432 }
433
434 /* Asynchronous invalidation */
435 SDT_PROBE_DEFINE3(fusefs, , internal, invalidate_entry,
436 "struct vnode*", "struct fuse_notify_inval_entry_out*", "char*");
437 int
fuse_internal_invalidate_entry(struct mount * mp,struct uio * uio)438 fuse_internal_invalidate_entry(struct mount *mp, struct uio *uio)
439 {
440 struct fuse_notify_inval_entry_out fnieo;
441 struct componentname cn;
442 struct vnode *dvp, *vp;
443 char name[PATH_MAX];
444 int err;
445
446 if ((err = uiomove(&fnieo, sizeof(fnieo), uio)) != 0)
447 return (err);
448
449 if (fnieo.namelen >= sizeof(name))
450 return (EINVAL);
451
452 if ((err = uiomove(name, fnieo.namelen, uio)) != 0)
453 return (err);
454 name[fnieo.namelen] = '\0';
455 /* fusefs does not cache "." or ".." entries */
456 if (strncmp(name, ".", sizeof(".")) == 0 ||
457 strncmp(name, "..", sizeof("..")) == 0)
458 return (0);
459
460 if (fnieo.parent == FUSE_ROOT_ID)
461 err = VFS_ROOT(mp, LK_SHARED, &dvp);
462 else
463 err = fuse_internal_get_cached_vnode( mp, fnieo.parent,
464 LK_SHARED, &dvp);
465 SDT_PROBE3(fusefs, , internal, invalidate_entry, dvp, &fnieo, name);
466 /*
467 * If dvp is not in the cache, then it must've been reclaimed. And
468 * since fuse_vnop_reclaim does a cache_purge, name's entry must've
469 * been invalidated already. So we can safely return if dvp == NULL
470 */
471 if (err != 0 || dvp == NULL)
472 return (err);
473 /*
474 * XXX we can't check dvp's generation because the FUSE invalidate
475 * entry message doesn't include it. Worse case is that we invalidate
476 * an entry that didn't need to be invalidated.
477 */
478
479 cn.cn_nameiop = LOOKUP;
480 cn.cn_flags = 0; /* !MAKEENTRY means free cached entry */
481 cn.cn_thread = curthread;
482 cn.cn_cred = curthread->td_ucred;
483 cn.cn_lkflags = LK_SHARED;
484 cn.cn_pnbuf = NULL;
485 cn.cn_nameptr = name;
486 cn.cn_namelen = fnieo.namelen;
487 err = cache_lookup(dvp, &vp, &cn, NULL, NULL);
488 MPASS(err == 0);
489 fuse_vnode_clear_attr_cache(dvp);
490 vput(dvp);
491 return (0);
492 }
493
494 SDT_PROBE_DEFINE2(fusefs, , internal, invalidate_inode,
495 "struct vnode*", "struct fuse_notify_inval_inode_out *");
496 int
fuse_internal_invalidate_inode(struct mount * mp,struct uio * uio)497 fuse_internal_invalidate_inode(struct mount *mp, struct uio *uio)
498 {
499 struct fuse_notify_inval_inode_out fniio;
500 struct vnode *vp;
501 int err;
502
503 if ((err = uiomove(&fniio, sizeof(fniio), uio)) != 0)
504 return (err);
505
506 if (fniio.ino == FUSE_ROOT_ID)
507 err = VFS_ROOT(mp, LK_EXCLUSIVE, &vp);
508 else
509 err = fuse_internal_get_cached_vnode(mp, fniio.ino, LK_SHARED,
510 &vp);
511 SDT_PROBE2(fusefs, , internal, invalidate_inode, vp, &fniio);
512 if (err != 0 || vp == NULL)
513 return (err);
514 /*
515 * XXX we can't check vp's generation because the FUSE invalidate
516 * entry message doesn't include it. Worse case is that we invalidate
517 * an inode that didn't need to be invalidated.
518 */
519
520 /*
521 * Flush and invalidate buffers if off >= 0. Technically we only need
522 * to flush and invalidate the range of offsets [off, off + len), but
523 * for simplicity's sake we do everything.
524 */
525 if (fniio.off >= 0)
526 fuse_io_invalbuf(vp, curthread);
527 fuse_vnode_clear_attr_cache(vp);
528 vput(vp);
529 return (0);
530 }
531
532 /* mknod */
533 int
fuse_internal_mknod(struct vnode * dvp,struct vnode ** vpp,struct componentname * cnp,struct vattr * vap)534 fuse_internal_mknod(struct vnode *dvp, struct vnode **vpp,
535 struct componentname *cnp, struct vattr *vap)
536 {
537 struct fuse_data *data;
538 struct fuse_mknod_in fmni;
539 size_t insize;
540
541 data = fuse_get_mpdata(dvp->v_mount);
542
543 fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode);
544 fmni.rdev = vap->va_rdev;
545 if (fuse_libabi_geq(data, 7, 12)) {
546 insize = sizeof(fmni);
547 fmni.umask = curthread->td_proc->p_fd->fd_cmask;
548 } else {
549 insize = FUSE_COMPAT_MKNOD_IN_SIZE;
550 }
551 return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni,
552 insize, vap->va_type));
553 }
554
555 /* readdir */
556
557 int
fuse_internal_readdir(struct vnode * vp,struct uio * uio,off_t startoff,struct fuse_filehandle * fufh,struct fuse_iov * cookediov,int * ncookies,u_long * cookies)558 fuse_internal_readdir(struct vnode *vp,
559 struct uio *uio,
560 off_t startoff,
561 struct fuse_filehandle *fufh,
562 struct fuse_iov *cookediov,
563 int *ncookies,
564 u_long *cookies)
565 {
566 int err = 0;
567 struct fuse_dispatcher fdi;
568 struct fuse_read_in *fri = NULL;
569 int fnd_start;
570
571 if (uio_resid(uio) == 0)
572 return 0;
573 fdisp_init(&fdi, 0);
574
575 /*
576 * Note that we DO NOT have a UIO_SYSSPACE here (so no need for p2p
577 * I/O).
578 */
579
580 /*
581 * fnd_start is set non-zero once the offset in the directory gets
582 * to the startoff. This is done because directories must be read
583 * from the beginning (offset == 0) when fuse_vnop_readdir() needs
584 * to do an open of the directory.
585 * If it is not set non-zero here, it will be set non-zero in
586 * fuse_internal_readdir_processdata() when uio_offset == startoff.
587 */
588 fnd_start = 0;
589 if (uio->uio_offset == startoff)
590 fnd_start = 1;
591 while (uio_resid(uio) > 0) {
592 fdi.iosize = sizeof(*fri);
593 if (fri == NULL)
594 fdisp_make_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
595 else
596 fdisp_refresh_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
597
598 fri = fdi.indata;
599 fri->fh = fufh->fh_id;
600 fri->offset = uio_offset(uio);
601 fri->size = MIN(uio->uio_resid,
602 fuse_get_mpdata(vp->v_mount)->max_read);
603
604 if ((err = fdisp_wait_answ(&fdi)))
605 break;
606 if ((err = fuse_internal_readdir_processdata(uio, startoff,
607 &fnd_start, fri->size, fdi.answ, fdi.iosize, cookediov,
608 ncookies, &cookies)))
609 break;
610 }
611
612 fdisp_destroy(&fdi);
613 return ((err == -1) ? 0 : err);
614 }
615
616 /*
617 * Return -1 to indicate that this readdir is finished, 0 if it copied
618 * all the directory data read in and it may be possible to read more
619 * and greater than 0 for a failure.
620 */
621 int
fuse_internal_readdir_processdata(struct uio * uio,off_t startoff,int * fnd_start,size_t reqsize,void * buf,size_t bufsize,struct fuse_iov * cookediov,int * ncookies,u_long ** cookiesp)622 fuse_internal_readdir_processdata(struct uio *uio,
623 off_t startoff,
624 int *fnd_start,
625 size_t reqsize,
626 void *buf,
627 size_t bufsize,
628 struct fuse_iov *cookediov,
629 int *ncookies,
630 u_long **cookiesp)
631 {
632 int err = 0;
633 int oreclen;
634 size_t freclen;
635
636 struct dirent *de;
637 struct fuse_dirent *fudge;
638 u_long *cookies;
639
640 cookies = *cookiesp;
641 if (bufsize < FUSE_NAME_OFFSET)
642 return -1;
643 for (;;) {
644 if (bufsize < FUSE_NAME_OFFSET) {
645 err = -1;
646 break;
647 }
648 fudge = (struct fuse_dirent *)buf;
649 freclen = FUSE_DIRENT_SIZE(fudge);
650
651 if (bufsize < freclen) {
652 /*
653 * This indicates a partial directory entry at the
654 * end of the directory data.
655 */
656 err = -1;
657 break;
658 }
659 #ifdef ZERO_PAD_INCOMPLETE_BUFS
660 if (isbzero(buf, FUSE_NAME_OFFSET)) {
661 err = -1;
662 break;
663 }
664 #endif
665
666 if (!fudge->namelen || fudge->namelen > MAXNAMLEN) {
667 err = EINVAL;
668 break;
669 }
670 oreclen = GENERIC_DIRSIZ((struct pseudo_dirent *)
671 &fudge->namelen);
672
673 if (oreclen > uio_resid(uio)) {
674 /* Out of space for the dir so we are done. */
675 err = -1;
676 break;
677 }
678 /*
679 * Don't start to copy the directory entries out until
680 * the requested offset in the directory is found.
681 */
682 if (*fnd_start != 0) {
683 fiov_adjust(cookediov, oreclen);
684 bzero(cookediov->base, oreclen);
685
686 de = (struct dirent *)cookediov->base;
687 de->d_fileno = fudge->ino;
688 de->d_off = fudge->off;
689 de->d_reclen = oreclen;
690 de->d_type = fudge->type;
691 de->d_namlen = fudge->namelen;
692 memcpy((char *)cookediov->base + sizeof(struct dirent) -
693 MAXNAMLEN - 1,
694 (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
695 dirent_terminate(de);
696
697 err = uiomove(cookediov->base, cookediov->len, uio);
698 if (err)
699 break;
700 if (cookies != NULL) {
701 if (*ncookies == 0) {
702 err = -1;
703 break;
704 }
705 *cookies = fudge->off;
706 cookies++;
707 (*ncookies)--;
708 }
709 } else if (startoff == fudge->off)
710 *fnd_start = 1;
711 buf = (char *)buf + freclen;
712 bufsize -= freclen;
713 uio_setoffset(uio, fudge->off);
714 }
715 *cookiesp = cookies;
716
717 return err;
718 }
719
720 /* remove */
721
722 int
fuse_internal_remove(struct vnode * dvp,struct vnode * vp,struct componentname * cnp,enum fuse_opcode op)723 fuse_internal_remove(struct vnode *dvp,
724 struct vnode *vp,
725 struct componentname *cnp,
726 enum fuse_opcode op)
727 {
728 struct fuse_dispatcher fdi;
729 nlink_t nlink;
730 int err = 0;
731
732 fdisp_init(&fdi, cnp->cn_namelen + 1);
733 fdisp_make_vp(&fdi, op, dvp, cnp->cn_thread, cnp->cn_cred);
734
735 memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
736 ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
737
738 err = fdisp_wait_answ(&fdi);
739 fdisp_destroy(&fdi);
740
741 if (err)
742 return (err);
743
744 /*
745 * Access the cached nlink even if the attr cached has expired. If
746 * it's inaccurate, the worst that will happen is:
747 * 1) We'll recycle the vnode even though the file has another link we
748 * don't know about, costing a bit of cpu time, or
749 * 2) We won't recycle the vnode even though all of its links are gone.
750 * It will linger around until vnlru reclaims it, costing a bit of
751 * temporary memory.
752 */
753 nlink = VTOFUD(vp)->cached_attrs.va_nlink--;
754
755 /*
756 * Purge the parent's attribute cache because the daemon
757 * should've updated its mtime and ctime.
758 */
759 fuse_vnode_clear_attr_cache(dvp);
760
761 /* NB: nlink could be zero if it was never cached */
762 if (nlink <= 1 || vnode_vtype(vp) == VDIR) {
763 fuse_internal_vnode_disappear(vp);
764 } else {
765 cache_purge(vp);
766 fuse_vnode_update(vp, FN_CTIMECHANGE);
767 }
768
769 return err;
770 }
771
772 /* rename */
773
774 int
fuse_internal_rename(struct vnode * fdvp,struct componentname * fcnp,struct vnode * tdvp,struct componentname * tcnp)775 fuse_internal_rename(struct vnode *fdvp,
776 struct componentname *fcnp,
777 struct vnode *tdvp,
778 struct componentname *tcnp)
779 {
780 struct fuse_dispatcher fdi;
781 struct fuse_rename_in *fri;
782 int err = 0;
783
784 fdisp_init(&fdi, sizeof(*fri) + fcnp->cn_namelen + tcnp->cn_namelen + 2);
785 fdisp_make_vp(&fdi, FUSE_RENAME, fdvp, tcnp->cn_thread, tcnp->cn_cred);
786
787 fri = fdi.indata;
788 fri->newdir = VTOI(tdvp);
789 memcpy((char *)fdi.indata + sizeof(*fri), fcnp->cn_nameptr,
790 fcnp->cn_namelen);
791 ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen] = '\0';
792 memcpy((char *)fdi.indata + sizeof(*fri) + fcnp->cn_namelen + 1,
793 tcnp->cn_nameptr, tcnp->cn_namelen);
794 ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen +
795 tcnp->cn_namelen + 1] = '\0';
796
797 err = fdisp_wait_answ(&fdi);
798 fdisp_destroy(&fdi);
799 return err;
800 }
801
802 /* strategy */
803
804 /* entity creation */
805
806 void
fuse_internal_newentry_makerequest(struct mount * mp,uint64_t dnid,struct componentname * cnp,enum fuse_opcode op,void * buf,size_t bufsize,struct fuse_dispatcher * fdip)807 fuse_internal_newentry_makerequest(struct mount *mp,
808 uint64_t dnid,
809 struct componentname *cnp,
810 enum fuse_opcode op,
811 void *buf,
812 size_t bufsize,
813 struct fuse_dispatcher *fdip)
814 {
815 fdip->iosize = bufsize + cnp->cn_namelen + 1;
816
817 fdisp_make(fdip, op, mp, dnid, cnp->cn_thread, cnp->cn_cred);
818 memcpy(fdip->indata, buf, bufsize);
819 memcpy((char *)fdip->indata + bufsize, cnp->cn_nameptr, cnp->cn_namelen);
820 ((char *)fdip->indata)[bufsize + cnp->cn_namelen] = '\0';
821 }
822
823 int
fuse_internal_newentry_core(struct vnode * dvp,struct vnode ** vpp,struct componentname * cnp,enum vtype vtyp,struct fuse_dispatcher * fdip)824 fuse_internal_newentry_core(struct vnode *dvp,
825 struct vnode **vpp,
826 struct componentname *cnp,
827 enum vtype vtyp,
828 struct fuse_dispatcher *fdip)
829 {
830 int err = 0;
831 struct fuse_entry_out *feo;
832 struct mount *mp = vnode_mount(dvp);
833
834 if ((err = fdisp_wait_answ(fdip))) {
835 return err;
836 }
837 feo = fdip->answ;
838
839 if ((err = fuse_internal_checkentry(feo, vtyp))) {
840 return err;
841 }
842 err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, vtyp);
843 if (err) {
844 fuse_internal_forget_send(mp, cnp->cn_thread, cnp->cn_cred,
845 feo->nodeid, 1);
846 return err;
847 }
848
849 /*
850 * Purge the parent's attribute cache because the daemon should've
851 * updated its mtime and ctime
852 */
853 fuse_vnode_clear_attr_cache(dvp);
854
855 fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
856 feo->attr_valid_nsec, NULL, true);
857
858 return err;
859 }
860
861 int
fuse_internal_newentry(struct vnode * dvp,struct vnode ** vpp,struct componentname * cnp,enum fuse_opcode op,void * buf,size_t bufsize,enum vtype vtype)862 fuse_internal_newentry(struct vnode *dvp,
863 struct vnode **vpp,
864 struct componentname *cnp,
865 enum fuse_opcode op,
866 void *buf,
867 size_t bufsize,
868 enum vtype vtype)
869 {
870 int err;
871 struct fuse_dispatcher fdi;
872 struct mount *mp = vnode_mount(dvp);
873
874 fdisp_init(&fdi, 0);
875 fuse_internal_newentry_makerequest(mp, VTOI(dvp), cnp, op, buf,
876 bufsize, &fdi);
877 err = fuse_internal_newentry_core(dvp, vpp, cnp, vtype, &fdi);
878 fdisp_destroy(&fdi);
879
880 return err;
881 }
882
883 /* entity destruction */
884
885 int
fuse_internal_forget_callback(struct fuse_ticket * ftick,struct uio * uio)886 fuse_internal_forget_callback(struct fuse_ticket *ftick, struct uio *uio)
887 {
888 fuse_internal_forget_send(ftick->tk_data->mp, curthread, NULL,
889 ((struct fuse_in_header *)ftick->tk_ms_fiov.base)->nodeid, 1);
890
891 return 0;
892 }
893
894 void
fuse_internal_forget_send(struct mount * mp,struct thread * td,struct ucred * cred,uint64_t nodeid,uint64_t nlookup)895 fuse_internal_forget_send(struct mount *mp,
896 struct thread *td,
897 struct ucred *cred,
898 uint64_t nodeid,
899 uint64_t nlookup)
900 {
901
902 struct fuse_dispatcher fdi;
903 struct fuse_forget_in *ffi;
904
905 /*
906 * KASSERT(nlookup > 0, ("zero-times forget for vp #%llu",
907 * (long long unsigned) nodeid));
908 */
909
910 fdisp_init(&fdi, sizeof(*ffi));
911 fdisp_make(&fdi, FUSE_FORGET, mp, nodeid, td, cred);
912
913 ffi = fdi.indata;
914 ffi->nlookup = nlookup;
915
916 fuse_insert_message(fdi.tick, false);
917 fdisp_destroy(&fdi);
918 }
919
920 /* Fetch the vnode's attributes from the daemon*/
921 int
fuse_internal_do_getattr(struct vnode * vp,struct vattr * vap,struct ucred * cred,struct thread * td)922 fuse_internal_do_getattr(struct vnode *vp, struct vattr *vap,
923 struct ucred *cred, struct thread *td)
924 {
925 struct fuse_dispatcher fdi;
926 struct fuse_vnode_data *fvdat = VTOFUD(vp);
927 struct fuse_getattr_in *fgai;
928 struct fuse_attr_out *fao;
929 off_t old_filesize = fvdat->cached_attrs.va_size;
930 struct timespec old_atime = fvdat->cached_attrs.va_atime;
931 struct timespec old_ctime = fvdat->cached_attrs.va_ctime;
932 struct timespec old_mtime = fvdat->cached_attrs.va_mtime;
933 enum vtype vtyp;
934 int err;
935
936 ASSERT_VOP_LOCKED(vp, __func__);
937
938 fdisp_init(&fdi, sizeof(*fgai));
939 fdisp_make_vp(&fdi, FUSE_GETATTR, vp, td, cred);
940 fgai = fdi.indata;
941 /*
942 * We could look up a file handle and set it in fgai->fh, but that
943 * involves extra runtime work and I'm unaware of any file systems that
944 * care.
945 */
946 fgai->getattr_flags = 0;
947 if ((err = fdisp_wait_answ(&fdi))) {
948 if (err == ENOENT)
949 fuse_internal_vnode_disappear(vp);
950 goto out;
951 }
952
953 fao = (struct fuse_attr_out *)fdi.answ;
954 vtyp = IFTOVT(fao->attr.mode);
955 if (fvdat->flag & FN_SIZECHANGE)
956 fao->attr.size = old_filesize;
957 if (fvdat->flag & FN_ATIMECHANGE) {
958 fao->attr.atime = old_atime.tv_sec;
959 fao->attr.atimensec = old_atime.tv_nsec;
960 }
961 if (fvdat->flag & FN_CTIMECHANGE) {
962 fao->attr.ctime = old_ctime.tv_sec;
963 fao->attr.ctimensec = old_ctime.tv_nsec;
964 }
965 if (fvdat->flag & FN_MTIMECHANGE) {
966 fao->attr.mtime = old_mtime.tv_sec;
967 fao->attr.mtimensec = old_mtime.tv_nsec;
968 }
969 fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
970 fao->attr_valid_nsec, vap, true);
971 if (vtyp != vnode_vtype(vp)) {
972 fuse_internal_vnode_disappear(vp);
973 err = ENOENT;
974 }
975
976 out:
977 fdisp_destroy(&fdi);
978 return err;
979 }
980
981 /* Read a vnode's attributes from cache or fetch them from the fuse daemon */
982 int
fuse_internal_getattr(struct vnode * vp,struct vattr * vap,struct ucred * cred,struct thread * td)983 fuse_internal_getattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
984 struct thread *td)
985 {
986 struct vattr *attrs;
987
988 if ((attrs = VTOVA(vp)) != NULL) {
989 *vap = *attrs; /* struct copy */
990 return 0;
991 }
992
993 return fuse_internal_do_getattr(vp, vap, cred, td);
994 }
995
996 void
fuse_internal_vnode_disappear(struct vnode * vp)997 fuse_internal_vnode_disappear(struct vnode *vp)
998 {
999 struct fuse_vnode_data *fvdat = VTOFUD(vp);
1000
1001 ASSERT_VOP_ELOCKED(vp, "fuse_internal_vnode_disappear");
1002 fvdat->flag |= FN_REVOKED;
1003 cache_purge(vp);
1004 }
1005
1006 /* fuse start/stop */
1007
1008 SDT_PROBE_DEFINE2(fusefs, , internal, init_done,
1009 "struct fuse_data*", "struct fuse_init_out*");
1010 int
fuse_internal_init_callback(struct fuse_ticket * tick,struct uio * uio)1011 fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
1012 {
1013 int err = 0;
1014 struct fuse_data *data = tick->tk_data;
1015 struct fuse_init_out *fiio = NULL;
1016
1017 if ((err = tick->tk_aw_ohead.error)) {
1018 goto out;
1019 }
1020 if ((err = fticket_pull(tick, uio))) {
1021 goto out;
1022 }
1023 fiio = fticket_resp(tick)->base;
1024
1025 data->fuse_libabi_major = fiio->major;
1026 data->fuse_libabi_minor = fiio->minor;
1027 if (!fuse_libabi_geq(data, 7, 4)) {
1028 /*
1029 * With a little work we could support servers as old as 7.1.
1030 * But there would be little payoff.
1031 */
1032 SDT_PROBE2(fusefs, , internal, trace, 1,
1033 "userpace version too low");
1034 err = EPROTONOSUPPORT;
1035 goto out;
1036 }
1037
1038 if (fuse_libabi_geq(data, 7, 5)) {
1039 if (fticket_resp(tick)->len == sizeof(struct fuse_init_out) ||
1040 fticket_resp(tick)->len == FUSE_COMPAT_22_INIT_OUT_SIZE) {
1041 data->max_write = fiio->max_write;
1042 if (fiio->flags & FUSE_ASYNC_READ)
1043 data->dataflags |= FSESS_ASYNC_READ;
1044 if (fiio->flags & FUSE_POSIX_LOCKS)
1045 data->dataflags |= FSESS_POSIX_LOCKS;
1046 if (fiio->flags & FUSE_EXPORT_SUPPORT)
1047 data->dataflags |= FSESS_EXPORT_SUPPORT;
1048 /*
1049 * Don't bother to check FUSE_BIG_WRITES, because it's
1050 * redundant with max_write
1051 */
1052 /*
1053 * max_background and congestion_threshold are not
1054 * implemented
1055 */
1056 } else {
1057 err = EINVAL;
1058 }
1059 } else {
1060 /* Old fixed values */
1061 data->max_write = 4096;
1062 }
1063
1064 if (fuse_libabi_geq(data, 7, 6))
1065 data->max_readahead_blocks = fiio->max_readahead / maxbcachebuf;
1066
1067 if (!fuse_libabi_geq(data, 7, 7))
1068 fsess_set_notimpl(data->mp, FUSE_INTERRUPT);
1069
1070 if (!fuse_libabi_geq(data, 7, 8)) {
1071 fsess_set_notimpl(data->mp, FUSE_BMAP);
1072 fsess_set_notimpl(data->mp, FUSE_DESTROY);
1073 }
1074
1075 if (fuse_libabi_geq(data, 7, 23) && fiio->time_gran >= 1 &&
1076 fiio->time_gran <= 1000000000)
1077 data->time_gran = fiio->time_gran;
1078 else
1079 data->time_gran = 1;
1080
1081 if (!fuse_libabi_geq(data, 7, 23))
1082 data->cache_mode = fuse_data_cache_mode;
1083 else if (fiio->flags & FUSE_WRITEBACK_CACHE)
1084 data->cache_mode = FUSE_CACHE_WB;
1085 else
1086 data->cache_mode = FUSE_CACHE_WT;
1087
1088 out:
1089 if (err) {
1090 fdata_set_dead(data);
1091 }
1092 FUSE_LOCK();
1093 data->dataflags |= FSESS_INITED;
1094 SDT_PROBE2(fusefs, , internal, init_done, data, fiio);
1095 wakeup(&data->ticketer);
1096 FUSE_UNLOCK();
1097
1098 return 0;
1099 }
1100
1101 void
fuse_internal_send_init(struct fuse_data * data,struct thread * td)1102 fuse_internal_send_init(struct fuse_data *data, struct thread *td)
1103 {
1104 struct fuse_init_in *fiii;
1105 struct fuse_dispatcher fdi;
1106
1107 fdisp_init(&fdi, sizeof(*fiii));
1108 fdisp_make(&fdi, FUSE_INIT, data->mp, 0, td, NULL);
1109 fiii = fdi.indata;
1110 fiii->major = FUSE_KERNEL_VERSION;
1111 fiii->minor = FUSE_KERNEL_MINOR_VERSION;
1112 /*
1113 * fusefs currently reads ahead no more than one cache block at a time.
1114 * See fuse_read_biobackend
1115 */
1116 fiii->max_readahead = maxbcachebuf;
1117 /*
1118 * Unsupported features:
1119 * FUSE_FILE_OPS: No known FUSE server or client supports it
1120 * FUSE_ATOMIC_O_TRUNC: our VFS cannot support it
1121 * FUSE_DONT_MASK: unlike Linux, FreeBSD always applies the umask, even
1122 * when default ACLs are in use.
1123 * FUSE_SPLICE_WRITE, FUSE_SPLICE_MOVE, FUSE_SPLICE_READ: FreeBSD
1124 * doesn't have splice(2).
1125 * FUSE_FLOCK_LOCKS: not yet implemented
1126 * FUSE_HAS_IOCTL_DIR: not yet implemented
1127 * FUSE_AUTO_INVAL_DATA: not yet implemented
1128 * FUSE_DO_READDIRPLUS: not yet implemented
1129 * FUSE_READDIRPLUS_AUTO: not yet implemented
1130 * FUSE_ASYNC_DIO: not yet implemented
1131 * FUSE_NO_OPEN_SUPPORT: not yet implemented
1132 */
1133 fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT
1134 | FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE;
1135
1136 fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
1137 fuse_insert_message(fdi.tick, false);
1138 fdisp_destroy(&fdi);
1139 }
1140
1141 /*
1142 * Send a FUSE_SETATTR operation with no permissions checks. If cred is NULL,
1143 * send the request with root credentials
1144 */
fuse_internal_setattr(struct vnode * vp,struct vattr * vap,struct thread * td,struct ucred * cred)1145 int fuse_internal_setattr(struct vnode *vp, struct vattr *vap,
1146 struct thread *td, struct ucred *cred)
1147 {
1148 struct fuse_vnode_data *fvdat;
1149 struct fuse_dispatcher fdi;
1150 struct fuse_setattr_in *fsai;
1151 struct mount *mp;
1152 pid_t pid = td->td_proc->p_pid;
1153 struct fuse_data *data;
1154 int dataflags;
1155 int err = 0;
1156 enum vtype vtyp;
1157 int sizechanged = -1;
1158 uint64_t newsize = 0;
1159
1160 ASSERT_VOP_ELOCKED(vp, __func__);
1161
1162 mp = vnode_mount(vp);
1163 fvdat = VTOFUD(vp);
1164 data = fuse_get_mpdata(mp);
1165 dataflags = data->dataflags;
1166
1167 fdisp_init(&fdi, sizeof(*fsai));
1168 fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
1169 if (!cred) {
1170 fdi.finh->uid = 0;
1171 fdi.finh->gid = 0;
1172 }
1173 fsai = fdi.indata;
1174 fsai->valid = 0;
1175
1176 if (vap->va_uid != (uid_t)VNOVAL) {
1177 fsai->uid = vap->va_uid;
1178 fsai->valid |= FATTR_UID;
1179 }
1180 if (vap->va_gid != (gid_t)VNOVAL) {
1181 fsai->gid = vap->va_gid;
1182 fsai->valid |= FATTR_GID;
1183 }
1184 if (vap->va_size != VNOVAL) {
1185 struct fuse_filehandle *fufh = NULL;
1186
1187 /*Truncate to a new value. */
1188 fsai->size = vap->va_size;
1189 sizechanged = 1;
1190 newsize = vap->va_size;
1191 fsai->valid |= FATTR_SIZE;
1192
1193 fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
1194 if (fufh) {
1195 fsai->fh = fufh->fh_id;
1196 fsai->valid |= FATTR_FH;
1197 }
1198 VTOFUD(vp)->flag &= ~FN_SIZECHANGE;
1199 }
1200 if (vap->va_atime.tv_sec != VNOVAL) {
1201 fsai->atime = vap->va_atime.tv_sec;
1202 fsai->atimensec = vap->va_atime.tv_nsec;
1203 fsai->valid |= FATTR_ATIME;
1204 if (vap->va_vaflags & VA_UTIMES_NULL)
1205 fsai->valid |= FATTR_ATIME_NOW;
1206 } else if (fvdat->flag & FN_ATIMECHANGE) {
1207 fsai->atime = fvdat->cached_attrs.va_atime.tv_sec;
1208 fsai->atimensec = fvdat->cached_attrs.va_atime.tv_nsec;
1209 fsai->valid |= FATTR_ATIME;
1210 }
1211 if (vap->va_mtime.tv_sec != VNOVAL) {
1212 fsai->mtime = vap->va_mtime.tv_sec;
1213 fsai->mtimensec = vap->va_mtime.tv_nsec;
1214 fsai->valid |= FATTR_MTIME;
1215 if (vap->va_vaflags & VA_UTIMES_NULL)
1216 fsai->valid |= FATTR_MTIME_NOW;
1217 } else if (fvdat->flag & FN_MTIMECHANGE) {
1218 fsai->mtime = fvdat->cached_attrs.va_mtime.tv_sec;
1219 fsai->mtimensec = fvdat->cached_attrs.va_mtime.tv_nsec;
1220 fsai->valid |= FATTR_MTIME;
1221 }
1222 if (fuse_libabi_geq(data, 7, 23) && fvdat->flag & FN_CTIMECHANGE) {
1223 fsai->ctime = fvdat->cached_attrs.va_ctime.tv_sec;
1224 fsai->ctimensec = fvdat->cached_attrs.va_ctime.tv_nsec;
1225 fsai->valid |= FATTR_CTIME;
1226 }
1227 if (vap->va_mode != (mode_t)VNOVAL) {
1228 fsai->mode = vap->va_mode & ALLPERMS;
1229 fsai->valid |= FATTR_MODE;
1230 }
1231 if (!fsai->valid) {
1232 goto out;
1233 }
1234
1235 if ((err = fdisp_wait_answ(&fdi)))
1236 goto out;
1237 vtyp = IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode);
1238
1239 if (vnode_vtype(vp) != vtyp) {
1240 if (vnode_vtype(vp) == VNON && vtyp != VNON) {
1241 SDT_PROBE2(fusefs, , internal, trace, 1, "FUSE: Dang! "
1242 "vnode_vtype is VNON and vtype isn't.");
1243 } else {
1244 /*
1245 * STALE vnode, ditch
1246 *
1247 * The vnode has changed its type "behind our back".
1248 * This probably means that the file got deleted and
1249 * recreated on the server, with the same inode.
1250 * There's nothing really we can do, so let us just
1251 * return ENOENT. After all, the entry must not have
1252 * existed in the recent past. If the user tries
1253 * again, it will work.
1254 */
1255 fuse_internal_vnode_disappear(vp);
1256 err = ENOENT;
1257 }
1258 }
1259 if (err == 0) {
1260 struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
1261 fuse_vnode_undirty_cached_timestamps(vp, true);
1262 fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
1263 fao->attr_valid_nsec, NULL, false);
1264 getnanouptime(&fvdat->last_local_modify);
1265 }
1266
1267 out:
1268 fdisp_destroy(&fdi);
1269 return err;
1270 }
1271
1272 #ifdef ZERO_PAD_INCOMPLETE_BUFS
1273 static int
isbzero(void * buf,size_t len)1274 isbzero(void *buf, size_t len)
1275 {
1276 int i;
1277
1278 for (i = 0; i < len; i++) {
1279 if (((char *)buf)[i])
1280 return (0);
1281 }
1282
1283 return (1);
1284 }
1285
1286 #endif
1287
1288 void
fuse_internal_init(void)1289 fuse_internal_init(void)
1290 {
1291 fuse_lookup_cache_misses = counter_u64_alloc(M_WAITOK);
1292 fuse_lookup_cache_hits = counter_u64_alloc(M_WAITOK);
1293 }
1294
1295 void
fuse_internal_destroy(void)1296 fuse_internal_destroy(void)
1297 {
1298 counter_u64_free(fuse_lookup_cache_hits);
1299 counter_u64_free(fuse_lookup_cache_misses);
1300 }
1301