]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/fuse/fuse_internal.c
fusefs: set the flags fields of fuse_write_in and fuse_read_in
[FreeBSD/FreeBSD.git] / sys / fs / fuse / fuse_internal.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2007-2009 Google Inc. and Amit Singh
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  *   copyright notice, this list of conditions and the following disclaimer
15  *   in the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of Google Inc. nor the names of its
18  *   contributors may be used to endorse or promote products derived from
19  *   this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Copyright (C) 2005 Csaba Henk.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57
58 #include <sys/cdefs.h>
59 __FBSDID("$FreeBSD$");
60
61 #include <sys/param.h>
62 #include <sys/module.h>
63 #include <sys/systm.h>
64 #include <sys/errno.h>
65 #include <sys/kernel.h>
66 #include <sys/conf.h>
67 #include <sys/uio.h>
68 #include <sys/malloc.h>
69 #include <sys/queue.h>
70 #include <sys/lock.h>
71 #include <sys/mutex.h>
72 #include <sys/sdt.h>
73 #include <sys/sx.h>
74 #include <sys/proc.h>
75 #include <sys/mount.h>
76 #include <sys/vnode.h>
77 #include <sys/namei.h>
78 #include <sys/stat.h>
79 #include <sys/unistd.h>
80 #include <sys/filedesc.h>
81 #include <sys/file.h>
82 #include <sys/fcntl.h>
83 #include <sys/dirent.h>
84 #include <sys/bio.h>
85 #include <sys/buf.h>
86 #include <sys/sysctl.h>
87 #include <sys/priv.h>
88
89 #include "fuse.h"
90 #include "fuse_file.h"
91 #include "fuse_internal.h"
92 #include "fuse_ipc.h"
93 #include "fuse_node.h"
94 #include "fuse_file.h"
95
96 SDT_PROVIDER_DECLARE(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_getattr_in *fgai;
684         struct fuse_attr_out *fao;
685         off_t old_filesize = fvdat->cached_attrs.va_size;
686         enum vtype vtyp;
687         int err;
688
689         fdisp_init(&fdi, 0);
690         fdisp_make_vp(&fdi, FUSE_GETATTR, vp, td, cred);
691         fgai = fdi.indata;
692         /* 
693          * We could look up a file handle and set it in fgai->fh, but that
694          * involves extra runtime work and I'm unaware of any file systems that
695          * care.
696          */
697         fgai->getattr_flags = 0;
698         if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, td, cred))) {
699                 if (err == ENOENT)
700                         fuse_internal_vnode_disappear(vp);
701                 goto out;
702         }
703
704         fao = (struct fuse_attr_out *)fdi.answ;
705         vtyp = IFTOVT(fao->attr.mode);
706         if (fvdat->flag & FN_SIZECHANGE)
707                 fao->attr.size = old_filesize;
708         fuse_internal_cache_attrs(vp, NULL, &fao->attr, fao->attr_valid,
709                 fao->attr_valid_nsec, vap);
710         if (vtyp != vnode_vtype(vp)) {
711                 fuse_internal_vnode_disappear(vp);
712                 err = ENOENT;
713         }
714
715 out:
716         fdisp_destroy(&fdi);
717         return err;
718 }
719
720 /* Read a vnode's attributes from cache or fetch them from the fuse daemon */
721 int
722 fuse_internal_getattr(struct vnode *vp, struct vattr *vap, struct ucred *cred,
723         struct thread *td)
724 {
725         struct vattr *attrs;
726
727         if ((attrs = VTOVA(vp)) != NULL) {
728                 *vap = *attrs;  /* struct copy */
729                 return 0;
730         }
731
732         return fuse_internal_do_getattr(vp, vap, cred, td);
733 }
734
735 void
736 fuse_internal_vnode_disappear(struct vnode *vp)
737 {
738         struct fuse_vnode_data *fvdat = VTOFUD(vp);
739
740         ASSERT_VOP_ELOCKED(vp, "fuse_internal_vnode_disappear");
741         fvdat->flag |= FN_REVOKED;
742         bintime_clear(&fvdat->attr_cache_timeout);
743         cache_purge(vp);
744 }
745
746 /* fuse start/stop */
747
748 int
749 fuse_internal_init_callback(struct fuse_ticket *tick, struct uio *uio)
750 {
751         int err = 0;
752         struct fuse_data *data = tick->tk_data;
753         struct fuse_init_out *fiio;
754
755         if ((err = tick->tk_aw_ohead.error)) {
756                 goto out;
757         }
758         if ((err = fticket_pull(tick, uio))) {
759                 goto out;
760         }
761         fiio = fticket_resp(tick)->base;
762
763         /* XXX: Do we want to check anything further besides this? */
764         if (fiio->major < 7) {
765                 SDT_PROBE2(fusefs, , internal, trace, 1,
766                         "userpace version too low");
767                 err = EPROTONOSUPPORT;
768                 goto out;
769         }
770         data->fuse_libabi_major = fiio->major;
771         data->fuse_libabi_minor = fiio->minor;
772
773         if (fuse_libabi_geq(data, 7, 5)) {
774                 if (fticket_resp(tick)->len == sizeof(struct fuse_init_out)) {
775                         data->max_write = fiio->max_write;
776                         if (fiio->flags & FUSE_ASYNC_READ)
777                                 data->dataflags |= FSESS_ASYNC_READ;
778                         if (fiio->flags & FUSE_POSIX_LOCKS)
779                                 data->dataflags |= FSESS_POSIX_LOCKS;
780                         if (fiio->flags & FUSE_EXPORT_SUPPORT)
781                                 data->dataflags |= FSESS_EXPORT_SUPPORT;
782                         /* 
783                          * Don't bother to check FUSE_BIG_WRITES, because it's
784                          * redundant with max_write
785                          */
786                 } else {
787                         err = EINVAL;
788                 }
789         } else {
790                 /* Old fix values */
791                 data->max_write = 4096;
792         }
793
794 out:
795         if (err) {
796                 fdata_set_dead(data);
797         }
798         FUSE_LOCK();
799         data->dataflags |= FSESS_INITED;
800         wakeup(&data->ticketer);
801         FUSE_UNLOCK();
802
803         return 0;
804 }
805
806 void
807 fuse_internal_send_init(struct fuse_data *data, struct thread *td)
808 {
809         struct fuse_init_in *fiii;
810         struct fuse_dispatcher fdi;
811
812         fdisp_init(&fdi, sizeof(*fiii));
813         fdisp_make(&fdi, FUSE_INIT, data->mp, 0, td, NULL);
814         fiii = fdi.indata;
815         fiii->major = FUSE_KERNEL_VERSION;
816         fiii->minor = FUSE_KERNEL_MINOR_VERSION;
817         /* 
818          * fusefs currently doesn't do any readahead other than fetching whole
819          * buffer cache block sized regions at once.  So the max readahead is
820          * the size of a buffer cache block.
821          */
822         fiii->max_readahead = maxbcachebuf;
823         /*
824          * Unsupported features:
825          * FUSE_FILE_OPS: No known FUSE server or client supports it
826          * FUSE_ATOMIC_O_TRUNC: our VFS cannot support it
827          */
828         fiii->flags = FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_EXPORT_SUPPORT
829                 | FUSE_BIG_WRITES;
830
831         fuse_insert_callback(fdi.tick, fuse_internal_init_callback);
832         fuse_insert_message(fdi.tick, false);
833         fdisp_destroy(&fdi);
834 }
835
836 /* 
837  * Send a FUSE_SETATTR operation with no permissions checks.  If cred is NULL,
838  * send the request with root credentials
839  */
840 int fuse_internal_setattr(struct vnode *vp, struct vattr *vap,
841         struct thread *td, struct ucred *cred)
842 {
843         struct fuse_dispatcher fdi;
844         struct fuse_setattr_in *fsai;
845         struct mount *mp;
846         pid_t pid = td->td_proc->p_pid;
847         struct fuse_data *data;
848         int dataflags;
849         int err = 0;
850         enum vtype vtyp;
851         int sizechanged = -1;
852         uint64_t newsize = 0;
853
854         mp = vnode_mount(vp);
855         data = fuse_get_mpdata(mp);
856         dataflags = data->dataflags;
857
858         fdisp_init(&fdi, sizeof(*fsai));
859         fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
860         if (!cred) {
861                 fdi.finh->uid = 0;
862                 fdi.finh->gid = 0;
863         }
864         fsai = fdi.indata;
865         fsai->valid = 0;
866
867         if (vap->va_uid != (uid_t)VNOVAL) {
868                 fsai->uid = vap->va_uid;
869                 fsai->valid |= FATTR_UID;
870         }
871         if (vap->va_gid != (gid_t)VNOVAL) {
872                 fsai->gid = vap->va_gid;
873                 fsai->valid |= FATTR_GID;
874         }
875         if (vap->va_size != VNOVAL) {
876                 struct fuse_filehandle *fufh = NULL;
877
878                 /*Truncate to a new value. */
879                 fsai->size = vap->va_size;
880                 sizechanged = 1;
881                 newsize = vap->va_size;
882                 fsai->valid |= FATTR_SIZE;
883
884                 fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
885                 if (fufh) {
886                         fsai->fh = fufh->fh_id;
887                         fsai->valid |= FATTR_FH;
888                 }
889                 VTOFUD(vp)->flag &= ~FN_SIZECHANGE;
890         }
891         if (vap->va_atime.tv_sec != VNOVAL) {
892                 fsai->atime = vap->va_atime.tv_sec;
893                 fsai->atimensec = vap->va_atime.tv_nsec;
894                 fsai->valid |= FATTR_ATIME;
895                 if (vap->va_vaflags & VA_UTIMES_NULL)
896                         fsai->valid |= FATTR_ATIME_NOW;
897         }
898         if (vap->va_mtime.tv_sec != VNOVAL) {
899                 fsai->mtime = vap->va_mtime.tv_sec;
900                 fsai->mtimensec = vap->va_mtime.tv_nsec;
901                 fsai->valid |= FATTR_MTIME;
902                 if (vap->va_vaflags & VA_UTIMES_NULL)
903                         fsai->valid |= FATTR_MTIME_NOW;
904         }
905         if (vap->va_mode != (mode_t)VNOVAL) {
906                 fsai->mode = vap->va_mode & ALLPERMS;
907                 fsai->valid |= FATTR_MODE;
908         }
909         if (!fsai->valid) {
910                 goto out;
911         }
912
913         if ((err = fdisp_wait_answ(&fdi)))
914                 goto out;
915         vtyp = IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode);
916
917         if (vnode_vtype(vp) != vtyp) {
918                 if (vnode_vtype(vp) == VNON && vtyp != VNON) {
919                         SDT_PROBE2(fusefs, , internal, trace, 1, "FUSE: Dang! "
920                                 "vnode_vtype is VNON and vtype isn't.");
921                 } else {
922                         /*
923                          * STALE vnode, ditch
924                          *
925                          * The vnode has changed its type "behind our back".
926                          * There's nothing really we can do, so let us just
927                          * force an internal revocation and tell the caller to
928                          * try again, if interested.
929                          */
930                         fuse_internal_vnode_disappear(vp);
931                         err = EAGAIN;
932                 }
933         }
934         if (err == 0) {
935                 struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
936                 fuse_internal_cache_attrs(vp, cred, &fao->attr, fao->attr_valid,
937                         fao->attr_valid_nsec, NULL);
938         }
939
940 out:
941         fdisp_destroy(&fdi);
942         return err;
943 }
944
945 #ifdef ZERO_PAD_INCOMPLETE_BUFS
946 static int
947 isbzero(void *buf, size_t len)
948 {
949         int i;
950
951         for (i = 0; i < len; i++) {
952                 if (((char *)buf)[i])
953                         return (0);
954         }
955
956         return (1);
957 }
958
959 #endif