151369649SPedro F. Giffuni /*-
251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni *
45fe58019SAttilio Rao * Copyright (c) 2007-2009 Google Inc. and Amit Singh
55fe58019SAttilio Rao * All rights reserved.
65fe58019SAttilio Rao *
75fe58019SAttilio Rao * Redistribution and use in source and binary forms, with or without
85fe58019SAttilio Rao * modification, are permitted provided that the following conditions are
95fe58019SAttilio Rao * met:
105fe58019SAttilio Rao *
115fe58019SAttilio Rao * * Redistributions of source code must retain the above copyright
125fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer.
135fe58019SAttilio Rao * * Redistributions in binary form must reproduce the above
145fe58019SAttilio Rao * copyright notice, this list of conditions and the following disclaimer
155fe58019SAttilio Rao * in the documentation and/or other materials provided with the
165fe58019SAttilio Rao * distribution.
175fe58019SAttilio Rao * * Neither the name of Google Inc. nor the names of its
185fe58019SAttilio Rao * contributors may be used to endorse or promote products derived from
195fe58019SAttilio Rao * this software without specific prior written permission.
205fe58019SAttilio Rao *
215fe58019SAttilio Rao * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
225fe58019SAttilio Rao * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
235fe58019SAttilio Rao * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
245fe58019SAttilio Rao * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
255fe58019SAttilio Rao * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
265fe58019SAttilio Rao * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
275fe58019SAttilio Rao * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
285fe58019SAttilio Rao * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
295fe58019SAttilio Rao * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
305fe58019SAttilio Rao * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
315fe58019SAttilio Rao * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
325fe58019SAttilio Rao *
335fe58019SAttilio Rao * Copyright (C) 2005 Csaba Henk.
345fe58019SAttilio Rao * All rights reserved.
355fe58019SAttilio Rao *
368aafc8c3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
378aafc8c3SAlan Somers *
388aafc8c3SAlan Somers * Portions of this software were developed by BFF Storage Systems, LLC under
398aafc8c3SAlan Somers * sponsorship from the FreeBSD Foundation.
408aafc8c3SAlan Somers *
415fe58019SAttilio Rao * Redistribution and use in source and binary forms, with or without
425fe58019SAttilio Rao * modification, are permitted provided that the following conditions
435fe58019SAttilio Rao * are met:
445fe58019SAttilio Rao * 1. Redistributions of source code must retain the above copyright
455fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer.
465fe58019SAttilio Rao * 2. Redistributions in binary form must reproduce the above copyright
475fe58019SAttilio Rao * notice, this list of conditions and the following disclaimer in the
485fe58019SAttilio Rao * documentation and/or other materials provided with the distribution.
495fe58019SAttilio Rao *
505fe58019SAttilio Rao * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
515fe58019SAttilio Rao * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
525fe58019SAttilio Rao * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
535fe58019SAttilio Rao * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
545fe58019SAttilio Rao * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
555fe58019SAttilio Rao * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
565fe58019SAttilio Rao * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
575fe58019SAttilio Rao * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
585fe58019SAttilio Rao * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
595fe58019SAttilio Rao * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
605fe58019SAttilio Rao * SUCH DAMAGE.
615fe58019SAttilio Rao */
625fe58019SAttilio Rao
63cf169498SAlan Somers #include <sys/param.h>
645fe58019SAttilio Rao #include <sys/module.h>
655fe58019SAttilio Rao #include <sys/systm.h>
665fe58019SAttilio Rao #include <sys/errno.h>
675fe58019SAttilio Rao #include <sys/kernel.h>
685fe58019SAttilio Rao #include <sys/conf.h>
6937df9d3bSAlan Somers #include <sys/filio.h>
705fe58019SAttilio Rao #include <sys/uio.h>
715fe58019SAttilio Rao #include <sys/malloc.h>
725fe58019SAttilio Rao #include <sys/queue.h>
7391898857SMark Johnston #include <sys/limits.h>
745fe58019SAttilio Rao #include <sys/lock.h>
7589f6b863SAttilio Rao #include <sys/rwlock.h>
765fe58019SAttilio Rao #include <sys/sx.h>
775fe58019SAttilio Rao #include <sys/proc.h>
785fe58019SAttilio Rao #include <sys/mount.h>
795fe58019SAttilio Rao #include <sys/vnode.h>
805fe58019SAttilio Rao #include <sys/namei.h>
8104660064SFedor Uporov #include <sys/extattr.h>
825fe58019SAttilio Rao #include <sys/stat.h>
835fe58019SAttilio Rao #include <sys/unistd.h>
845fe58019SAttilio Rao #include <sys/filedesc.h>
855fe58019SAttilio Rao #include <sys/file.h>
865fe58019SAttilio Rao #include <sys/fcntl.h>
875fe58019SAttilio Rao #include <sys/dirent.h>
885fe58019SAttilio Rao #include <sys/bio.h>
895fe58019SAttilio Rao #include <sys/buf.h>
905fe58019SAttilio Rao #include <sys/sysctl.h>
91ca148cdaSGleb Smirnoff #include <sys/vmmeter.h>
925fe58019SAttilio Rao
935fe58019SAttilio Rao #include <vm/vm.h>
945fe58019SAttilio Rao #include <vm/vm_extern.h>
955fe58019SAttilio Rao #include <vm/pmap.h>
965fe58019SAttilio Rao #include <vm/vm_map.h>
975fe58019SAttilio Rao #include <vm/vm_page.h>
985fe58019SAttilio Rao #include <vm/vm_param.h>
995fe58019SAttilio Rao #include <vm/vm_object.h>
1005fe58019SAttilio Rao #include <vm/vm_pager.h>
1015fe58019SAttilio Rao #include <vm/vnode_pager.h>
1025fe58019SAttilio Rao #include <vm/vm_object.h>
1035fe58019SAttilio Rao
1045fe58019SAttilio Rao #include "fuse.h"
1055fe58019SAttilio Rao #include "fuse_file.h"
1065fe58019SAttilio Rao #include "fuse_internal.h"
1075fe58019SAttilio Rao #include "fuse_ipc.h"
1085fe58019SAttilio Rao #include "fuse_node.h"
1095fe58019SAttilio Rao #include "fuse_io.h"
1105fe58019SAttilio Rao
1115fe58019SAttilio Rao #include <sys/priv.h>
1125fe58019SAttilio Rao
113fd2749f2SAlan Somers /* Maximum number of hardlinks to a single FUSE file */
114fd2749f2SAlan Somers #define FUSE_LINK_MAX UINT32_MAX
115fd2749f2SAlan Somers
116419e7ff6SAlan Somers SDT_PROVIDER_DECLARE(fusefs);
117cf169498SAlan Somers /*
118cf169498SAlan Somers * Fuse trace probe:
119cf169498SAlan Somers * arg0: verbosity. Higher numbers give more verbose messages
120cf169498SAlan Somers * arg1: Textual message
121cf169498SAlan Somers */
122419e7ff6SAlan Somers SDT_PROBE_DEFINE2(fusefs, , vnops, trace, "int", "char*");
1235fe58019SAttilio Rao
1245fe58019SAttilio Rao /* vnode ops */
1255fe58019SAttilio Rao static vop_access_t fuse_vnop_access;
126f067b609SAlan Somers static vop_advlock_t fuse_vnop_advlock;
127398c88c7SAlan Somers static vop_allocate_t fuse_vnop_allocate;
128a1c9f4adSAlan Somers static vop_bmap_t fuse_vnop_bmap;
129f9b0e30bSAlan Somers static vop_close_t fuse_fifo_close;
1305fe58019SAttilio Rao static vop_close_t fuse_vnop_close;
13192bbfe1fSAlan Somers static vop_copy_file_range_t fuse_vnop_copy_file_range;
1325fe58019SAttilio Rao static vop_create_t fuse_vnop_create;
13389d57b94SAlan Somers static vop_deallocate_t fuse_vnop_deallocate;
13404660064SFedor Uporov static vop_deleteextattr_t fuse_vnop_deleteextattr;
135915012e0SAlan Somers static vop_fdatasync_t fuse_vnop_fdatasync;
1365fe58019SAttilio Rao static vop_fsync_t fuse_vnop_fsync;
1375fe58019SAttilio Rao static vop_getattr_t fuse_vnop_getattr;
13804660064SFedor Uporov static vop_getextattr_t fuse_vnop_getextattr;
1395fe58019SAttilio Rao static vop_inactive_t fuse_vnop_inactive;
14037df9d3bSAlan Somers static vop_ioctl_t fuse_vnop_ioctl;
1415fe58019SAttilio Rao static vop_link_t fuse_vnop_link;
14204660064SFedor Uporov static vop_listextattr_t fuse_vnop_listextattr;
1435fe58019SAttilio Rao static vop_lookup_t fuse_vnop_lookup;
1445fe58019SAttilio Rao static vop_mkdir_t fuse_vnop_mkdir;
1455fe58019SAttilio Rao static vop_mknod_t fuse_vnop_mknod;
1465fe58019SAttilio Rao static vop_open_t fuse_vnop_open;
147746c92e0SJohn Baldwin static vop_pathconf_t fuse_vnop_pathconf;
1485fe58019SAttilio Rao static vop_read_t fuse_vnop_read;
1495fe58019SAttilio Rao static vop_readdir_t fuse_vnop_readdir;
1505fe58019SAttilio Rao static vop_readlink_t fuse_vnop_readlink;
1515fe58019SAttilio Rao static vop_reclaim_t fuse_vnop_reclaim;
1525fe58019SAttilio Rao static vop_remove_t fuse_vnop_remove;
1535fe58019SAttilio Rao static vop_rename_t fuse_vnop_rename;
1545fe58019SAttilio Rao static vop_rmdir_t fuse_vnop_rmdir;
1555fe58019SAttilio Rao static vop_setattr_t fuse_vnop_setattr;
15604660064SFedor Uporov static vop_setextattr_t fuse_vnop_setextattr;
1575fe58019SAttilio Rao static vop_strategy_t fuse_vnop_strategy;
1585fe58019SAttilio Rao static vop_symlink_t fuse_vnop_symlink;
1595fe58019SAttilio Rao static vop_write_t fuse_vnop_write;
1605fe58019SAttilio Rao static vop_getpages_t fuse_vnop_getpages;
1615fe58019SAttilio Rao static vop_print_t fuse_vnop_print;
162e5b50fe7SAlan Somers static vop_vptofh_t fuse_vnop_vptofh;
1635fe58019SAttilio Rao
164f9b0e30bSAlan Somers struct vop_vector fuse_fifoops = {
165f9b0e30bSAlan Somers .vop_default = &fifo_specops,
166f9b0e30bSAlan Somers .vop_access = fuse_vnop_access,
167f9b0e30bSAlan Somers .vop_close = fuse_fifo_close,
168f9b0e30bSAlan Somers .vop_fsync = fuse_vnop_fsync,
169f9b0e30bSAlan Somers .vop_getattr = fuse_vnop_getattr,
170f9b0e30bSAlan Somers .vop_inactive = fuse_vnop_inactive,
171f9b0e30bSAlan Somers .vop_pathconf = fuse_vnop_pathconf,
172f9b0e30bSAlan Somers .vop_print = fuse_vnop_print,
173f9b0e30bSAlan Somers .vop_read = VOP_PANIC,
174f9b0e30bSAlan Somers .vop_reclaim = fuse_vnop_reclaim,
175f9b0e30bSAlan Somers .vop_setattr = fuse_vnop_setattr,
176f9b0e30bSAlan Somers .vop_write = VOP_PANIC,
177e5b50fe7SAlan Somers .vop_vptofh = fuse_vnop_vptofh,
178f9b0e30bSAlan Somers };
1796fa079fcSMateusz Guzik VFS_VOP_VECTOR_REGISTER(fuse_fifoops);
180f9b0e30bSAlan Somers
1815fe58019SAttilio Rao struct vop_vector fuse_vnops = {
182398c88c7SAlan Somers .vop_allocate = fuse_vnop_allocate,
1835fe58019SAttilio Rao .vop_default = &default_vnodeops,
1845fe58019SAttilio Rao .vop_access = fuse_vnop_access,
185f067b609SAlan Somers .vop_advlock = fuse_vnop_advlock,
186a1c9f4adSAlan Somers .vop_bmap = fuse_vnop_bmap,
1875fe58019SAttilio Rao .vop_close = fuse_vnop_close,
18892bbfe1fSAlan Somers .vop_copy_file_range = fuse_vnop_copy_file_range,
1895fe58019SAttilio Rao .vop_create = fuse_vnop_create,
19089d57b94SAlan Somers .vop_deallocate = fuse_vnop_deallocate,
19104660064SFedor Uporov .vop_deleteextattr = fuse_vnop_deleteextattr,
1925fe58019SAttilio Rao .vop_fsync = fuse_vnop_fsync,
193915012e0SAlan Somers .vop_fdatasync = fuse_vnop_fdatasync,
1945fe58019SAttilio Rao .vop_getattr = fuse_vnop_getattr,
19504660064SFedor Uporov .vop_getextattr = fuse_vnop_getextattr,
1965fe58019SAttilio Rao .vop_inactive = fuse_vnop_inactive,
19737df9d3bSAlan Somers .vop_ioctl = fuse_vnop_ioctl,
1985fe58019SAttilio Rao .vop_link = fuse_vnop_link,
19904660064SFedor Uporov .vop_listextattr = fuse_vnop_listextattr,
2005fe58019SAttilio Rao .vop_lookup = fuse_vnop_lookup,
2015fe58019SAttilio Rao .vop_mkdir = fuse_vnop_mkdir,
2025fe58019SAttilio Rao .vop_mknod = fuse_vnop_mknod,
2035fe58019SAttilio Rao .vop_open = fuse_vnop_open,
204746c92e0SJohn Baldwin .vop_pathconf = fuse_vnop_pathconf,
205e039bafaSAlan Somers /*
206e039bafaSAlan Somers * TODO: implement vop_poll after upgrading to protocol 7.21.
207e039bafaSAlan Somers * FUSE_POLL was added in protocol 7.11, but it's kind of broken until
208e039bafaSAlan Somers * 7.21, which adds the ability for the client to choose which poll
209e039bafaSAlan Somers * events it wants, and for a client to deregister a file handle
210e039bafaSAlan Somers */
2115fe58019SAttilio Rao .vop_read = fuse_vnop_read,
2125fe58019SAttilio Rao .vop_readdir = fuse_vnop_readdir,
2135fe58019SAttilio Rao .vop_readlink = fuse_vnop_readlink,
2145fe58019SAttilio Rao .vop_reclaim = fuse_vnop_reclaim,
2155fe58019SAttilio Rao .vop_remove = fuse_vnop_remove,
2165fe58019SAttilio Rao .vop_rename = fuse_vnop_rename,
2175fe58019SAttilio Rao .vop_rmdir = fuse_vnop_rmdir,
2185fe58019SAttilio Rao .vop_setattr = fuse_vnop_setattr,
21904660064SFedor Uporov .vop_setextattr = fuse_vnop_setextattr,
2205fe58019SAttilio Rao .vop_strategy = fuse_vnop_strategy,
2215fe58019SAttilio Rao .vop_symlink = fuse_vnop_symlink,
2225fe58019SAttilio Rao .vop_write = fuse_vnop_write,
2235fe58019SAttilio Rao .vop_getpages = fuse_vnop_getpages,
2245fe58019SAttilio Rao .vop_print = fuse_vnop_print,
225e5b50fe7SAlan Somers .vop_vptofh = fuse_vnop_vptofh,
2265fe58019SAttilio Rao };
2276fa079fcSMateusz Guzik VFS_VOP_VECTOR_REGISTER(fuse_vnops);
2285fe58019SAttilio Rao
229666f8543SAlan Somers /* Check permission for extattr operations, much like extattr_check_cred */
230666f8543SAlan Somers static int
fuse_extattr_check_cred(struct vnode * vp,int ns,struct ucred * cred,struct thread * td,accmode_t accmode)231666f8543SAlan Somers fuse_extattr_check_cred(struct vnode *vp, int ns, struct ucred *cred,
232666f8543SAlan Somers struct thread *td, accmode_t accmode)
233666f8543SAlan Somers {
234666f8543SAlan Somers struct mount *mp = vnode_mount(vp);
235666f8543SAlan Somers struct fuse_data *data = fuse_get_mpdata(mp);
236bfcb817bSAlan Somers int default_permissions = data->dataflags & FSESS_DEFAULT_PERMISSIONS;
237666f8543SAlan Somers
238666f8543SAlan Somers /*
239666f8543SAlan Somers * Kernel-invoked always succeeds.
240666f8543SAlan Somers */
241666f8543SAlan Somers if (cred == NOCRED)
242666f8543SAlan Somers return (0);
243666f8543SAlan Somers
244666f8543SAlan Somers /*
245666f8543SAlan Somers * Do not allow privileged processes in jail to directly manipulate
246666f8543SAlan Somers * system attributes.
247666f8543SAlan Somers */
248666f8543SAlan Somers switch (ns) {
249666f8543SAlan Somers case EXTATTR_NAMESPACE_SYSTEM:
250bfcb817bSAlan Somers if (default_permissions) {
251666f8543SAlan Somers return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM));
252666f8543SAlan Somers }
253bfcb817bSAlan Somers return (0);
254666f8543SAlan Somers case EXTATTR_NAMESPACE_USER:
255bfcb817bSAlan Somers if (default_permissions) {
256666f8543SAlan Somers return (fuse_internal_access(vp, accmode, td, cred));
257bfcb817bSAlan Somers }
258bfcb817bSAlan Somers return (0);
259666f8543SAlan Somers default:
260666f8543SAlan Somers return (EPERM);
261666f8543SAlan Somers }
262666f8543SAlan Somers }
263666f8543SAlan Somers
264f8d4af10SAlan Somers /* Get a filehandle for a directory */
265f8d4af10SAlan Somers static int
fuse_filehandle_get_dir(struct vnode * vp,struct fuse_filehandle ** fufhp,struct ucred * cred,pid_t pid)266f8d4af10SAlan Somers fuse_filehandle_get_dir(struct vnode *vp, struct fuse_filehandle **fufhp,
267f8d4af10SAlan Somers struct ucred *cred, pid_t pid)
268f8d4af10SAlan Somers {
2699f10f423SAlan Somers if (fuse_filehandle_get(vp, FREAD, fufhp, cred, pid) == 0)
270f8d4af10SAlan Somers return 0;
2719f10f423SAlan Somers return fuse_filehandle_get(vp, FEXEC, fufhp, cred, pid);
2729f10f423SAlan Somers }
2739f10f423SAlan Somers
2749f10f423SAlan Somers /* Send FUSE_FLUSH for this vnode */
2759f10f423SAlan Somers static int
fuse_flush(struct vnode * vp,struct ucred * cred,pid_t pid,int fflag)2769f10f423SAlan Somers fuse_flush(struct vnode *vp, struct ucred *cred, pid_t pid, int fflag)
2779f10f423SAlan Somers {
2789f10f423SAlan Somers struct fuse_flush_in *ffi;
2799f10f423SAlan Somers struct fuse_filehandle *fufh;
2809f10f423SAlan Somers struct fuse_dispatcher fdi;
2819f10f423SAlan Somers struct thread *td = curthread;
2829f10f423SAlan Somers struct mount *mp = vnode_mount(vp);
2839f10f423SAlan Somers int err;
2849f10f423SAlan Somers
28537df9d3bSAlan Somers if (fsess_not_impl(vnode_mount(vp), FUSE_FLUSH))
2869f10f423SAlan Somers return 0;
2879f10f423SAlan Somers
288f067b609SAlan Somers err = fuse_filehandle_getrw(vp, fflag, &fufh, cred, pid);
2899f10f423SAlan Somers if (err)
2909f10f423SAlan Somers return err;
2919f10f423SAlan Somers
2929f10f423SAlan Somers fdisp_init(&fdi, sizeof(*ffi));
2939f10f423SAlan Somers fdisp_make_vp(&fdi, FUSE_FLUSH, vp, td, cred);
2949f10f423SAlan Somers ffi = fdi.indata;
2959f10f423SAlan Somers ffi->fh = fufh->fh_id;
296f067b609SAlan Somers /*
297f067b609SAlan Somers * If the file has a POSIX lock then we're supposed to set lock_owner.
298f067b609SAlan Somers * If not, then lock_owner is undefined. So we may as well always set
299f067b609SAlan Somers * it.
300f067b609SAlan Somers */
301f067b609SAlan Somers ffi->lock_owner = td->td_proc->p_pid;
3029f10f423SAlan Somers
3039f10f423SAlan Somers err = fdisp_wait_answ(&fdi);
3049f10f423SAlan Somers if (err == ENOSYS) {
3059f10f423SAlan Somers fsess_set_notimpl(mp, FUSE_FLUSH);
3069f10f423SAlan Somers err = 0;
3079f10f423SAlan Somers }
3089f10f423SAlan Somers fdisp_destroy(&fdi);
3099f10f423SAlan Somers return err;
310f8d4af10SAlan Somers }
311f8d4af10SAlan Somers
312f9b0e30bSAlan Somers /* Close wrapper for fifos. */
313f9b0e30bSAlan Somers static int
fuse_fifo_close(struct vop_close_args * ap)314f9b0e30bSAlan Somers fuse_fifo_close(struct vop_close_args *ap)
315f9b0e30bSAlan Somers {
316f9b0e30bSAlan Somers return (fifo_specops.vop_close(ap));
317f9b0e30bSAlan Somers }
318f9b0e30bSAlan Somers
319dc433e15SAlan Somers /* Invalidate a range of cached data, whether dirty of not */
320dc433e15SAlan Somers static int
fuse_inval_buf_range(struct vnode * vp,off_t filesize,off_t start,off_t end)321dc433e15SAlan Somers fuse_inval_buf_range(struct vnode *vp, off_t filesize, off_t start, off_t end)
322dc433e15SAlan Somers {
323dc433e15SAlan Somers struct buf *bp;
324dc433e15SAlan Somers daddr_t left_lbn, end_lbn, right_lbn;
325dc433e15SAlan Somers off_t new_filesize;
326dc433e15SAlan Somers int iosize, left_on, right_on, right_blksize;
327dc433e15SAlan Somers
328dc433e15SAlan Somers iosize = fuse_iosize(vp);
329dc433e15SAlan Somers left_lbn = start / iosize;
330dc433e15SAlan Somers end_lbn = howmany(end, iosize);
331dc433e15SAlan Somers left_on = start & (iosize - 1);
332dc433e15SAlan Somers if (left_on != 0) {
333dc433e15SAlan Somers bp = getblk(vp, left_lbn, iosize, PCATCH, 0, 0);
334dc433e15SAlan Somers if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyend >= left_on) {
335dc433e15SAlan Somers /*
336dc433e15SAlan Somers * Flush the dirty buffer, because we don't have a
337dc433e15SAlan Somers * byte-granular way to record which parts of the
338dc433e15SAlan Somers * buffer are valid.
339dc433e15SAlan Somers */
340dc433e15SAlan Somers bwrite(bp);
341dc433e15SAlan Somers if (bp->b_error)
342dc433e15SAlan Somers return (bp->b_error);
343dc433e15SAlan Somers } else {
344dc433e15SAlan Somers brelse(bp);
345dc433e15SAlan Somers }
346dc433e15SAlan Somers }
347dc433e15SAlan Somers right_on = end & (iosize - 1);
348dc433e15SAlan Somers if (right_on != 0) {
349dc433e15SAlan Somers right_lbn = end / iosize;
350dc433e15SAlan Somers new_filesize = MAX(filesize, end);
351dc433e15SAlan Somers right_blksize = MIN(iosize, new_filesize - iosize * right_lbn);
352dc433e15SAlan Somers bp = getblk(vp, right_lbn, right_blksize, PCATCH, 0, 0);
353dc433e15SAlan Somers if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyoff < right_on) {
354dc433e15SAlan Somers /*
355dc433e15SAlan Somers * Flush the dirty buffer, because we don't have a
356dc433e15SAlan Somers * byte-granular way to record which parts of the
357dc433e15SAlan Somers * buffer are valid.
358dc433e15SAlan Somers */
359dc433e15SAlan Somers bwrite(bp);
360dc433e15SAlan Somers if (bp->b_error)
361dc433e15SAlan Somers return (bp->b_error);
362dc433e15SAlan Somers } else {
363dc433e15SAlan Somers brelse(bp);
364dc433e15SAlan Somers }
365dc433e15SAlan Somers }
366dc433e15SAlan Somers
367dc433e15SAlan Somers v_inval_buf_range(vp, left_lbn, end_lbn, iosize);
368dc433e15SAlan Somers return (0);
369dc433e15SAlan Somers }
370dc433e15SAlan Somers
371dc433e15SAlan Somers
37237df9d3bSAlan Somers /* Send FUSE_LSEEK for this node */
37337df9d3bSAlan Somers static int
fuse_vnop_do_lseek(struct vnode * vp,struct thread * td,struct ucred * cred,pid_t pid,off_t * offp,int whence)37437df9d3bSAlan Somers fuse_vnop_do_lseek(struct vnode *vp, struct thread *td, struct ucred *cred,
37537df9d3bSAlan Somers pid_t pid, off_t *offp, int whence)
37637df9d3bSAlan Somers {
37737df9d3bSAlan Somers struct fuse_dispatcher fdi;
37837df9d3bSAlan Somers struct fuse_filehandle *fufh;
37937df9d3bSAlan Somers struct fuse_lseek_in *flsi;
38037df9d3bSAlan Somers struct fuse_lseek_out *flso;
38137df9d3bSAlan Somers struct mount *mp = vnode_mount(vp);
38237df9d3bSAlan Somers int err;
38337df9d3bSAlan Somers
38434477e25SAlan Somers ASSERT_VOP_LOCKED(vp, __func__);
38537df9d3bSAlan Somers
38637df9d3bSAlan Somers err = fuse_filehandle_getrw(vp, FREAD, &fufh, cred, pid);
38737df9d3bSAlan Somers if (err)
38837df9d3bSAlan Somers return (err);
38937df9d3bSAlan Somers fdisp_init(&fdi, sizeof(*flsi));
39037df9d3bSAlan Somers fdisp_make_vp(&fdi, FUSE_LSEEK, vp, td, cred);
39137df9d3bSAlan Somers flsi = fdi.indata;
39237df9d3bSAlan Somers flsi->fh = fufh->fh_id;
39337df9d3bSAlan Somers flsi->offset = *offp;
39437df9d3bSAlan Somers flsi->whence = whence;
39537df9d3bSAlan Somers err = fdisp_wait_answ(&fdi);
39637df9d3bSAlan Somers if (err == ENOSYS) {
39737df9d3bSAlan Somers fsess_set_notimpl(mp, FUSE_LSEEK);
3986efba04dSAlan Somers } else if (err == ENXIO) {
3996efba04dSAlan Somers /* Note: ENXIO means "no more hole/data regions until EOF" */
4006efba04dSAlan Somers fsess_set_impl(mp, FUSE_LSEEK);
40137df9d3bSAlan Somers } else if (err == 0) {
40237df9d3bSAlan Somers fsess_set_impl(mp, FUSE_LSEEK);
40337df9d3bSAlan Somers flso = fdi.answ;
40437df9d3bSAlan Somers *offp = flso->offset;
40537df9d3bSAlan Somers }
40637df9d3bSAlan Somers fdisp_destroy(&fdi);
40737df9d3bSAlan Somers
40837df9d3bSAlan Somers return (err);
40937df9d3bSAlan Somers }
41037df9d3bSAlan Somers
4115fe58019SAttilio Rao /*
4125fe58019SAttilio Rao struct vnop_access_args {
4135fe58019SAttilio Rao struct vnode *a_vp;
4145fe58019SAttilio Rao #if VOP_ACCESS_TAKES_ACCMODE_T
4155fe58019SAttilio Rao accmode_t a_accmode;
4165fe58019SAttilio Rao #else
4175fe58019SAttilio Rao int a_mode;
4185fe58019SAttilio Rao #endif
4195fe58019SAttilio Rao struct ucred *a_cred;
4205fe58019SAttilio Rao struct thread *a_td;
4215fe58019SAttilio Rao };
4225fe58019SAttilio Rao */
4235fe58019SAttilio Rao static int
fuse_vnop_access(struct vop_access_args * ap)4245fe58019SAttilio Rao fuse_vnop_access(struct vop_access_args *ap)
4255fe58019SAttilio Rao {
4265fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
4275fe58019SAttilio Rao int accmode = ap->a_accmode;
4285fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
4295fe58019SAttilio Rao
4305fe58019SAttilio Rao struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
4315fe58019SAttilio Rao
4325fe58019SAttilio Rao int err;
4335fe58019SAttilio Rao
4345fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
4355fe58019SAttilio Rao if (vnode_isvroot(vp)) {
4365fe58019SAttilio Rao return 0;
4375fe58019SAttilio Rao }
4385fe58019SAttilio Rao return ENXIO;
4395fe58019SAttilio Rao }
4405fe58019SAttilio Rao if (!(data->dataflags & FSESS_INITED)) {
4415fe58019SAttilio Rao if (vnode_isvroot(vp)) {
442cc426dd3SMateusz Guzik if (priv_check_cred(cred, PRIV_VFS_ADMIN) ||
4435fe58019SAttilio Rao (fuse_match_cred(data->daemoncred, cred) == 0)) {
4445fe58019SAttilio Rao return 0;
4455fe58019SAttilio Rao }
4465fe58019SAttilio Rao }
4475fe58019SAttilio Rao return EBADF;
4485fe58019SAttilio Rao }
4495fe58019SAttilio Rao if (vnode_islnk(vp)) {
4505fe58019SAttilio Rao return 0;
4515fe58019SAttilio Rao }
4525fe58019SAttilio Rao
453666f8543SAlan Somers err = fuse_internal_access(vp, accmode, ap->a_td, ap->a_cred);
4545fe58019SAttilio Rao return err;
4555fe58019SAttilio Rao }
4565fe58019SAttilio Rao
4575fe58019SAttilio Rao /*
458f067b609SAlan Somers * struct vop_advlock_args {
459f067b609SAlan Somers * struct vop_generic_args a_gen;
460f067b609SAlan Somers * struct vnode *a_vp;
461f067b609SAlan Somers * void *a_id;
462f067b609SAlan Somers * int a_op;
463f067b609SAlan Somers * struct flock *a_fl;
464f067b609SAlan Somers * int a_flags;
465f067b609SAlan Somers * }
466f067b609SAlan Somers */
467f067b609SAlan Somers static int
fuse_vnop_advlock(struct vop_advlock_args * ap)468f067b609SAlan Somers fuse_vnop_advlock(struct vop_advlock_args *ap)
469f067b609SAlan Somers {
470f067b609SAlan Somers struct vnode *vp = ap->a_vp;
471f067b609SAlan Somers struct flock *fl = ap->a_fl;
472f067b609SAlan Somers struct thread *td = curthread;
473f067b609SAlan Somers struct ucred *cred = td->td_ucred;
474f067b609SAlan Somers pid_t pid = td->td_proc->p_pid;
475f067b609SAlan Somers struct fuse_filehandle *fufh;
476f067b609SAlan Somers struct fuse_dispatcher fdi;
477f067b609SAlan Somers struct fuse_lk_in *fli;
478f067b609SAlan Somers struct fuse_lk_out *flo;
479f6e53195SAlan Somers struct vattr vattr;
480f067b609SAlan Somers enum fuse_opcode op;
481f6e53195SAlan Somers off_t size, start;
482f067b609SAlan Somers int dataflags, err;
4838aa24ed3SAlan Somers int flags = ap->a_flags;
484f067b609SAlan Somers
485f067b609SAlan Somers dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags;
486f067b609SAlan Somers
487f067b609SAlan Somers if (fuse_isdeadfs(vp)) {
488f067b609SAlan Somers return ENXIO;
489f067b609SAlan Somers }
490f067b609SAlan Somers
491f067b609SAlan Somers switch(ap->a_op) {
492f067b609SAlan Somers case F_GETLK:
493f067b609SAlan Somers op = FUSE_GETLK;
494f067b609SAlan Somers break;
495f067b609SAlan Somers case F_SETLK:
496929acdb1SAlan Somers if (flags & F_WAIT)
497929acdb1SAlan Somers op = FUSE_SETLKW;
498929acdb1SAlan Somers else
499f067b609SAlan Somers op = FUSE_SETLK;
500f067b609SAlan Somers break;
501929acdb1SAlan Somers case F_UNLCK:
502929acdb1SAlan Somers op = FUSE_SETLK;
503f067b609SAlan Somers break;
504f067b609SAlan Somers default:
505f067b609SAlan Somers return EINVAL;
506f067b609SAlan Somers }
507f067b609SAlan Somers
508542711e5SAlan Somers if (!(dataflags & FSESS_POSIX_LOCKS))
509542711e5SAlan Somers return vop_stdadvlock(ap);
510542711e5SAlan Somers /* FUSE doesn't properly support flock until protocol 7.17 */
511542711e5SAlan Somers if (flags & F_FLOCK)
512542711e5SAlan Somers return vop_stdadvlock(ap);
513542711e5SAlan Somers
514542711e5SAlan Somers vn_lock(vp, LK_SHARED | LK_RETRY);
515542711e5SAlan Somers
516f6e53195SAlan Somers switch (fl->l_whence) {
517f6e53195SAlan Somers case SEEK_SET:
518f6e53195SAlan Somers case SEEK_CUR:
519f6e53195SAlan Somers /*
520f6e53195SAlan Somers * Caller is responsible for adding any necessary offset
521f6e53195SAlan Somers * when SEEK_CUR is used.
522f6e53195SAlan Somers */
523f6e53195SAlan Somers start = fl->l_start;
524f6e53195SAlan Somers break;
525f6e53195SAlan Somers
526f6e53195SAlan Somers case SEEK_END:
527f6e53195SAlan Somers err = fuse_internal_getattr(vp, &vattr, cred, td);
528f6e53195SAlan Somers if (err)
529f6e53195SAlan Somers goto out;
530f6e53195SAlan Somers size = vattr.va_size;
531f6e53195SAlan Somers if (size > OFF_MAX ||
532f6e53195SAlan Somers (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) {
533f6e53195SAlan Somers err = EOVERFLOW;
534f6e53195SAlan Somers goto out;
535f6e53195SAlan Somers }
536f6e53195SAlan Somers start = size + fl->l_start;
537f6e53195SAlan Somers break;
538f6e53195SAlan Somers
539f6e53195SAlan Somers default:
540f6e53195SAlan Somers return (EINVAL);
541f6e53195SAlan Somers }
542f6e53195SAlan Somers
543542711e5SAlan Somers err = fuse_filehandle_get_anyflags(vp, &fufh, cred, pid);
544542711e5SAlan Somers if (err)
545542711e5SAlan Somers goto out;
546542711e5SAlan Somers
547542711e5SAlan Somers fdisp_init(&fdi, sizeof(*fli));
548542711e5SAlan Somers
549f067b609SAlan Somers fdisp_make_vp(&fdi, op, vp, td, cred);
550f067b609SAlan Somers fli = fdi.indata;
551f067b609SAlan Somers fli->fh = fufh->fh_id;
55218b19f8cSAlan Somers fli->owner = td->td_proc->p_pid;
553f6e53195SAlan Somers fli->lk.start = start;
554f067b609SAlan Somers if (fl->l_len != 0)
555f6e53195SAlan Somers fli->lk.end = start + fl->l_len - 1;
556f067b609SAlan Somers else
557f067b609SAlan Somers fli->lk.end = INT64_MAX;
558f067b609SAlan Somers fli->lk.type = fl->l_type;
55918b19f8cSAlan Somers fli->lk.pid = td->td_proc->p_pid;
560f067b609SAlan Somers
561f067b609SAlan Somers err = fdisp_wait_answ(&fdi);
562f067b609SAlan Somers fdisp_destroy(&fdi);
563f067b609SAlan Somers
564f067b609SAlan Somers if (err == 0 && op == FUSE_GETLK) {
565f067b609SAlan Somers flo = fdi.answ;
566f067b609SAlan Somers fl->l_type = flo->lk.type;
5673c3b906bSAlan Somers fl->l_whence = SEEK_SET;
568f067b609SAlan Somers if (flo->lk.type != F_UNLCK) {
56946fcf947SAlan Somers fl->l_pid = flo->lk.pid;
570f067b609SAlan Somers fl->l_start = flo->lk.start;
571f067b609SAlan Somers if (flo->lk.end == INT64_MAX)
572f067b609SAlan Somers fl->l_len = 0;
573f067b609SAlan Somers else
574f067b609SAlan Somers fl->l_len = flo->lk.end - flo->lk.start + 1;
575f067b609SAlan Somers fl->l_start = flo->lk.start;
576f067b609SAlan Somers }
577f067b609SAlan Somers }
578f067b609SAlan Somers
579542711e5SAlan Somers out:
580542711e5SAlan Somers VOP_UNLOCK(vp);
581f067b609SAlan Somers return err;
582f067b609SAlan Somers }
583f067b609SAlan Somers
584398c88c7SAlan Somers static int
fuse_vnop_allocate(struct vop_allocate_args * ap)585398c88c7SAlan Somers fuse_vnop_allocate(struct vop_allocate_args *ap)
586398c88c7SAlan Somers {
587398c88c7SAlan Somers struct vnode *vp = ap->a_vp;
588398c88c7SAlan Somers off_t *len = ap->a_len;
589398c88c7SAlan Somers off_t *offset = ap->a_offset;
590398c88c7SAlan Somers struct ucred *cred = ap->a_cred;
591398c88c7SAlan Somers struct fuse_filehandle *fufh;
592398c88c7SAlan Somers struct mount *mp = vnode_mount(vp);
593398c88c7SAlan Somers struct fuse_dispatcher fdi;
594398c88c7SAlan Somers struct fuse_fallocate_in *ffi;
595398c88c7SAlan Somers struct uio io;
596398c88c7SAlan Somers pid_t pid = curthread->td_proc->p_pid;
597398c88c7SAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
598398c88c7SAlan Somers off_t filesize;
599398c88c7SAlan Somers int err;
600398c88c7SAlan Somers
601398c88c7SAlan Somers if (fuse_isdeadfs(vp))
602398c88c7SAlan Somers return (ENXIO);
603398c88c7SAlan Somers
604398c88c7SAlan Somers switch (vp->v_type) {
605398c88c7SAlan Somers case VFIFO:
606398c88c7SAlan Somers return (ESPIPE);
607398c88c7SAlan Somers case VLNK:
608398c88c7SAlan Somers case VREG:
609398c88c7SAlan Somers if (vfs_isrdonly(mp))
610398c88c7SAlan Somers return (EROFS);
611398c88c7SAlan Somers break;
612398c88c7SAlan Somers default:
613398c88c7SAlan Somers return (ENODEV);
614398c88c7SAlan Somers }
615398c88c7SAlan Somers
616398c88c7SAlan Somers if (vfs_isrdonly(mp))
617398c88c7SAlan Somers return (EROFS);
618398c88c7SAlan Somers
619398c88c7SAlan Somers if (fsess_not_impl(mp, FUSE_FALLOCATE))
620398c88c7SAlan Somers return (EINVAL);
621398c88c7SAlan Somers
622398c88c7SAlan Somers io.uio_offset = *offset;
623398c88c7SAlan Somers io.uio_resid = *len;
624398c88c7SAlan Somers err = vn_rlimit_fsize(vp, &io, curthread);
625398c88c7SAlan Somers if (err)
626398c88c7SAlan Somers return (err);
627398c88c7SAlan Somers
628398c88c7SAlan Somers err = fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
629398c88c7SAlan Somers if (err)
630398c88c7SAlan Somers return (err);
631398c88c7SAlan Somers
632398c88c7SAlan Somers fuse_vnode_update(vp, FN_MTIMECHANGE | FN_CTIMECHANGE);
633398c88c7SAlan Somers
634398c88c7SAlan Somers err = fuse_vnode_size(vp, &filesize, cred, curthread);
635398c88c7SAlan Somers if (err)
636398c88c7SAlan Somers return (err);
637398c88c7SAlan Somers fuse_inval_buf_range(vp, filesize, *offset, *offset + *len);
638398c88c7SAlan Somers
639398c88c7SAlan Somers fdisp_init(&fdi, sizeof(*ffi));
640398c88c7SAlan Somers fdisp_make_vp(&fdi, FUSE_FALLOCATE, vp, curthread, cred);
641398c88c7SAlan Somers ffi = fdi.indata;
642398c88c7SAlan Somers ffi->fh = fufh->fh_id;
643398c88c7SAlan Somers ffi->offset = *offset;
644398c88c7SAlan Somers ffi->length = *len;
645398c88c7SAlan Somers ffi->mode = 0;
646398c88c7SAlan Somers err = fdisp_wait_answ(&fdi);
647398c88c7SAlan Somers
648398c88c7SAlan Somers if (err == ENOSYS) {
649398c88c7SAlan Somers fsess_set_notimpl(mp, FUSE_FALLOCATE);
650398c88c7SAlan Somers err = EINVAL;
651398c88c7SAlan Somers } else if (err == EOPNOTSUPP) {
652398c88c7SAlan Somers /*
653398c88c7SAlan Somers * The file system server does not support FUSE_FALLOCATE with
65489d57b94SAlan Somers * the supplied mode for this particular file.
655398c88c7SAlan Somers */
656398c88c7SAlan Somers err = EINVAL;
657398c88c7SAlan Somers } else if (!err) {
658398c88c7SAlan Somers *offset += *len;
659398c88c7SAlan Somers *len = 0;
660398c88c7SAlan Somers fuse_vnode_undirty_cached_timestamps(vp, false);
661398c88c7SAlan Somers fuse_internal_clear_suid_on_write(vp, cred, curthread);
662398c88c7SAlan Somers if (*offset > fvdat->cached_attrs.va_size) {
663398c88c7SAlan Somers fuse_vnode_setsize(vp, *offset, false);
664398c88c7SAlan Somers getnanouptime(&fvdat->last_local_modify);
665398c88c7SAlan Somers }
666398c88c7SAlan Somers }
667398c88c7SAlan Somers
6681bdf879bSAlan Somers fdisp_destroy(&fdi);
669398c88c7SAlan Somers return (err);
670398c88c7SAlan Somers }
671398c88c7SAlan Somers
672a1c9f4adSAlan Somers /* {
673a1c9f4adSAlan Somers struct vnode *a_vp;
674a1c9f4adSAlan Somers daddr_t a_bn;
675a1c9f4adSAlan Somers struct bufobj **a_bop;
676a1c9f4adSAlan Somers daddr_t *a_bnp;
677a1c9f4adSAlan Somers int *a_runp;
678a1c9f4adSAlan Somers int *a_runb;
679a1c9f4adSAlan Somers } */
680a1c9f4adSAlan Somers static int
fuse_vnop_bmap(struct vop_bmap_args * ap)681a1c9f4adSAlan Somers fuse_vnop_bmap(struct vop_bmap_args *ap)
682a1c9f4adSAlan Somers {
683a1c9f4adSAlan Somers struct vnode *vp = ap->a_vp;
684a1c9f4adSAlan Somers struct bufobj **bo = ap->a_bop;
685a1c9f4adSAlan Somers struct thread *td = curthread;
686a1c9f4adSAlan Somers struct mount *mp;
687a1c9f4adSAlan Somers struct fuse_dispatcher fdi;
688a1c9f4adSAlan Somers struct fuse_bmap_in *fbi;
689a1c9f4adSAlan Somers struct fuse_bmap_out *fbo;
690a1c9f4adSAlan Somers struct fuse_data *data;
6917430017bSAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
692a1c9f4adSAlan Somers uint64_t biosize;
6937430017bSAlan Somers off_t fsize;
694a1c9f4adSAlan Somers daddr_t lbn = ap->a_bn;
695a1c9f4adSAlan Somers daddr_t *pbn = ap->a_bnp;
696a1c9f4adSAlan Somers int *runp = ap->a_runp;
697a1c9f4adSAlan Somers int *runb = ap->a_runb;
698a1c9f4adSAlan Somers int error = 0;
699a1c9f4adSAlan Somers int maxrun;
700a1c9f4adSAlan Somers
701a1c9f4adSAlan Somers if (fuse_isdeadfs(vp)) {
702a1c9f4adSAlan Somers return ENXIO;
703a1c9f4adSAlan Somers }
704a1c9f4adSAlan Somers
705a1c9f4adSAlan Somers mp = vnode_mount(vp);
706a1c9f4adSAlan Somers data = fuse_get_mpdata(mp);
707a1c9f4adSAlan Somers biosize = fuse_iosize(vp);
708a1c9f4adSAlan Somers maxrun = MIN(vp->v_mount->mnt_iosize_max / biosize - 1,
709a1c9f4adSAlan Somers data->max_readahead_blocks);
710a1c9f4adSAlan Somers
711a1c9f4adSAlan Somers if (bo != NULL)
712a1c9f4adSAlan Somers *bo = &vp->v_bufobj;
713a1c9f4adSAlan Somers
714a1c9f4adSAlan Somers /*
715a1c9f4adSAlan Somers * The FUSE_BMAP operation does not include the runp and runb
716a1c9f4adSAlan Somers * variables, so we must guess. Report nonzero contiguous runs so
717a1c9f4adSAlan Somers * cluster_read will combine adjacent reads. It's worthwhile to reduce
718a1c9f4adSAlan Somers * upcalls even if we don't know the true physical layout of the file.
719a1c9f4adSAlan Somers *
720a1c9f4adSAlan Somers * FUSE file systems may opt out of read clustering in two ways:
721a1c9f4adSAlan Somers * * mounting with -onoclusterr
722a1c9f4adSAlan Somers * * Setting max_readahead <= maxbcachebuf during FUSE_INIT
723a1c9f4adSAlan Somers */
724a1c9f4adSAlan Somers if (runb != NULL)
725a1c9f4adSAlan Somers *runb = MIN(lbn, maxrun);
7267430017bSAlan Somers if (runp != NULL && maxrun == 0)
7277430017bSAlan Somers *runp = 0;
7287430017bSAlan Somers else if (runp != NULL) {
7297430017bSAlan Somers /*
7307430017bSAlan Somers * If the file's size is cached, use that value to calculate
7317430017bSAlan Somers * runp, even if the cache is expired. runp is only advisory,
7327430017bSAlan Somers * and the risk of getting it wrong is not worth the cost of
7337430017bSAlan Somers * another upcall.
7347430017bSAlan Somers */
7357430017bSAlan Somers if (fvdat->cached_attrs.va_size != VNOVAL)
7367430017bSAlan Somers fsize = fvdat->cached_attrs.va_size;
7377430017bSAlan Somers else
7387430017bSAlan Somers error = fuse_vnode_size(vp, &fsize, td->td_ucred, td);
739a1c9f4adSAlan Somers if (error == 0)
7407430017bSAlan Somers *runp = MIN(MAX(0, fsize / (off_t)biosize - lbn - 1),
741a1c9f4adSAlan Somers maxrun);
742a1c9f4adSAlan Somers else
743a1c9f4adSAlan Somers *runp = 0;
744a1c9f4adSAlan Somers }
745a1c9f4adSAlan Somers
74637df9d3bSAlan Somers if (fsess_maybe_impl(mp, FUSE_BMAP)) {
747a1c9f4adSAlan Somers fdisp_init(&fdi, sizeof(*fbi));
748a1c9f4adSAlan Somers fdisp_make_vp(&fdi, FUSE_BMAP, vp, td, td->td_ucred);
749a1c9f4adSAlan Somers fbi = fdi.indata;
750a1c9f4adSAlan Somers fbi->block = lbn;
751a1c9f4adSAlan Somers fbi->blocksize = biosize;
752a1c9f4adSAlan Somers error = fdisp_wait_answ(&fdi);
753a1c9f4adSAlan Somers if (error == ENOSYS) {
754caeea8b4SAlan Somers fdisp_destroy(&fdi);
755a1c9f4adSAlan Somers fsess_set_notimpl(mp, FUSE_BMAP);
756a1c9f4adSAlan Somers error = 0;
757a1c9f4adSAlan Somers } else {
758a1c9f4adSAlan Somers fbo = fdi.answ;
759a1c9f4adSAlan Somers if (error == 0 && pbn != NULL)
760a1c9f4adSAlan Somers *pbn = fbo->block;
761caeea8b4SAlan Somers fdisp_destroy(&fdi);
762a1c9f4adSAlan Somers return error;
763a1c9f4adSAlan Somers }
764a1c9f4adSAlan Somers }
765a1c9f4adSAlan Somers
766a1c9f4adSAlan Somers /* If the daemon doesn't support BMAP, make up a sensible default */
767a1c9f4adSAlan Somers if (pbn != NULL)
768a1c9f4adSAlan Somers *pbn = lbn * btodb(biosize);
769a1c9f4adSAlan Somers return (error);
770a1c9f4adSAlan Somers }
771a1c9f4adSAlan Somers
772f067b609SAlan Somers /*
7739f10f423SAlan Somers struct vop_close_args {
7745fe58019SAttilio Rao struct vnode *a_vp;
7755fe58019SAttilio Rao int a_fflag;
7765fe58019SAttilio Rao struct ucred *a_cred;
7775fe58019SAttilio Rao struct thread *a_td;
7785fe58019SAttilio Rao };
7795fe58019SAttilio Rao */
7805fe58019SAttilio Rao static int
fuse_vnop_close(struct vop_close_args * ap)7815fe58019SAttilio Rao fuse_vnop_close(struct vop_close_args *ap)
7825fe58019SAttilio Rao {
7835fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
784fb619c94SAlan Somers struct mount *mp = vnode_mount(vp);
7855fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
7865fe58019SAttilio Rao int fflag = ap->a_fflag;
787f8d4af10SAlan Somers struct thread *td = ap->a_td;
788f8d4af10SAlan Somers pid_t pid = td->td_proc->p_pid;
78991972cfcSAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
7909f10f423SAlan Somers int err = 0;
7915fe58019SAttilio Rao
79235cf0e7eSAlan Somers if (fuse_isdeadfs(vp))
7935fe58019SAttilio Rao return 0;
79435cf0e7eSAlan Somers if (vnode_isdir(vp))
79535cf0e7eSAlan Somers return 0;
79635cf0e7eSAlan Somers if (fflag & IO_NDELAY)
79735cf0e7eSAlan Somers return 0;
7985ec10aa5SAlan Somers
7999f10f423SAlan Somers err = fuse_flush(vp, cred, pid, fflag);
800fb619c94SAlan Somers if (err == 0 && (fvdat->flag & FN_ATIMECHANGE) && !vfs_isrdonly(mp)) {
80191972cfcSAlan Somers struct vattr vap;
802fb619c94SAlan Somers struct fuse_data *data;
803fb619c94SAlan Somers int dataflags;
804fb619c94SAlan Somers int access_e = 0;
80591972cfcSAlan Somers
806fb619c94SAlan Somers data = fuse_get_mpdata(mp);
807fb619c94SAlan Somers dataflags = data->dataflags;
808fb619c94SAlan Somers if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
809fb619c94SAlan Somers struct vattr va;
810fb619c94SAlan Somers
811fb619c94SAlan Somers fuse_internal_getattr(vp, &va, cred, td);
812fb619c94SAlan Somers access_e = vaccess(vp->v_type, va.va_mode, va.va_uid,
813fb619c94SAlan Somers va.va_gid, VWRITE, cred);
814fb619c94SAlan Somers }
815fb619c94SAlan Somers if (access_e == 0) {
81691972cfcSAlan Somers VATTR_NULL(&vap);
81791972cfcSAlan Somers vap.va_atime = fvdat->cached_attrs.va_atime;
818fb619c94SAlan Somers /*
819fb619c94SAlan Somers * Ignore errors setting when setting atime. That
820fb619c94SAlan Somers * should not cause close(2) to fail.
821fb619c94SAlan Somers */
822fb619c94SAlan Somers fuse_internal_setattr(vp, &vap, td, NULL);
823fb619c94SAlan Somers }
82491972cfcSAlan Somers }
8255ec10aa5SAlan Somers /* TODO: close the file handle, if we're sure it's no longer used */
82691972cfcSAlan Somers if ((fvdat->flag & FN_SIZECHANGE) != 0) {
827f8d4af10SAlan Somers fuse_vnode_savesize(vp, cred, td->td_proc->p_pid);
8285fe58019SAttilio Rao }
8299f10f423SAlan Somers return err;
8305fe58019SAttilio Rao }
8315fe58019SAttilio Rao
83292bbfe1fSAlan Somers /*
83392bbfe1fSAlan Somers struct vop_copy_file_range_args {
83492bbfe1fSAlan Somers struct vop_generic_args a_gen;
83592bbfe1fSAlan Somers struct vnode *a_invp;
83692bbfe1fSAlan Somers off_t *a_inoffp;
83792bbfe1fSAlan Somers struct vnode *a_outvp;
83892bbfe1fSAlan Somers off_t *a_outoffp;
83992bbfe1fSAlan Somers size_t *a_lenp;
84092bbfe1fSAlan Somers unsigned int a_flags;
84192bbfe1fSAlan Somers struct ucred *a_incred;
84292bbfe1fSAlan Somers struct ucred *a_outcred;
84392bbfe1fSAlan Somers struct thread *a_fsizetd;
84492bbfe1fSAlan Somers }
84592bbfe1fSAlan Somers */
84692bbfe1fSAlan Somers static int
fuse_vnop_copy_file_range(struct vop_copy_file_range_args * ap)84792bbfe1fSAlan Somers fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap)
84892bbfe1fSAlan Somers {
84992bbfe1fSAlan Somers struct vnode *invp = ap->a_invp;
85092bbfe1fSAlan Somers struct vnode *outvp = ap->a_outvp;
85192bbfe1fSAlan Somers struct mount *mp = vnode_mount(invp);
85265d70b3bSAlan Somers struct fuse_vnode_data *outfvdat = VTOFUD(outvp);
85392bbfe1fSAlan Somers struct fuse_dispatcher fdi;
85492bbfe1fSAlan Somers struct fuse_filehandle *infufh, *outfufh;
85592bbfe1fSAlan Somers struct fuse_copy_file_range_in *fcfri;
85692bbfe1fSAlan Somers struct ucred *incred = ap->a_incred;
85792bbfe1fSAlan Somers struct ucred *outcred = ap->a_outcred;
85892bbfe1fSAlan Somers struct fuse_write_out *fwo;
85992bbfe1fSAlan Somers struct thread *td;
86092bbfe1fSAlan Somers struct uio io;
86141ae9f9eSAlan Somers off_t outfilesize;
86252360ca3SAlan Somers ssize_t r = 0;
86392bbfe1fSAlan Somers pid_t pid;
86492bbfe1fSAlan Somers int err;
86592bbfe1fSAlan Somers
866c5405d1cSKonstantin Belousov err = ENOSYS;
8674c6cded2SKonstantin Belousov if (mp == NULL || mp != vnode_mount(outvp))
86892bbfe1fSAlan Somers goto fallback;
86992bbfe1fSAlan Somers
87092bbfe1fSAlan Somers if (incred->cr_uid != outcred->cr_uid)
87192bbfe1fSAlan Somers goto fallback;
87292bbfe1fSAlan Somers
87392bbfe1fSAlan Somers if (incred->cr_groups[0] != outcred->cr_groups[0])
87492bbfe1fSAlan Somers goto fallback;
87592bbfe1fSAlan Somers
8764c6cded2SKonstantin Belousov /* Caller busied mp, mnt_data can be safely accessed. */
87792bbfe1fSAlan Somers if (fsess_not_impl(mp, FUSE_COPY_FILE_RANGE))
87892bbfe1fSAlan Somers goto fallback;
87992bbfe1fSAlan Somers
88092bbfe1fSAlan Somers if (ap->a_fsizetd == NULL)
88192bbfe1fSAlan Somers td = curthread;
88292bbfe1fSAlan Somers else
88392bbfe1fSAlan Somers td = ap->a_fsizetd;
88492bbfe1fSAlan Somers pid = td->td_proc->p_pid;
88592bbfe1fSAlan Somers
886318c5671SKonstantin Belousov vn_lock_pair(invp, false, LK_SHARED, outvp, false, LK_EXCLUSIVE);
887318c5671SKonstantin Belousov if (invp->v_data == NULL || outvp->v_data == NULL) {
888318c5671SKonstantin Belousov err = EBADF;
889318c5671SKonstantin Belousov goto unlock;
89092bbfe1fSAlan Somers }
89192bbfe1fSAlan Somers
89217a82e6aSAlan Somers err = fuse_filehandle_getrw(invp, FREAD, &infufh, incred, pid);
89317a82e6aSAlan Somers if (err)
89417a82e6aSAlan Somers goto unlock;
89517a82e6aSAlan Somers
89617a82e6aSAlan Somers err = fuse_filehandle_getrw(outvp, FWRITE, &outfufh, outcred, pid);
89717a82e6aSAlan Somers if (err)
89817a82e6aSAlan Somers goto unlock;
89917a82e6aSAlan Somers
90052360ca3SAlan Somers io.uio_resid = *ap->a_lenp;
90192bbfe1fSAlan Somers if (ap->a_fsizetd) {
90292bbfe1fSAlan Somers io.uio_offset = *ap->a_outoffp;
90352360ca3SAlan Somers err = vn_rlimit_fsizex(outvp, &io, 0, &r, ap->a_fsizetd);
90452360ca3SAlan Somers if (err != 0)
90592bbfe1fSAlan Somers goto unlock;
90692bbfe1fSAlan Somers }
90792bbfe1fSAlan Somers
90841ae9f9eSAlan Somers err = fuse_vnode_size(outvp, &outfilesize, outcred, curthread);
90941ae9f9eSAlan Somers if (err)
91041ae9f9eSAlan Somers goto unlock;
91141ae9f9eSAlan Somers
9121c909c30SAlan Somers vnode_pager_clean_sync(invp);
91341ae9f9eSAlan Somers err = fuse_inval_buf_range(outvp, outfilesize, *ap->a_outoffp,
91452360ca3SAlan Somers *ap->a_outoffp + io.uio_resid);
91541ae9f9eSAlan Somers if (err)
91641ae9f9eSAlan Somers goto unlock;
91741ae9f9eSAlan Somers
91892bbfe1fSAlan Somers fdisp_init(&fdi, sizeof(*fcfri));
91992bbfe1fSAlan Somers fdisp_make_vp(&fdi, FUSE_COPY_FILE_RANGE, invp, td, incred);
92092bbfe1fSAlan Somers fcfri = fdi.indata;
92192bbfe1fSAlan Somers fcfri->fh_in = infufh->fh_id;
92292bbfe1fSAlan Somers fcfri->off_in = *ap->a_inoffp;
92392bbfe1fSAlan Somers fcfri->nodeid_out = VTOI(outvp);
92492bbfe1fSAlan Somers fcfri->fh_out = outfufh->fh_id;
92592bbfe1fSAlan Somers fcfri->off_out = *ap->a_outoffp;
92652360ca3SAlan Somers fcfri->len = io.uio_resid;
92792bbfe1fSAlan Somers fcfri->flags = 0;
92892bbfe1fSAlan Somers
92992bbfe1fSAlan Somers err = fdisp_wait_answ(&fdi);
93092bbfe1fSAlan Somers if (err == 0) {
93192bbfe1fSAlan Somers fwo = fdi.answ;
93292bbfe1fSAlan Somers *ap->a_lenp = fwo->size;
93392bbfe1fSAlan Somers *ap->a_inoffp += fwo->size;
93492bbfe1fSAlan Somers *ap->a_outoffp += fwo->size;
93592bbfe1fSAlan Somers fuse_internal_clear_suid_on_write(outvp, outcred, td);
93613d593a5SAlan Somers if (*ap->a_outoffp > outfvdat->cached_attrs.va_size) {
93765d70b3bSAlan Somers fuse_vnode_setsize(outvp, *ap->a_outoffp, false);
93813d593a5SAlan Somers getnanouptime(&outfvdat->last_local_modify);
93913d593a5SAlan Somers }
9405169832cSAlan Somers fuse_vnode_update(invp, FN_ATIMECHANGE);
9415169832cSAlan Somers fuse_vnode_update(outvp, FN_MTIMECHANGE | FN_CTIMECHANGE);
94292bbfe1fSAlan Somers }
94392bbfe1fSAlan Somers fdisp_destroy(&fdi);
94492bbfe1fSAlan Somers
94592bbfe1fSAlan Somers unlock:
94692bbfe1fSAlan Somers if (invp != outvp)
94792bbfe1fSAlan Somers VOP_UNLOCK(invp);
94892bbfe1fSAlan Somers VOP_UNLOCK(outvp);
94992bbfe1fSAlan Somers
950c5405d1cSKonstantin Belousov if (err == ENOSYS)
95192bbfe1fSAlan Somers fsess_set_notimpl(mp, FUSE_COPY_FILE_RANGE);
95292bbfe1fSAlan Somers fallback:
95392bbfe1fSAlan Somers
95452360ca3SAlan Somers /*
95552360ca3SAlan Somers * No need to call vn_rlimit_fsizex_res before return, since the uio is
95652360ca3SAlan Somers * local.
95752360ca3SAlan Somers */
95892bbfe1fSAlan Somers return (err);
95992bbfe1fSAlan Somers }
96092bbfe1fSAlan Somers
96119ef317dSAlan Somers static void
fdisp_make_mknod_for_fallback(struct fuse_dispatcher * fdip,struct componentname * cnp,struct vnode * dvp,uint64_t parentnid,struct thread * td,struct ucred * cred,mode_t mode,enum fuse_opcode * op)96219ef317dSAlan Somers fdisp_make_mknod_for_fallback(
96319ef317dSAlan Somers struct fuse_dispatcher *fdip,
96419ef317dSAlan Somers struct componentname *cnp,
96519ef317dSAlan Somers struct vnode *dvp,
96619ef317dSAlan Somers uint64_t parentnid,
96719ef317dSAlan Somers struct thread *td,
96819ef317dSAlan Somers struct ucred *cred,
96919ef317dSAlan Somers mode_t mode,
97019ef317dSAlan Somers enum fuse_opcode *op)
97119ef317dSAlan Somers {
97219ef317dSAlan Somers struct fuse_mknod_in *fmni;
97319ef317dSAlan Somers
97419ef317dSAlan Somers fdisp_init(fdip, sizeof(*fmni) + cnp->cn_namelen + 1);
97519ef317dSAlan Somers *op = FUSE_MKNOD;
97619ef317dSAlan Somers fdisp_make(fdip, *op, vnode_mount(dvp), parentnid, td, cred);
97719ef317dSAlan Somers fmni = fdip->indata;
97819ef317dSAlan Somers fmni->mode = mode;
97919ef317dSAlan Somers fmni->rdev = 0;
98019ef317dSAlan Somers memcpy((char *)fdip->indata + sizeof(*fmni), cnp->cn_nameptr,
98119ef317dSAlan Somers cnp->cn_namelen);
98219ef317dSAlan Somers ((char *)fdip->indata)[sizeof(*fmni) + cnp->cn_namelen] = '\0';
98319ef317dSAlan Somers }
9845fe58019SAttilio Rao /*
9855fe58019SAttilio Rao struct vnop_create_args {
9865fe58019SAttilio Rao struct vnode *a_dvp;
9875fe58019SAttilio Rao struct vnode **a_vpp;
9885fe58019SAttilio Rao struct componentname *a_cnp;
9895fe58019SAttilio Rao struct vattr *a_vap;
9905fe58019SAttilio Rao };
9915fe58019SAttilio Rao */
9925fe58019SAttilio Rao static int
fuse_vnop_create(struct vop_create_args * ap)9935fe58019SAttilio Rao fuse_vnop_create(struct vop_create_args *ap)
9945fe58019SAttilio Rao {
9955fe58019SAttilio Rao struct vnode *dvp = ap->a_dvp;
9965fe58019SAttilio Rao struct vnode **vpp = ap->a_vpp;
9975fe58019SAttilio Rao struct componentname *cnp = ap->a_cnp;
9985fe58019SAttilio Rao struct vattr *vap = ap->a_vap;
999b4a58fbfSMateusz Guzik struct thread *td = curthread;
10005fe58019SAttilio Rao struct ucred *cred = cnp->cn_cred;
10015fe58019SAttilio Rao
1002a4856c96SAlan Somers struct fuse_data *data;
1003a4856c96SAlan Somers struct fuse_create_in *fci;
10045fe58019SAttilio Rao struct fuse_entry_out *feo;
100519ef317dSAlan Somers struct fuse_open_out *foo;
100619ef317dSAlan Somers struct fuse_dispatcher fdi, fdi2;
10075fe58019SAttilio Rao struct fuse_dispatcher *fdip = &fdi;
100819ef317dSAlan Somers struct fuse_dispatcher *fdip2 = NULL;
10095fe58019SAttilio Rao
10105fe58019SAttilio Rao int err;
10115fe58019SAttilio Rao
10125fe58019SAttilio Rao struct mount *mp = vnode_mount(dvp);
1013a4856c96SAlan Somers data = fuse_get_mpdata(mp);
10145fe58019SAttilio Rao uint64_t parentnid = VTOFUD(dvp)->nid;
10155fe58019SAttilio Rao mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
101619ef317dSAlan Somers enum fuse_opcode op;
10179e444871SAlan Somers int flags;
10189e444871SAlan Somers
1019d5ff2688SAlan Somers if (fuse_isdeadfs(dvp))
1020d5ff2688SAlan Somers return ENXIO;
1021d5ff2688SAlan Somers
1022d5ff2688SAlan Somers /* FUSE expects sockets to be created with FUSE_MKNOD */
1023d5ff2688SAlan Somers if (vap->va_type == VSOCK)
1024d5ff2688SAlan Somers return fuse_internal_mknod(dvp, vpp, cnp, vap);
1025d5ff2688SAlan Somers
10269e444871SAlan Somers /*
10279e444871SAlan Somers * VOP_CREATE doesn't tell us the open(2) flags, so we guess. Only a
10289e444871SAlan Somers * writable mode makes sense, and we might as well include readability
10299e444871SAlan Somers * too.
10309e444871SAlan Somers */
10319e444871SAlan Somers flags = O_RDWR;
10325fe58019SAttilio Rao
10335fe58019SAttilio Rao bzero(&fdi, sizeof(fdi));
10345fe58019SAttilio Rao
1035d5ff2688SAlan Somers if (vap->va_type != VREG)
1036372b97d0SRick Macklem return (EINVAL);
10375fe58019SAttilio Rao
103837df9d3bSAlan Somers if (fsess_not_impl(mp, FUSE_CREATE) || vap->va_type == VSOCK) {
103919ef317dSAlan Somers /* Fallback to FUSE_MKNOD/FUSE_OPEN */
104019ef317dSAlan Somers fdisp_make_mknod_for_fallback(fdip, cnp, dvp, parentnid, td,
104119ef317dSAlan Somers cred, mode, &op);
104219ef317dSAlan Somers } else {
104319ef317dSAlan Somers /* Use FUSE_CREATE */
1044a4856c96SAlan Somers size_t insize;
1045a4856c96SAlan Somers
104619ef317dSAlan Somers op = FUSE_CREATE;
1047a4856c96SAlan Somers fdisp_init(fdip, sizeof(*fci) + cnp->cn_namelen + 1);
104819ef317dSAlan Somers fdisp_make(fdip, op, vnode_mount(dvp), parentnid, td, cred);
1049a4856c96SAlan Somers fci = fdip->indata;
1050a4856c96SAlan Somers fci->mode = mode;
1051a4856c96SAlan Somers fci->flags = O_CREAT | flags;
1052a4856c96SAlan Somers if (fuse_libabi_geq(data, 7, 12)) {
1053a4856c96SAlan Somers insize = sizeof(*fci);
105485078b85SConrad Meyer fci->umask = td->td_proc->p_pd->pd_cmask;
1055a4856c96SAlan Somers } else {
1056a4856c96SAlan Somers insize = sizeof(struct fuse_open_in);
1057a4856c96SAlan Somers }
1058a4856c96SAlan Somers
1059a4856c96SAlan Somers memcpy((char *)fdip->indata + insize, cnp->cn_nameptr,
10605fe58019SAttilio Rao cnp->cn_namelen);
1061a4856c96SAlan Somers ((char *)fdip->indata)[insize + cnp->cn_namelen] = '\0';
106219ef317dSAlan Somers }
10635fe58019SAttilio Rao
10645fe58019SAttilio Rao err = fdisp_wait_answ(fdip);
10655fe58019SAttilio Rao
10666de3b00dSAttilio Rao if (err) {
106719ef317dSAlan Somers if (err == ENOSYS && op == FUSE_CREATE) {
10685fe58019SAttilio Rao fsess_set_notimpl(mp, FUSE_CREATE);
1069caeea8b4SAlan Somers fdisp_destroy(fdip);
107019ef317dSAlan Somers fdisp_make_mknod_for_fallback(fdip, cnp, dvp,
107119ef317dSAlan Somers parentnid, td, cred, mode, &op);
107219ef317dSAlan Somers err = fdisp_wait_answ(fdip);
107319ef317dSAlan Somers }
107419ef317dSAlan Somers if (err)
10755fe58019SAttilio Rao goto out;
10765fe58019SAttilio Rao }
1077372b97d0SRick Macklem
10785fe58019SAttilio Rao feo = fdip->answ;
10795fe58019SAttilio Rao
1080ede571e4SAlan Somers if ((err = fuse_internal_checkentry(feo, vap->va_type))) {
10815fe58019SAttilio Rao goto out;
10825fe58019SAttilio Rao }
108319ef317dSAlan Somers
108419ef317dSAlan Somers if (op == FUSE_CREATE) {
108545825a12SAlan Somers if (fuse_libabi_geq(data, 7, 9))
108619ef317dSAlan Somers foo = (struct fuse_open_out*)(feo + 1);
108745825a12SAlan Somers else
108845825a12SAlan Somers foo = (struct fuse_open_out*)((char*)feo +
108945825a12SAlan Somers FUSE_COMPAT_ENTRY_OUT_SIZE);
109019ef317dSAlan Somers } else {
109119ef317dSAlan Somers /* Issue a separate FUSE_OPEN */
1092a4856c96SAlan Somers struct fuse_open_in *foi;
1093a4856c96SAlan Somers
109419ef317dSAlan Somers fdip2 = &fdi2;
109519ef317dSAlan Somers fdisp_init(fdip2, sizeof(*foi));
109619ef317dSAlan Somers fdisp_make(fdip2, FUSE_OPEN, vnode_mount(dvp), feo->nodeid, td,
109719ef317dSAlan Somers cred);
109819ef317dSAlan Somers foi = fdip2->indata;
10999e444871SAlan Somers foi->flags = flags;
110019ef317dSAlan Somers err = fdisp_wait_answ(fdip2);
110119ef317dSAlan Somers if (err)
110219ef317dSAlan Somers goto out;
110319ef317dSAlan Somers foo = fdip2->answ;
110419ef317dSAlan Somers }
1105ede571e4SAlan Somers err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, vap->va_type);
11065fe58019SAttilio Rao if (err) {
11075fe58019SAttilio Rao struct fuse_release_in *fri;
11085fe58019SAttilio Rao uint64_t nodeid = feo->nodeid;
110919ef317dSAlan Somers uint64_t fh_id = foo->fh;
11105fe58019SAttilio Rao
11111bdf879bSAlan Somers fdisp_destroy(fdip);
11125fe58019SAttilio Rao fdisp_init(fdip, sizeof(*fri));
11135fe58019SAttilio Rao fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, td, cred);
11145fe58019SAttilio Rao fri = fdip->indata;
11155fe58019SAttilio Rao fri->fh = fh_id;
11169e444871SAlan Somers fri->flags = flags;
11175fe58019SAttilio Rao fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
1118268c28edSAlan Somers fuse_insert_message(fdip->tick, false);
111919ef317dSAlan Somers goto out;
11205fe58019SAttilio Rao }
11215fe58019SAttilio Rao ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
11220269ae4cSAlan Somers fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
11235d94aaacSAlan Somers feo->attr_valid_nsec, NULL, true);
11245fe58019SAttilio Rao
1125a7e81cb3SAlan Somers fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
112619ef317dSAlan Somers fuse_vnode_open(*vpp, foo->open_flags, td);
1127002e54b0SAlan Somers /*
1128002e54b0SAlan Somers * Purge the parent's attribute cache because the daemon should've
1129002e54b0SAlan Somers * updated its mtime and ctime
1130002e54b0SAlan Somers */
1131002e54b0SAlan Somers fuse_vnode_clear_attr_cache(dvp);
11325fe58019SAttilio Rao cache_purge_negative(dvp);
11335fe58019SAttilio Rao
11345fe58019SAttilio Rao out:
113519ef317dSAlan Somers if (fdip2)
113619ef317dSAlan Somers fdisp_destroy(fdip2);
11375fe58019SAttilio Rao fdisp_destroy(fdip);
11385fe58019SAttilio Rao return err;
11395fe58019SAttilio Rao }
11405fe58019SAttilio Rao
11415fe58019SAttilio Rao /*
1142915012e0SAlan Somers struct vnop_fdatasync_args {
1143915012e0SAlan Somers struct vop_generic_args a_gen;
1144915012e0SAlan Somers struct vnode * a_vp;
1145915012e0SAlan Somers struct thread * a_td;
1146915012e0SAlan Somers };
11475fe58019SAttilio Rao */
1148915012e0SAlan Somers static int
fuse_vnop_fdatasync(struct vop_fdatasync_args * ap)1149915012e0SAlan Somers fuse_vnop_fdatasync(struct vop_fdatasync_args *ap)
1150915012e0SAlan Somers {
1151915012e0SAlan Somers struct vnode *vp = ap->a_vp;
1152915012e0SAlan Somers struct thread *td = ap->a_td;
1153915012e0SAlan Somers int waitfor = MNT_WAIT;
1154915012e0SAlan Somers
1155915012e0SAlan Somers int err = 0;
1156915012e0SAlan Somers
1157915012e0SAlan Somers if (fuse_isdeadfs(vp)) {
1158915012e0SAlan Somers return 0;
1159915012e0SAlan Somers }
1160915012e0SAlan Somers if ((err = vop_stdfdatasync_buf(ap)))
1161915012e0SAlan Somers return err;
1162915012e0SAlan Somers
1163915012e0SAlan Somers return fuse_internal_fsync(vp, td, waitfor, true);
1164915012e0SAlan Somers }
11655fe58019SAttilio Rao
11665fe58019SAttilio Rao /*
11675fe58019SAttilio Rao struct vnop_fsync_args {
1168915012e0SAlan Somers struct vop_generic_args a_gen;
11695fe58019SAttilio Rao struct vnode * a_vp;
11705fe58019SAttilio Rao int a_waitfor;
11715fe58019SAttilio Rao struct thread * a_td;
11725fe58019SAttilio Rao };
11735fe58019SAttilio Rao */
11745fe58019SAttilio Rao static int
fuse_vnop_fsync(struct vop_fsync_args * ap)11755fe58019SAttilio Rao fuse_vnop_fsync(struct vop_fsync_args *ap)
11765fe58019SAttilio Rao {
11775fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
11785fe58019SAttilio Rao struct thread *td = ap->a_td;
117990612f3cSAlan Somers int waitfor = ap->a_waitfor;
1180915012e0SAlan Somers int err = 0;
11815fe58019SAttilio Rao
11825fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
11835fe58019SAttilio Rao return 0;
11845fe58019SAttilio Rao }
11855fe58019SAttilio Rao if ((err = vop_stdfsync(ap)))
11865fe58019SAttilio Rao return err;
11875fe58019SAttilio Rao
1188915012e0SAlan Somers return fuse_internal_fsync(vp, td, waitfor, false);
11895fe58019SAttilio Rao }
11905fe58019SAttilio Rao
11915fe58019SAttilio Rao /*
11925fe58019SAttilio Rao struct vnop_getattr_args {
11935fe58019SAttilio Rao struct vnode *a_vp;
11945fe58019SAttilio Rao struct vattr *a_vap;
11955fe58019SAttilio Rao struct ucred *a_cred;
11965fe58019SAttilio Rao struct thread *a_td;
11975fe58019SAttilio Rao };
11985fe58019SAttilio Rao */
11995fe58019SAttilio Rao static int
fuse_vnop_getattr(struct vop_getattr_args * ap)12005fe58019SAttilio Rao fuse_vnop_getattr(struct vop_getattr_args *ap)
12015fe58019SAttilio Rao {
12025fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
12035fe58019SAttilio Rao struct vattr *vap = ap->a_vap;
12045fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
12055fe58019SAttilio Rao struct thread *td = curthread;
12065fe58019SAttilio Rao
12075fe58019SAttilio Rao int err = 0;
12085fe58019SAttilio Rao int dataflags;
12095fe58019SAttilio Rao
12105fe58019SAttilio Rao dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags;
12115fe58019SAttilio Rao
12125fe58019SAttilio Rao /* Note that we are not bailing out on a dead file system just yet. */
12135fe58019SAttilio Rao
12145fe58019SAttilio Rao if (!(dataflags & FSESS_INITED)) {
12155fe58019SAttilio Rao if (!vnode_isvroot(vp)) {
12165fe58019SAttilio Rao fdata_set_dead(fuse_get_mpdata(vnode_mount(vp)));
12175fe58019SAttilio Rao err = ENOTCONN;
12185fe58019SAttilio Rao return err;
12195fe58019SAttilio Rao } else {
12205fe58019SAttilio Rao goto fake;
12215fe58019SAttilio Rao }
12225fe58019SAttilio Rao }
1223cad67791SAlan Somers err = fuse_internal_getattr(vp, vap, cred, td);
1224cad67791SAlan Somers if (err == ENOTCONN && vnode_isvroot(vp)) {
1225cf169498SAlan Somers /* see comment in fuse_vfsop_statfs() */
12265fe58019SAttilio Rao goto fake;
1227cad67791SAlan Somers } else {
12285fe58019SAttilio Rao return err;
1229cad67791SAlan Somers }
12305fe58019SAttilio Rao
12315fe58019SAttilio Rao fake:
12325fe58019SAttilio Rao bzero(vap, sizeof(*vap));
12335fe58019SAttilio Rao vap->va_type = vnode_vtype(vp);
12345fe58019SAttilio Rao
12355fe58019SAttilio Rao return 0;
12365fe58019SAttilio Rao }
12375fe58019SAttilio Rao
12385fe58019SAttilio Rao /*
12395fe58019SAttilio Rao struct vnop_inactive_args {
12405fe58019SAttilio Rao struct vnode *a_vp;
12415fe58019SAttilio Rao };
12425fe58019SAttilio Rao */
12435fe58019SAttilio Rao static int
fuse_vnop_inactive(struct vop_inactive_args * ap)12445fe58019SAttilio Rao fuse_vnop_inactive(struct vop_inactive_args *ap)
12455fe58019SAttilio Rao {
12465fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
1247ab21ed17SMateusz Guzik struct thread *td = curthread;
12485fe58019SAttilio Rao
12495fe58019SAttilio Rao struct fuse_vnode_data *fvdat = VTOFUD(vp);
12505ec10aa5SAlan Somers struct fuse_filehandle *fufh, *fufh_tmp;
12515fe58019SAttilio Rao
12525ec10aa5SAlan Somers int need_flush = 1;
12535fe58019SAttilio Rao
12545ec10aa5SAlan Somers LIST_FOREACH_SAFE(fufh, &fvdat->handles, next, fufh_tmp) {
12555fe58019SAttilio Rao if (need_flush && vp->v_type == VREG) {
12565fe58019SAttilio Rao if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
1257f8d4af10SAlan Somers fuse_vnode_savesize(vp, NULL, 0);
12585fe58019SAttilio Rao }
12595940f822SAlan Somers if ((fvdat->flag & FN_REVOKED) != 0)
12605fe58019SAttilio Rao fuse_io_invalbuf(vp, td);
12615fe58019SAttilio Rao else
12625fe58019SAttilio Rao fuse_io_flushbuf(vp, MNT_WAIT, td);
12635fe58019SAttilio Rao need_flush = 0;
12645fe58019SAttilio Rao }
12655ec10aa5SAlan Somers fuse_filehandle_close(vp, fufh, td, NULL);
12665fe58019SAttilio Rao }
12675fe58019SAttilio Rao
1268435ecf40SAlan Somers if ((fvdat->flag & FN_REVOKED) != 0)
12695fe58019SAttilio Rao vrecycle(vp);
1270435ecf40SAlan Somers
12715fe58019SAttilio Rao return 0;
12725fe58019SAttilio Rao }
12735fe58019SAttilio Rao
12745fe58019SAttilio Rao /*
127537df9d3bSAlan Somers struct vnop_ioctl_args {
127637df9d3bSAlan Somers struct vnode *a_vp;
127737df9d3bSAlan Somers u_long a_command;
127837df9d3bSAlan Somers caddr_t a_data;
127937df9d3bSAlan Somers int a_fflag;
128037df9d3bSAlan Somers struct ucred *a_cred;
128137df9d3bSAlan Somers struct thread *a_td;
128237df9d3bSAlan Somers };
128337df9d3bSAlan Somers */
128437df9d3bSAlan Somers static int
fuse_vnop_ioctl(struct vop_ioctl_args * ap)128537df9d3bSAlan Somers fuse_vnop_ioctl(struct vop_ioctl_args *ap)
128637df9d3bSAlan Somers {
128737df9d3bSAlan Somers struct vnode *vp = ap->a_vp;
128837df9d3bSAlan Somers struct mount *mp = vnode_mount(vp);
128937df9d3bSAlan Somers struct ucred *cred = ap->a_cred;
129037df9d3bSAlan Somers off_t *offp;
129137df9d3bSAlan Somers pid_t pid = ap->a_td->td_proc->p_pid;
129237df9d3bSAlan Somers int err;
129337df9d3bSAlan Somers
129437df9d3bSAlan Somers switch (ap->a_command) {
129537df9d3bSAlan Somers case FIOSEEKDATA:
129637df9d3bSAlan Somers case FIOSEEKHOLE:
129737df9d3bSAlan Somers /* Call FUSE_LSEEK, if we can, or fall back to vop_stdioctl */
129837df9d3bSAlan Somers if (fsess_maybe_impl(mp, FUSE_LSEEK)) {
129937df9d3bSAlan Somers int whence;
130037df9d3bSAlan Somers
130137df9d3bSAlan Somers offp = ap->a_data;
130237df9d3bSAlan Somers if (ap->a_command == FIOSEEKDATA)
130337df9d3bSAlan Somers whence = SEEK_DATA;
130437df9d3bSAlan Somers else
130537df9d3bSAlan Somers whence = SEEK_HOLE;
130637df9d3bSAlan Somers
130737df9d3bSAlan Somers vn_lock(vp, LK_SHARED | LK_RETRY);
130837df9d3bSAlan Somers err = fuse_vnop_do_lseek(vp, ap->a_td, cred, pid, offp,
130937df9d3bSAlan Somers whence);
131037df9d3bSAlan Somers VOP_UNLOCK(vp);
131137df9d3bSAlan Somers }
131237df9d3bSAlan Somers if (fsess_not_impl(mp, FUSE_LSEEK))
131337df9d3bSAlan Somers err = vop_stdioctl(ap);
131437df9d3bSAlan Somers break;
131537df9d3bSAlan Somers default:
131637df9d3bSAlan Somers /* TODO: implement FUSE_IOCTL */
131737df9d3bSAlan Somers err = ENOTTY;
131837df9d3bSAlan Somers break;
131937df9d3bSAlan Somers }
132037df9d3bSAlan Somers return (err);
132137df9d3bSAlan Somers }
132237df9d3bSAlan Somers
132337df9d3bSAlan Somers
132437df9d3bSAlan Somers /*
13255fe58019SAttilio Rao struct vnop_link_args {
13265fe58019SAttilio Rao struct vnode *a_tdvp;
13275fe58019SAttilio Rao struct vnode *a_vp;
13285fe58019SAttilio Rao struct componentname *a_cnp;
13295fe58019SAttilio Rao };
13305fe58019SAttilio Rao */
13315fe58019SAttilio Rao static int
fuse_vnop_link(struct vop_link_args * ap)13325fe58019SAttilio Rao fuse_vnop_link(struct vop_link_args *ap)
13335fe58019SAttilio Rao {
13345fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
13355fe58019SAttilio Rao struct vnode *tdvp = ap->a_tdvp;
13365fe58019SAttilio Rao struct componentname *cnp = ap->a_cnp;
13375fe58019SAttilio Rao
13385fe58019SAttilio Rao struct vattr *vap = VTOVA(vp);
13395fe58019SAttilio Rao
13405fe58019SAttilio Rao struct fuse_dispatcher fdi;
13415fe58019SAttilio Rao struct fuse_entry_out *feo;
13425fe58019SAttilio Rao struct fuse_link_in fli;
13435fe58019SAttilio Rao
13445fe58019SAttilio Rao int err;
13455fe58019SAttilio Rao
13465fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
13475fe58019SAttilio Rao return ENXIO;
13485fe58019SAttilio Rao }
13495fe58019SAttilio Rao if (vnode_mount(tdvp) != vnode_mount(vp)) {
13505fe58019SAttilio Rao return EXDEV;
13515fe58019SAttilio Rao }
135278a7722fSConrad Meyer
135378a7722fSConrad Meyer /*
135478a7722fSConrad Meyer * This is a seatbelt check to protect naive userspace filesystems from
135578a7722fSConrad Meyer * themselves and the limitations of the FUSE IPC protocol. If a
135678a7722fSConrad Meyer * filesystem does not allow attribute caching, assume it is capable of
135778a7722fSConrad Meyer * validating that nlink does not overflow.
135878a7722fSConrad Meyer */
135978a7722fSConrad Meyer if (vap != NULL && vap->va_nlink >= FUSE_LINK_MAX)
13605fe58019SAttilio Rao return EMLINK;
13615fe58019SAttilio Rao fli.oldnodeid = VTOI(vp);
13625fe58019SAttilio Rao
13635fe58019SAttilio Rao fdisp_init(&fdi, 0);
13645fe58019SAttilio Rao fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp,
13655fe58019SAttilio Rao FUSE_LINK, &fli, sizeof(fli), &fdi);
13665fe58019SAttilio Rao if ((err = fdisp_wait_answ(&fdi))) {
13675fe58019SAttilio Rao goto out;
13685fe58019SAttilio Rao }
13695fe58019SAttilio Rao feo = fdi.answ;
13705fe58019SAttilio Rao
13710bef4927SAlan Somers if (fli.oldnodeid != feo->nodeid) {
13720bef4927SAlan Somers struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
13730bef4927SAlan Somers fuse_warn(data, FSESS_WARN_ILLEGAL_INODE,
13740bef4927SAlan Somers "Assigned wrong inode for a hard link.");
13750bef4927SAlan Somers fuse_vnode_clear_attr_cache(vp);
13760bef4927SAlan Somers fuse_vnode_clear_attr_cache(tdvp);
13770bef4927SAlan Somers err = EIO;
13780bef4927SAlan Somers goto out;
13790bef4927SAlan Somers }
13800bef4927SAlan Somers
13815fe58019SAttilio Rao err = fuse_internal_checkentry(feo, vnode_vtype(vp));
1382002e54b0SAlan Somers if (!err) {
1383002e54b0SAlan Somers /*
1384002e54b0SAlan Somers * Purge the parent's attribute cache because the daemon
1385002e54b0SAlan Somers * should've updated its mtime and ctime
1386002e54b0SAlan Somers */
1387002e54b0SAlan Somers fuse_vnode_clear_attr_cache(tdvp);
13880269ae4cSAlan Somers fuse_internal_cache_attrs(vp, &feo->attr, feo->attr_valid,
13895d94aaacSAlan Somers feo->attr_valid_nsec, NULL, true);
1390002e54b0SAlan Somers }
13915fe58019SAttilio Rao out:
13925fe58019SAttilio Rao fdisp_destroy(&fdi);
13935fe58019SAttilio Rao return err;
13945fe58019SAttilio Rao }
13955fe58019SAttilio Rao
1396dc14d593SAlan Somers struct fuse_lookup_alloc_arg {
1397dc14d593SAlan Somers struct fuse_entry_out *feo;
1398dc14d593SAlan Somers struct componentname *cnp;
1399dc14d593SAlan Somers uint64_t nid;
1400ba8cc6d7SMateusz Guzik __enum_uint8(vtype) vtyp;
1401dc14d593SAlan Somers };
1402dc14d593SAlan Somers
1403dc14d593SAlan Somers /* Callback for vn_get_ino */
1404dc14d593SAlan Somers static int
fuse_lookup_alloc(struct mount * mp,void * arg,int lkflags,struct vnode ** vpp)1405dc14d593SAlan Somers fuse_lookup_alloc(struct mount *mp, void *arg, int lkflags, struct vnode **vpp)
1406dc14d593SAlan Somers {
1407dc14d593SAlan Somers struct fuse_lookup_alloc_arg *flaa = arg;
1408dc14d593SAlan Somers
1409dc14d593SAlan Somers return fuse_vnode_get(mp, flaa->feo, flaa->nid, NULL, vpp, flaa->cnp,
1410dc14d593SAlan Somers flaa->vtyp);
1411dc14d593SAlan Somers }
1412dc14d593SAlan Somers
1413419e7ff6SAlan Somers SDT_PROBE_DEFINE3(fusefs, , vnops, cache_lookup,
1414ccb75e49SAlan Somers "int", "struct timespec*", "struct timespec*");
14155fe58019SAttilio Rao /*
14165fe58019SAttilio Rao struct vnop_lookup_args {
14175fe58019SAttilio Rao struct vnodeop_desc *a_desc;
14185fe58019SAttilio Rao struct vnode *a_dvp;
14195fe58019SAttilio Rao struct vnode **a_vpp;
14205fe58019SAttilio Rao struct componentname *a_cnp;
14215fe58019SAttilio Rao };
14225fe58019SAttilio Rao */
14235fe58019SAttilio Rao int
fuse_vnop_lookup(struct vop_lookup_args * ap)14245fe58019SAttilio Rao fuse_vnop_lookup(struct vop_lookup_args *ap)
14255fe58019SAttilio Rao {
14265fe58019SAttilio Rao struct vnode *dvp = ap->a_dvp;
14275fe58019SAttilio Rao struct vnode **vpp = ap->a_vpp;
14285fe58019SAttilio Rao struct componentname *cnp = ap->a_cnp;
1429b4a58fbfSMateusz Guzik struct thread *td = curthread;
14305fe58019SAttilio Rao struct ucred *cred = cnp->cn_cred;
143113d593a5SAlan Somers struct timespec now;
14325fe58019SAttilio Rao
14335fe58019SAttilio Rao int nameiop = cnp->cn_nameiop;
14345fe58019SAttilio Rao int flags = cnp->cn_flags;
14355fe58019SAttilio Rao int islastcn = flags & ISLASTCN;
14365fe58019SAttilio Rao struct mount *mp = vnode_mount(dvp);
1437bfcb817bSAlan Somers struct fuse_data *data = fuse_get_mpdata(mp);
1438bfcb817bSAlan Somers int default_permissions = data->dataflags & FSESS_DEFAULT_PERMISSIONS;
14390bef4927SAlan Somers bool is_dot;
14405fe58019SAttilio Rao
14415fe58019SAttilio Rao int err = 0;
14425fe58019SAttilio Rao int lookup_err = 0;
14435fe58019SAttilio Rao struct vnode *vp = NULL;
14445fe58019SAttilio Rao
14455fe58019SAttilio Rao struct fuse_dispatcher fdi;
1446438b8a6fSAlan Somers bool did_lookup = false;
144744f10c6eSAlan Somers struct fuse_entry_out *feo = NULL;
1448ba8cc6d7SMateusz Guzik __enum_uint8(vtype) vtyp; /* vnode type of target */
14495fe58019SAttilio Rao
14505fe58019SAttilio Rao uint64_t nid;
14515fe58019SAttilio Rao
14525fe58019SAttilio Rao if (fuse_isdeadfs(dvp)) {
14535fe58019SAttilio Rao *vpp = NULL;
14545fe58019SAttilio Rao return ENXIO;
14555fe58019SAttilio Rao }
145673825da3SAlan Somers if (!vnode_isdir(dvp))
14575fe58019SAttilio Rao return ENOTDIR;
14585fe58019SAttilio Rao
145973825da3SAlan Somers if (islastcn && vfs_isrdonly(mp) && (nameiop != LOOKUP))
146073825da3SAlan Somers return EROFS;
146173825da3SAlan Somers
14626a5abb1eSKyle Evans if ((cnp->cn_flags & NOEXECCHECK) != 0)
14636a5abb1eSKyle Evans cnp->cn_flags &= ~NOEXECCHECK;
14646a5abb1eSKyle Evans else if ((err = fuse_internal_access(dvp, VEXEC, td, cred)))
14655fe58019SAttilio Rao return err;
146673825da3SAlan Somers
14670bef4927SAlan Somers is_dot = cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.';
14681613087aSAlan Somers if ((flags & ISDOTDOT) && !(data->dataflags & FSESS_EXPORT_SUPPORT))
14691613087aSAlan Somers {
14701613087aSAlan Somers if (!(VTOFUD(dvp)->flag & FN_PARENT_NID)) {
14711613087aSAlan Somers /*
14721613087aSAlan Somers * Since the file system doesn't support ".." lookups,
14731613087aSAlan Somers * we have no way to find this entry.
14741613087aSAlan Somers */
14751613087aSAlan Somers return ESTALE;
14761613087aSAlan Somers }
14775fe58019SAttilio Rao nid = VTOFUD(dvp)->parent_nid;
1478438b8a6fSAlan Somers if (nid == 0)
14795fe58019SAttilio Rao return ENOENT;
1480438b8a6fSAlan Somers /* .. is obviously a directory */
1481438b8a6fSAlan Somers vtyp = VDIR;
14820bef4927SAlan Somers } else if (is_dot) {
14835fe58019SAttilio Rao nid = VTOI(dvp);
1484438b8a6fSAlan Somers /* . is obviously a directory */
1485438b8a6fSAlan Somers vtyp = VDIR;
1486ccb75e49SAlan Somers } else {
148713d593a5SAlan Somers struct timespec timeout;
1488ef1534caSGordon Bergling int ncpticks; /* here to accommodate for API contract */
14893f2c630cSAlan Somers
14904961e997SMateusz Guzik err = cache_lookup(dvp, vpp, cnp, &timeout, &ncpticks);
14913f2c630cSAlan Somers getnanouptime(&now);
1492419e7ff6SAlan Somers SDT_PROBE3(fusefs, , vnops, cache_lookup, err, &timeout, &now);
1493ccb75e49SAlan Somers switch (err) {
1494ccb75e49SAlan Somers case -1: /* positive match */
1495ccb75e49SAlan Somers if (timespeccmp(&timeout, &now, >)) {
1496560a55d0SAlan Somers counter_u64_add(fuse_lookup_cache_hits, 1);
14973f2c630cSAlan Somers } else {
14983f2c630cSAlan Somers /* Cache timeout */
1499560a55d0SAlan Somers counter_u64_add(fuse_lookup_cache_misses, 1);
15000d2bf489SAlan Somers bintime_clear(
15010d2bf489SAlan Somers &VTOFUD(*vpp)->entry_cache_timeout);
15024683b905SAlan Somers cache_purge(*vpp);
1503ccb75e49SAlan Somers if (dvp != *vpp)
1504ccb75e49SAlan Somers vput(*vpp);
1505ccb75e49SAlan Somers else
1506ccb75e49SAlan Somers vrele(*vpp);
1507ccb75e49SAlan Somers *vpp = NULL;
15083f2c630cSAlan Somers break;
15093f2c630cSAlan Somers }
15103f2c630cSAlan Somers return 0;
15113f2c630cSAlan Somers
15123f2c630cSAlan Somers case 0: /* no match in cache */
1513560a55d0SAlan Somers counter_u64_add(fuse_lookup_cache_misses, 1);
15143f2c630cSAlan Somers break;
15153f2c630cSAlan Somers
15163f2c630cSAlan Somers case ENOENT: /* negative match */
1517ccb75e49SAlan Somers if (timespeccmp(&timeout, &now, <=)) {
15183f2c630cSAlan Somers /* Cache timeout */
1519ccb75e49SAlan Somers cache_purge_negative(dvp);
15203f2c630cSAlan Somers break;
15213f2c630cSAlan Somers }
15223f2c630cSAlan Somers /* fall through */
15233f2c630cSAlan Somers default:
15243f2c630cSAlan Somers return err;
15253f2c630cSAlan Somers }
1526438b8a6fSAlan Somers
15275fe58019SAttilio Rao fdisp_init(&fdi, cnp->cn_namelen + 1);
15281613087aSAlan Somers fdisp_make(&fdi, FUSE_LOOKUP, mp, VTOI(dvp), td, cred);
15295fe58019SAttilio Rao
15305fe58019SAttilio Rao memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
15315fe58019SAttilio Rao ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
15325fe58019SAttilio Rao lookup_err = fdisp_wait_answ(&fdi);
1533438b8a6fSAlan Somers did_lookup = true;
15345fe58019SAttilio Rao
1535438b8a6fSAlan Somers if (!lookup_err) {
1536ccb75e49SAlan Somers /* lookup call succeeded */
1537438b8a6fSAlan Somers feo = (struct fuse_entry_out *)fdi.answ;
1538e5b50fe7SAlan Somers nid = feo->nodeid;
153944f10c6eSAlan Somers if (nid == 0) {
154044f10c6eSAlan Somers /* zero nodeid means ENOENT and cache it */
154144f10c6eSAlan Somers struct timespec timeout;
154244f10c6eSAlan Somers
15436248288eSAlan Somers fdi.answ_stat = ENOENT;
15445fe58019SAttilio Rao lookup_err = ENOENT;
154544f10c6eSAlan Somers if (cnp->cn_flags & MAKEENTRY) {
154644f10c6eSAlan Somers fuse_validity_2_timespec(feo, &timeout);
15471613087aSAlan Somers /* Use the same entry_time for .. as for
15481613087aSAlan Somers * the file itself. That doesn't honor
15491613087aSAlan Somers * exactly what the fuse server tells
15501613087aSAlan Somers * us, but to do otherwise would require
15511613087aSAlan Somers * another cache lookup at this point.
15521613087aSAlan Somers */
15531613087aSAlan Somers struct timespec *dtsp = NULL;
1554438b8a6fSAlan Somers cache_enter_time(dvp, *vpp, cnp,
15551613087aSAlan Somers &timeout, dtsp);
155644f10c6eSAlan Somers }
15575fe58019SAttilio Rao }
1558438b8a6fSAlan Somers vtyp = IFTOVT(feo->attr.mode);
15595fe58019SAttilio Rao }
1560438b8a6fSAlan Somers if (lookup_err && (!fdi.answ_stat || lookup_err != ENOENT)) {
15615fe58019SAttilio Rao fdisp_destroy(&fdi);
15625fe58019SAttilio Rao return lookup_err;
15635fe58019SAttilio Rao }
1564438b8a6fSAlan Somers }
15655fe58019SAttilio Rao /* lookup_err, if non-zero, must be ENOENT at this point */
15665fe58019SAttilio Rao
15675fe58019SAttilio Rao if (lookup_err) {
1568ff4fbdf5SAlan Somers /* Entry not found */
1569ff4fbdf5SAlan Somers if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1570bfcb817bSAlan Somers if (default_permissions)
1571bfcb817bSAlan Somers err = fuse_internal_access(dvp, VWRITE, td,
1572bfcb817bSAlan Somers cred);
1573bfcb817bSAlan Somers else
1574bfcb817bSAlan Somers err = 0;
15756124fd71SAlan Somers if (!err) {
15765fe58019SAttilio Rao err = EJUSTRETURN;
15775fe58019SAttilio Rao }
15786124fd71SAlan Somers } else {
15795fe58019SAttilio Rao err = ENOENT;
15806124fd71SAlan Somers }
15815fe58019SAttilio Rao } else {
1582ff4fbdf5SAlan Somers /* Entry was found */
15835fe58019SAttilio Rao if (flags & ISDOTDOT) {
1584dc14d593SAlan Somers struct fuse_lookup_alloc_arg flaa;
15855fe58019SAttilio Rao
1586dc14d593SAlan Somers flaa.nid = nid;
1587dc14d593SAlan Somers flaa.feo = feo;
1588dc14d593SAlan Somers flaa.cnp = cnp;
1589dc14d593SAlan Somers flaa.vtyp = vtyp;
1590dc14d593SAlan Somers err = vn_vget_ino_gen(dvp, fuse_lookup_alloc, &flaa, 0,
1591dc14d593SAlan Somers &vp);
15925fe58019SAttilio Rao *vpp = vp;
15935fe58019SAttilio Rao } else if (nid == VTOI(dvp)) {
15940bef4927SAlan Somers if (is_dot) {
15955fe58019SAttilio Rao vref(dvp);
15965fe58019SAttilio Rao *vpp = dvp;
15975fe58019SAttilio Rao } else {
15980bef4927SAlan Somers fuse_warn(fuse_get_mpdata(mp),
15990bef4927SAlan Somers FSESS_WARN_ILLEGAL_INODE,
16000bef4927SAlan Somers "Assigned same inode to both parent and "
16010bef4927SAlan Somers "child.");
16020bef4927SAlan Somers err = EIO;
16030bef4927SAlan Somers }
16040bef4927SAlan Somers
16050bef4927SAlan Somers } else {
16063c324b94SConrad Meyer struct fuse_vnode_data *fvdat;
16073c324b94SConrad Meyer
160809176f09SConrad Meyer err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
1609438b8a6fSAlan Somers &vp, cnp, vtyp);
16106124fd71SAlan Somers if (err)
16115fe58019SAttilio Rao goto out;
16126124fd71SAlan Somers *vpp = vp;
16133c324b94SConrad Meyer fvdat = VTOFUD(vp);
16145fe58019SAttilio Rao
16156124fd71SAlan Somers MPASS(feo != NULL);
161613d593a5SAlan Somers if (timespeccmp(&now, &fvdat->last_local_modify, >)) {
161713d593a5SAlan Somers /*
161813d593a5SAlan Somers * Attributes from the server are definitely
161913d593a5SAlan Somers * newer than the last attributes we sent to
162013d593a5SAlan Somers * the server, so cache them.
162113d593a5SAlan Somers */
16220269ae4cSAlan Somers fuse_internal_cache_attrs(*vpp, &feo->attr,
162313d593a5SAlan Somers feo->attr_valid, feo->attr_valid_nsec,
162413d593a5SAlan Somers NULL, true);
162513d593a5SAlan Somers }
16260d2bf489SAlan Somers fuse_validity_2_bintime(feo->entry_valid,
16270d2bf489SAlan Somers feo->entry_valid_nsec,
16280d2bf489SAlan Somers &fvdat->entry_cache_timeout);
16296124fd71SAlan Somers
16306124fd71SAlan Somers if ((nameiop == DELETE || nameiop == RENAME) &&
1631bfcb817bSAlan Somers islastcn && default_permissions)
16326124fd71SAlan Somers {
16336124fd71SAlan Somers struct vattr dvattr;
16346124fd71SAlan Somers
16356124fd71SAlan Somers err = fuse_internal_access(dvp, VWRITE, td,
16366124fd71SAlan Somers cred);
16376124fd71SAlan Somers if (err != 0)
16386124fd71SAlan Somers goto out;
16396124fd71SAlan Somers /*
16406124fd71SAlan Somers * if the parent's sticky bit is set, check
16416124fd71SAlan Somers * whether we're allowed to remove the file.
16426124fd71SAlan Somers * Need to figure out the vnode locking to make
16436124fd71SAlan Somers * this work.
16446124fd71SAlan Somers */
16456124fd71SAlan Somers fuse_internal_getattr(dvp, &dvattr, cred, td);
16466124fd71SAlan Somers if ((dvattr.va_mode & S_ISTXT) &&
16476124fd71SAlan Somers fuse_internal_access(dvp, VADMIN, td,
16486124fd71SAlan Somers cred) &&
16496124fd71SAlan Somers fuse_internal_access(*vpp, VADMIN, td,
16506124fd71SAlan Somers cred)) {
16516124fd71SAlan Somers err = EPERM;
16526124fd71SAlan Somers goto out;
16536124fd71SAlan Somers }
16546124fd71SAlan Somers }
16555fe58019SAttilio Rao }
16565fe58019SAttilio Rao }
16575fe58019SAttilio Rao out:
16586124fd71SAlan Somers if (err) {
16596124fd71SAlan Somers if (vp != NULL && dvp != vp)
16606124fd71SAlan Somers vput(vp);
16616124fd71SAlan Somers else if (vp != NULL)
16626124fd71SAlan Somers vrele(vp);
16636124fd71SAlan Somers *vpp = NULL;
16645fe58019SAttilio Rao }
1665438b8a6fSAlan Somers if (did_lookup)
16665fe58019SAttilio Rao fdisp_destroy(&fdi);
16675fe58019SAttilio Rao
16685fe58019SAttilio Rao return err;
16695fe58019SAttilio Rao }
16705fe58019SAttilio Rao
16715fe58019SAttilio Rao /*
16725fe58019SAttilio Rao struct vnop_mkdir_args {
16735fe58019SAttilio Rao struct vnode *a_dvp;
16745fe58019SAttilio Rao struct vnode **a_vpp;
16755fe58019SAttilio Rao struct componentname *a_cnp;
16765fe58019SAttilio Rao struct vattr *a_vap;
16775fe58019SAttilio Rao };
16785fe58019SAttilio Rao */
16795fe58019SAttilio Rao static int
fuse_vnop_mkdir(struct vop_mkdir_args * ap)16805fe58019SAttilio Rao fuse_vnop_mkdir(struct vop_mkdir_args *ap)
16815fe58019SAttilio Rao {
16825fe58019SAttilio Rao struct vnode *dvp = ap->a_dvp;
16835fe58019SAttilio Rao struct vnode **vpp = ap->a_vpp;
16845fe58019SAttilio Rao struct componentname *cnp = ap->a_cnp;
16855fe58019SAttilio Rao struct vattr *vap = ap->a_vap;
16865fe58019SAttilio Rao
16875fe58019SAttilio Rao struct fuse_mkdir_in fmdi;
16885fe58019SAttilio Rao
16895fe58019SAttilio Rao if (fuse_isdeadfs(dvp)) {
16905fe58019SAttilio Rao return ENXIO;
16915fe58019SAttilio Rao }
16925fe58019SAttilio Rao fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
169385078b85SConrad Meyer fmdi.umask = curthread->td_proc->p_pd->pd_cmask;
16945fe58019SAttilio Rao
1695d9454fabSAttilio Rao return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
1696d9454fabSAttilio Rao sizeof(fmdi), VDIR));
16975fe58019SAttilio Rao }
16985fe58019SAttilio Rao
16995fe58019SAttilio Rao /*
17005fe58019SAttilio Rao struct vnop_mknod_args {
17015fe58019SAttilio Rao struct vnode *a_dvp;
17025fe58019SAttilio Rao struct vnode **a_vpp;
17035fe58019SAttilio Rao struct componentname *a_cnp;
17045fe58019SAttilio Rao struct vattr *a_vap;
17055fe58019SAttilio Rao };
17065fe58019SAttilio Rao */
17075fe58019SAttilio Rao static int
fuse_vnop_mknod(struct vop_mknod_args * ap)17085fe58019SAttilio Rao fuse_vnop_mknod(struct vop_mknod_args *ap)
17095fe58019SAttilio Rao {
17105fe58019SAttilio Rao
1711bf4d7084SAlan Somers struct vnode *dvp = ap->a_dvp;
1712bf4d7084SAlan Somers struct vnode **vpp = ap->a_vpp;
1713bf4d7084SAlan Somers struct componentname *cnp = ap->a_cnp;
1714bf4d7084SAlan Somers struct vattr *vap = ap->a_vap;
17155fe58019SAttilio Rao
1716bf4d7084SAlan Somers if (fuse_isdeadfs(dvp))
1717bf4d7084SAlan Somers return ENXIO;
1718bf4d7084SAlan Somers
1719d5ff2688SAlan Somers return fuse_internal_mknod(dvp, vpp, cnp, vap);
1720bf4d7084SAlan Somers }
17215fe58019SAttilio Rao
17225fe58019SAttilio Rao /*
1723e76986fdSAlan Somers struct vop_open_args {
17245fe58019SAttilio Rao struct vnode *a_vp;
17255fe58019SAttilio Rao int a_mode;
17265fe58019SAttilio Rao struct ucred *a_cred;
17275fe58019SAttilio Rao struct thread *a_td;
17285fe58019SAttilio Rao int a_fdidx; / struct file *a_fp;
17295fe58019SAttilio Rao };
17305fe58019SAttilio Rao */
17315fe58019SAttilio Rao static int
fuse_vnop_open(struct vop_open_args * ap)17325fe58019SAttilio Rao fuse_vnop_open(struct vop_open_args *ap)
17335fe58019SAttilio Rao {
17345fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
17359e444871SAlan Somers int a_mode = ap->a_mode;
17365fe58019SAttilio Rao struct thread *td = ap->a_td;
17375fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
1738f8d4af10SAlan Somers pid_t pid = td->td_proc->p_pid;
17395fe58019SAttilio Rao
1740bf4d7084SAlan Somers if (fuse_isdeadfs(vp))
17415fe58019SAttilio Rao return ENXIO;
1742bf4d7084SAlan Somers if (vp->v_type == VCHR || vp->v_type == VBLK || vp->v_type == VFIFO)
1743bf4d7084SAlan Somers return (EOPNOTSUPP);
17449e444871SAlan Somers if ((a_mode & (FREAD | FWRITE | FEXEC)) == 0)
1745e7df9886SConrad Meyer return EINVAL;
1746e7df9886SConrad Meyer
17479e444871SAlan Somers if (fuse_filehandle_validrw(vp, a_mode, cred, pid)) {
17485fccbf31SAlan Somers fuse_vnode_open(vp, 0, td);
17495fe58019SAttilio Rao return 0;
17505fe58019SAttilio Rao }
17515fe58019SAttilio Rao
17529e444871SAlan Somers return fuse_filehandle_open(vp, a_mode, NULL, td, cred);
17535fe58019SAttilio Rao }
17545fe58019SAttilio Rao
1755746c92e0SJohn Baldwin static int
fuse_vnop_pathconf(struct vop_pathconf_args * ap)1756746c92e0SJohn Baldwin fuse_vnop_pathconf(struct vop_pathconf_args *ap)
1757746c92e0SJohn Baldwin {
175837df9d3bSAlan Somers struct vnode *vp = ap->a_vp;
175937df9d3bSAlan Somers struct mount *mp;
17606efba04dSAlan Somers struct fuse_filehandle *fufh;
17616efba04dSAlan Somers int err;
17626efba04dSAlan Somers bool closefufh = false;
1763746c92e0SJohn Baldwin
1764746c92e0SJohn Baldwin switch (ap->a_name) {
1765746c92e0SJohn Baldwin case _PC_FILESIZEBITS:
1766746c92e0SJohn Baldwin *ap->a_retval = 64;
1767746c92e0SJohn Baldwin return (0);
1768599afe53SJohn Baldwin case _PC_NAME_MAX:
1769599afe53SJohn Baldwin *ap->a_retval = NAME_MAX;
1770599afe53SJohn Baldwin return (0);
1771599afe53SJohn Baldwin case _PC_LINK_MAX:
1772f83f3d79SJohn Baldwin *ap->a_retval = MIN(LONG_MAX, FUSE_LINK_MAX);
1773599afe53SJohn Baldwin return (0);
1774746c92e0SJohn Baldwin case _PC_SYMLINK_MAX:
1775746c92e0SJohn Baldwin *ap->a_retval = MAXPATHLEN;
1776746c92e0SJohn Baldwin return (0);
1777746c92e0SJohn Baldwin case _PC_NO_TRUNC:
1778746c92e0SJohn Baldwin *ap->a_retval = 1;
1779746c92e0SJohn Baldwin return (0);
178037df9d3bSAlan Somers case _PC_MIN_HOLE_SIZE:
178137df9d3bSAlan Somers /*
178237df9d3bSAlan Somers * The FUSE protocol provides no mechanism for a server to
178337df9d3bSAlan Somers * report _PC_MIN_HOLE_SIZE. It's a protocol bug. Instead,
178437df9d3bSAlan Somers * return EINVAL if the server does not support FUSE_LSEEK, or
178537df9d3bSAlan Somers * 1 if it does.
178637df9d3bSAlan Somers */
178737df9d3bSAlan Somers mp = vnode_mount(vp);
178837df9d3bSAlan Somers if (!fsess_is_impl(mp, FUSE_LSEEK) &&
178937df9d3bSAlan Somers !fsess_not_impl(mp, FUSE_LSEEK)) {
179037df9d3bSAlan Somers off_t offset = 0;
179137df9d3bSAlan Somers
17926efba04dSAlan Somers /*
17936efba04dSAlan Somers * Issue a FUSE_LSEEK to find out if it's supported.
17946efba04dSAlan Somers * Use SEEK_DATA instead of SEEK_HOLE, because the
17956efba04dSAlan Somers * latter generally requires sequential scans of file
17966efba04dSAlan Somers * metadata, which can be slow.
17976efba04dSAlan Somers */
17986efba04dSAlan Somers err = fuse_vnop_do_lseek(vp, curthread,
17996efba04dSAlan Somers curthread->td_ucred, curthread->td_proc->p_pid,
18006efba04dSAlan Somers &offset, SEEK_DATA);
18016efba04dSAlan Somers if (err == EBADF) {
18026efba04dSAlan Somers /*
18036efba04dSAlan Somers * pathconf() doesn't necessarily open the
18046efba04dSAlan Somers * file. So we may need to do it here.
18056efba04dSAlan Somers */
18066efba04dSAlan Somers err = fuse_filehandle_open(vp, FREAD, &fufh,
18076efba04dSAlan Somers curthread, curthread->td_ucred);
18086efba04dSAlan Somers if (err == 0) {
18096efba04dSAlan Somers closefufh = true;
18106efba04dSAlan Somers err = fuse_vnop_do_lseek(vp, curthread,
18116efba04dSAlan Somers curthread->td_ucred,
18126efba04dSAlan Somers curthread->td_proc->p_pid, &offset,
18136efba04dSAlan Somers SEEK_DATA);
18146efba04dSAlan Somers }
18156efba04dSAlan Somers if (closefufh)
18166efba04dSAlan Somers fuse_filehandle_close(vp, fufh,
18176efba04dSAlan Somers curthread, curthread->td_ucred);
18186efba04dSAlan Somers }
18196efba04dSAlan Somers
182037df9d3bSAlan Somers }
182137df9d3bSAlan Somers
182237df9d3bSAlan Somers if (fsess_is_impl(mp, FUSE_LSEEK)) {
182337df9d3bSAlan Somers *ap->a_retval = 1;
182437df9d3bSAlan Somers return (0);
18256efba04dSAlan Somers } else if (fsess_not_impl(mp, FUSE_LSEEK)) {
18266efba04dSAlan Somers /* FUSE_LSEEK is not implemented */
182737df9d3bSAlan Somers return (EINVAL);
18286efba04dSAlan Somers } else {
18296efba04dSAlan Somers return (err);
183037df9d3bSAlan Somers }
1831746c92e0SJohn Baldwin default:
1832746c92e0SJohn Baldwin return (vop_stdpathconf(ap));
1833746c92e0SJohn Baldwin }
1834746c92e0SJohn Baldwin }
1835746c92e0SJohn Baldwin
1836dc433e15SAlan Somers SDT_PROBE_DEFINE3(fusefs, , vnops, filehandles_closed, "struct vnode*",
1837dc433e15SAlan Somers "struct uio*", "struct ucred*");
18385fe58019SAttilio Rao /*
18395fe58019SAttilio Rao struct vnop_read_args {
18405fe58019SAttilio Rao struct vnode *a_vp;
18415fe58019SAttilio Rao struct uio *a_uio;
18425fe58019SAttilio Rao int a_ioflag;
18435fe58019SAttilio Rao struct ucred *a_cred;
18445fe58019SAttilio Rao };
18455fe58019SAttilio Rao */
18465fe58019SAttilio Rao static int
fuse_vnop_read(struct vop_read_args * ap)18475fe58019SAttilio Rao fuse_vnop_read(struct vop_read_args *ap)
18485fe58019SAttilio Rao {
18495fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
18505fe58019SAttilio Rao struct uio *uio = ap->a_uio;
18515fe58019SAttilio Rao int ioflag = ap->a_ioflag;
18525fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
1853f8d4af10SAlan Somers pid_t pid = curthread->td_proc->p_pid;
1854dc433e15SAlan Somers struct fuse_filehandle *fufh;
1855dc433e15SAlan Somers int err;
1856dc433e15SAlan Somers bool closefufh = false, directio;
1857dc433e15SAlan Somers
1858dc433e15SAlan Somers MPASS(vp->v_type == VREG || vp->v_type == VDIR);
18595fe58019SAttilio Rao
18605fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
18615fe58019SAttilio Rao return ENXIO;
18625fe58019SAttilio Rao }
1863ead063e0SEdward Tomasz Napierala
1864ead063e0SEdward Tomasz Napierala if (VTOFUD(vp)->flag & FN_DIRECTIO) {
1865ead063e0SEdward Tomasz Napierala ioflag |= IO_DIRECT;
1866ead063e0SEdward Tomasz Napierala }
1867ead063e0SEdward Tomasz Napierala
1868dc433e15SAlan Somers err = fuse_filehandle_getrw(vp, FREAD, &fufh, cred, pid);
1869dc433e15SAlan Somers if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
1870dc433e15SAlan Somers /*
1871dc433e15SAlan Somers * nfsd will do I/O without first doing VOP_OPEN. We
1872dc433e15SAlan Somers * must implicitly open the file here
1873dc433e15SAlan Somers */
1874dc433e15SAlan Somers err = fuse_filehandle_open(vp, FREAD, &fufh, curthread, cred);
1875dc433e15SAlan Somers closefufh = true;
1876dc433e15SAlan Somers }
1877dc433e15SAlan Somers if (err) {
1878dc433e15SAlan Somers SDT_PROBE3(fusefs, , vnops, filehandles_closed, vp, uio, cred);
1879dc433e15SAlan Somers return err;
1880dc433e15SAlan Somers }
1881dc433e15SAlan Somers
1882dc433e15SAlan Somers /*
1883dc433e15SAlan Somers * Ideally, when the daemon asks for direct io at open time, the
1884dc433e15SAlan Somers * standard file flag should be set according to this, so that would
1885dc433e15SAlan Somers * just change the default mode, which later on could be changed via
1886dc433e15SAlan Somers * fcntl(2).
1887dc433e15SAlan Somers * But this doesn't work, the O_DIRECT flag gets cleared at some point
1888dc433e15SAlan Somers * (don't know where). So to make any use of the Fuse direct_io option,
1889dc433e15SAlan Somers * we hardwire it into the file's private data (similarly to Linux,
1890dc433e15SAlan Somers * btw.).
1891dc433e15SAlan Somers */
1892dc433e15SAlan Somers directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));
1893dc433e15SAlan Somers
1894dc433e15SAlan Somers fuse_vnode_update(vp, FN_ATIMECHANGE);
1895dc433e15SAlan Somers if (directio) {
1896dc433e15SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1, "direct read of vnode");
1897dc433e15SAlan Somers err = fuse_read_directbackend(vp, uio, cred, fufh);
1898dc433e15SAlan Somers } else {
1899dc433e15SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1, "buffered read of vnode");
1900dc433e15SAlan Somers err = fuse_read_biobackend(vp, uio, ioflag, cred, fufh, pid);
1901dc433e15SAlan Somers }
1902dc433e15SAlan Somers
1903dc433e15SAlan Somers if (closefufh)
1904dc433e15SAlan Somers fuse_filehandle_close(vp, fufh, curthread, cred);
1905dc433e15SAlan Somers
1906dc433e15SAlan Somers return (err);
19075fe58019SAttilio Rao }
19085fe58019SAttilio Rao
19095fe58019SAttilio Rao /*
19105fe58019SAttilio Rao struct vnop_readdir_args {
19115fe58019SAttilio Rao struct vnode *a_vp;
19125fe58019SAttilio Rao struct uio *a_uio;
19135fe58019SAttilio Rao struct ucred *a_cred;
19145fe58019SAttilio Rao int *a_eofflag;
1915e76986fdSAlan Somers int *a_ncookies;
1916b214fcceSAlan Somers uint64_t **a_cookies;
19175fe58019SAttilio Rao };
19185fe58019SAttilio Rao */
19195fe58019SAttilio Rao static int
fuse_vnop_readdir(struct vop_readdir_args * ap)19205fe58019SAttilio Rao fuse_vnop_readdir(struct vop_readdir_args *ap)
19215fe58019SAttilio Rao {
19225fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
19235fe58019SAttilio Rao struct uio *uio = ap->a_uio;
19245fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
19255fe58019SAttilio Rao struct fuse_filehandle *fufh = NULL;
192600134a07SAlan Somers struct mount *mp = vnode_mount(vp);
19275fe58019SAttilio Rao struct fuse_iov cookediov;
19285fe58019SAttilio Rao int err = 0;
1929b214fcceSAlan Somers uint64_t *cookies;
1930e76986fdSAlan Somers ssize_t tresid;
1931e76986fdSAlan Somers int ncookies;
1932e76986fdSAlan Somers bool closefufh = false;
1933f8d4af10SAlan Somers pid_t pid = curthread->td_proc->p_pid;
19345fe58019SAttilio Rao
1935e76986fdSAlan Somers if (ap->a_eofflag)
1936e76986fdSAlan Somers *ap->a_eofflag = 0;
19375fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
19385fe58019SAttilio Rao return ENXIO;
19395fe58019SAttilio Rao }
19405fe58019SAttilio Rao if ( /* XXXIP ((uio_iovcnt(uio) > 1)) || */
19415fe58019SAttilio Rao (uio_resid(uio) < sizeof(struct dirent))) {
19425fe58019SAttilio Rao return EINVAL;
19435fe58019SAttilio Rao }
19445fe58019SAttilio Rao
1945e76986fdSAlan Somers tresid = uio->uio_resid;
194635cf0e7eSAlan Somers err = fuse_filehandle_get_dir(vp, &fufh, cred, pid);
194700134a07SAlan Somers if (err == EBADF && mp->mnt_flag & MNT_EXPORTED) {
1948*f0f596bdSCismonX KASSERT(!fsess_is_impl(mp, FUSE_OPENDIR),
1949*f0f596bdSCismonX ("FUSE file systems that implement "
1950*f0f596bdSCismonX "FUSE_OPENDIR should not be exported"));
1951e76986fdSAlan Somers /*
1952e76986fdSAlan Somers * nfsd will do VOP_READDIR without first doing VOP_OPEN. We
195300134a07SAlan Somers * must implicitly open the directory here.
1954e76986fdSAlan Somers */
1955e76986fdSAlan Somers err = fuse_filehandle_open(vp, FREAD, &fufh, curthread, cred);
1956e76986fdSAlan Somers closefufh = true;
1957e76986fdSAlan Somers }
195835cf0e7eSAlan Somers if (err)
19595fe58019SAttilio Rao return (err);
1960e76986fdSAlan Somers if (ap->a_ncookies != NULL) {
1961e76986fdSAlan Somers ncookies = uio->uio_resid /
1962e76986fdSAlan Somers (offsetof(struct dirent, d_name) + 4) + 1;
1963e76986fdSAlan Somers cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
1964e76986fdSAlan Somers *ap->a_ncookies = ncookies;
1965e76986fdSAlan Somers *ap->a_cookies = cookies;
1966e76986fdSAlan Somers } else {
1967e76986fdSAlan Somers ncookies = 0;
1968e76986fdSAlan Somers cookies = NULL;
1969e76986fdSAlan Somers }
19705fe58019SAttilio Rao #define DIRCOOKEDSIZE FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + MAXNAMLEN + 1)
19715fe58019SAttilio Rao fiov_init(&cookediov, DIRCOOKEDSIZE);
19725fe58019SAttilio Rao
197300134a07SAlan Somers err = fuse_internal_readdir(vp, uio, fufh, &cookediov,
1974e76986fdSAlan Somers &ncookies, cookies);
19755fe58019SAttilio Rao
19765fe58019SAttilio Rao fiov_teardown(&cookediov);
1977e76986fdSAlan Somers if (closefufh)
1978e76986fdSAlan Somers fuse_filehandle_close(vp, fufh, curthread, cred);
1979e76986fdSAlan Somers
1980e76986fdSAlan Somers if (ap->a_ncookies != NULL) {
1981e76986fdSAlan Somers if (err == 0) {
1982e76986fdSAlan Somers *ap->a_ncookies -= ncookies;
1983e76986fdSAlan Somers } else {
1984e76986fdSAlan Somers free(*ap->a_cookies, M_TEMP);
1985e76986fdSAlan Somers *ap->a_ncookies = 0;
1986e76986fdSAlan Somers *ap->a_cookies = NULL;
1987e76986fdSAlan Somers }
1988e76986fdSAlan Somers }
1989e76986fdSAlan Somers if (err == 0 && tresid == uio->uio_resid)
1990e76986fdSAlan Somers *ap->a_eofflag = 1;
19915ec10aa5SAlan Somers
19925fe58019SAttilio Rao return err;
19935fe58019SAttilio Rao }
19945fe58019SAttilio Rao
19955fe58019SAttilio Rao /*
19965fe58019SAttilio Rao struct vnop_readlink_args {
19975fe58019SAttilio Rao struct vnode *a_vp;
19985fe58019SAttilio Rao struct uio *a_uio;
19995fe58019SAttilio Rao struct ucred *a_cred;
20005fe58019SAttilio Rao };
20015fe58019SAttilio Rao */
20025fe58019SAttilio Rao static int
fuse_vnop_readlink(struct vop_readlink_args * ap)20035fe58019SAttilio Rao fuse_vnop_readlink(struct vop_readlink_args *ap)
20045fe58019SAttilio Rao {
20055fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
20065fe58019SAttilio Rao struct uio *uio = ap->a_uio;
20075fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
20085fe58019SAttilio Rao
20095fe58019SAttilio Rao struct fuse_dispatcher fdi;
20105fe58019SAttilio Rao int err;
20115fe58019SAttilio Rao
20125fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
20135fe58019SAttilio Rao return ENXIO;
20145fe58019SAttilio Rao }
20155fe58019SAttilio Rao if (!vnode_islnk(vp)) {
20165fe58019SAttilio Rao return EINVAL;
20175fe58019SAttilio Rao }
20185fe58019SAttilio Rao fdisp_init(&fdi, 0);
20195fe58019SAttilio Rao err = fdisp_simple_putget_vp(&fdi, FUSE_READLINK, vp, curthread, cred);
20205fe58019SAttilio Rao if (err) {
20215fe58019SAttilio Rao goto out;
20225fe58019SAttilio Rao }
2023662ec2f7SAlan Somers if (strnlen(fdi.answ, fdi.iosize) + 1 < fdi.iosize) {
2024662ec2f7SAlan Somers struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
2025662ec2f7SAlan Somers fuse_warn(data, FSESS_WARN_READLINK_EMBEDDED_NUL,
2026662ec2f7SAlan Somers "Returned an embedded NUL from FUSE_READLINK.");
2027662ec2f7SAlan Somers err = EIO;
2028662ec2f7SAlan Somers goto out;
2029662ec2f7SAlan Somers }
20305fe58019SAttilio Rao if (((char *)fdi.answ)[0] == '/' &&
20315fe58019SAttilio Rao fuse_get_mpdata(vnode_mount(vp))->dataflags & FSESS_PUSH_SYMLINKS_IN) {
20325fe58019SAttilio Rao char *mpth = vnode_mount(vp)->mnt_stat.f_mntonname;
20335fe58019SAttilio Rao
20345fe58019SAttilio Rao err = uiomove(mpth, strlen(mpth), uio);
20355fe58019SAttilio Rao }
20365fe58019SAttilio Rao if (!err) {
20375fe58019SAttilio Rao err = uiomove(fdi.answ, fdi.iosize, uio);
20385fe58019SAttilio Rao }
20395fe58019SAttilio Rao out:
20405fe58019SAttilio Rao fdisp_destroy(&fdi);
20415fe58019SAttilio Rao return err;
20425fe58019SAttilio Rao }
20435fe58019SAttilio Rao
20445fe58019SAttilio Rao /*
20455fe58019SAttilio Rao struct vnop_reclaim_args {
20465fe58019SAttilio Rao struct vnode *a_vp;
20475fe58019SAttilio Rao };
20485fe58019SAttilio Rao */
20495fe58019SAttilio Rao static int
fuse_vnop_reclaim(struct vop_reclaim_args * ap)20505fe58019SAttilio Rao fuse_vnop_reclaim(struct vop_reclaim_args *ap)
20515fe58019SAttilio Rao {
20525fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
20538f226f4cSMateusz Guzik struct thread *td = curthread;
20545fe58019SAttilio Rao struct fuse_vnode_data *fvdat = VTOFUD(vp);
20555ec10aa5SAlan Somers struct fuse_filehandle *fufh, *fufh_tmp;
20565fe58019SAttilio Rao
20575fe58019SAttilio Rao if (!fvdat) {
20585fe58019SAttilio Rao panic("FUSE: no vnode data during recycling");
20595fe58019SAttilio Rao }
20605ec10aa5SAlan Somers LIST_FOREACH_SAFE(fufh, &fvdat->handles, next, fufh_tmp) {
20615ec10aa5SAlan Somers printf("FUSE: vnode being reclaimed with open fufh "
20629e444871SAlan Somers "(type=%#x)", fufh->fufh_type);
20635ec10aa5SAlan Somers fuse_filehandle_close(vp, fufh, td, NULL);
20645fe58019SAttilio Rao }
20655fe58019SAttilio Rao
206632273253SAlan Somers if (VTOI(vp) == 1) {
206732273253SAlan Somers /*
206832273253SAlan Somers * Don't send FUSE_FORGET for the root inode, because
206932273253SAlan Somers * we never send FUSE_LOOKUP for it (see
207032273253SAlan Somers * fuse_vfsop_root) and we don't want the server to see
207132273253SAlan Somers * mismatched lookup counts.
207232273253SAlan Somers */
207332273253SAlan Somers struct fuse_data *data;
207432273253SAlan Somers struct vnode *vroot;
207532273253SAlan Somers
207632273253SAlan Somers data = fuse_get_mpdata(vnode_mount(vp));
207732273253SAlan Somers FUSE_LOCK();
207832273253SAlan Somers vroot = data->vroot;
207932273253SAlan Somers data->vroot = NULL;
208032273253SAlan Somers FUSE_UNLOCK();
208132273253SAlan Somers if (vroot)
208232273253SAlan Somers vrele(vroot);
208332273253SAlan Somers } else if (!fuse_isdeadfs(vp) && fvdat->nlookup > 0) {
20845fe58019SAttilio Rao fuse_internal_forget_send(vnode_mount(vp), td, NULL, VTOI(vp),
20855fe58019SAttilio Rao fvdat->nlookup);
20865fe58019SAttilio Rao }
20875fe58019SAttilio Rao cache_purge(vp);
20885fe58019SAttilio Rao vfs_hash_remove(vp);
20895fe58019SAttilio Rao fuse_vnode_destroy(vp);
20905fe58019SAttilio Rao
20915fe58019SAttilio Rao return 0;
20925fe58019SAttilio Rao }
20935fe58019SAttilio Rao
20945fe58019SAttilio Rao /*
20955fe58019SAttilio Rao struct vnop_remove_args {
20965fe58019SAttilio Rao struct vnode *a_dvp;
20975fe58019SAttilio Rao struct vnode *a_vp;
20985fe58019SAttilio Rao struct componentname *a_cnp;
20995fe58019SAttilio Rao };
21005fe58019SAttilio Rao */
21015fe58019SAttilio Rao static int
fuse_vnop_remove(struct vop_remove_args * ap)21025fe58019SAttilio Rao fuse_vnop_remove(struct vop_remove_args *ap)
21035fe58019SAttilio Rao {
21045fe58019SAttilio Rao struct vnode *dvp = ap->a_dvp;
21055fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
21065fe58019SAttilio Rao struct componentname *cnp = ap->a_cnp;
21075fe58019SAttilio Rao
21085fe58019SAttilio Rao int err;
21095fe58019SAttilio Rao
21105fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
21115fe58019SAttilio Rao return ENXIO;
21125fe58019SAttilio Rao }
21135fe58019SAttilio Rao if (vnode_isdir(vp)) {
21145fe58019SAttilio Rao return EPERM;
21155fe58019SAttilio Rao }
21165fe58019SAttilio Rao
21175fe58019SAttilio Rao err = fuse_internal_remove(dvp, vp, cnp, FUSE_UNLINK);
21185fe58019SAttilio Rao
21195fe58019SAttilio Rao return err;
21205fe58019SAttilio Rao }
21215fe58019SAttilio Rao
21225fe58019SAttilio Rao /*
21235fe58019SAttilio Rao struct vnop_rename_args {
21245fe58019SAttilio Rao struct vnode *a_fdvp;
21255fe58019SAttilio Rao struct vnode *a_fvp;
21265fe58019SAttilio Rao struct componentname *a_fcnp;
21275fe58019SAttilio Rao struct vnode *a_tdvp;
21285fe58019SAttilio Rao struct vnode *a_tvp;
21295fe58019SAttilio Rao struct componentname *a_tcnp;
21305fe58019SAttilio Rao };
21315fe58019SAttilio Rao */
21325fe58019SAttilio Rao static int
fuse_vnop_rename(struct vop_rename_args * ap)21335fe58019SAttilio Rao fuse_vnop_rename(struct vop_rename_args *ap)
21345fe58019SAttilio Rao {
21355fe58019SAttilio Rao struct vnode *fdvp = ap->a_fdvp;
21365fe58019SAttilio Rao struct vnode *fvp = ap->a_fvp;
21375fe58019SAttilio Rao struct componentname *fcnp = ap->a_fcnp;
21385fe58019SAttilio Rao struct vnode *tdvp = ap->a_tdvp;
21395fe58019SAttilio Rao struct vnode *tvp = ap->a_tvp;
21405fe58019SAttilio Rao struct componentname *tcnp = ap->a_tcnp;
21415fe58019SAttilio Rao struct fuse_data *data;
21428e45ec4eSAlan Somers bool newparent = fdvp != tdvp;
21438e45ec4eSAlan Somers bool isdir = fvp->v_type == VDIR;
21445fe58019SAttilio Rao int err = 0;
21455fe58019SAttilio Rao
21465fe58019SAttilio Rao if (fuse_isdeadfs(fdvp)) {
21475fe58019SAttilio Rao return ENXIO;
21485fe58019SAttilio Rao }
21495fe58019SAttilio Rao if (fvp->v_mount != tdvp->v_mount ||
21505fe58019SAttilio Rao (tvp && fvp->v_mount != tvp->v_mount)) {
2151419e7ff6SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1, "cross-device rename");
21525fe58019SAttilio Rao err = EXDEV;
21535fe58019SAttilio Rao goto out;
21545fe58019SAttilio Rao }
21555fe58019SAttilio Rao cache_purge(fvp);
21565fe58019SAttilio Rao
21575fe58019SAttilio Rao /*
21585fe58019SAttilio Rao * FUSE library is expected to check if target directory is not
21595fe58019SAttilio Rao * under the source directory in the file system tree.
21605fe58019SAttilio Rao * Linux performs this check at VFS level.
21615fe58019SAttilio Rao */
21628e45ec4eSAlan Somers /*
21638e45ec4eSAlan Somers * If source is a directory, and it will get a new parent, user must
21648e45ec4eSAlan Somers * have write permission to it, so ".." can be modified.
21658e45ec4eSAlan Somers */
21665fe58019SAttilio Rao data = fuse_get_mpdata(vnode_mount(tdvp));
21678e45ec4eSAlan Somers if (data->dataflags & FSESS_DEFAULT_PERMISSIONS && isdir && newparent) {
21688e45ec4eSAlan Somers err = fuse_internal_access(fvp, VWRITE,
2169b4a58fbfSMateusz Guzik curthread, tcnp->cn_cred);
21708e45ec4eSAlan Somers if (err)
21718e45ec4eSAlan Somers goto out;
21728e45ec4eSAlan Somers }
21735fe58019SAttilio Rao sx_xlock(&data->rename_lock);
21745fe58019SAttilio Rao err = fuse_internal_rename(fdvp, fcnp, tdvp, tcnp);
21755fe58019SAttilio Rao if (err == 0) {
2176d9454fabSAttilio Rao if (tdvp != fdvp)
21775fe58019SAttilio Rao fuse_vnode_setparent(fvp, tdvp);
21785fe58019SAttilio Rao if (tvp != NULL)
21795fe58019SAttilio Rao fuse_vnode_setparent(tvp, NULL);
21805fe58019SAttilio Rao }
21815fe58019SAttilio Rao sx_unlock(&data->rename_lock);
21825fe58019SAttilio Rao
21835fe58019SAttilio Rao if (tvp != NULL && tvp != fvp) {
21845fe58019SAttilio Rao cache_purge(tvp);
21855fe58019SAttilio Rao }
21865fe58019SAttilio Rao if (vnode_isdir(fvp)) {
2187e8553be9SAlan Somers if (((tvp != NULL) && vnode_isdir(tvp)) || vnode_isdir(fvp)) {
21885fe58019SAttilio Rao cache_purge(tdvp);
21895fe58019SAttilio Rao }
21905fe58019SAttilio Rao cache_purge(fdvp);
21915fe58019SAttilio Rao }
21925fe58019SAttilio Rao out:
21935fe58019SAttilio Rao if (tdvp == tvp) {
21945fe58019SAttilio Rao vrele(tdvp);
21955fe58019SAttilio Rao } else {
21965fe58019SAttilio Rao vput(tdvp);
21975fe58019SAttilio Rao }
21985fe58019SAttilio Rao if (tvp != NULL) {
21995fe58019SAttilio Rao vput(tvp);
22005fe58019SAttilio Rao }
22015fe58019SAttilio Rao vrele(fdvp);
22025fe58019SAttilio Rao vrele(fvp);
22035fe58019SAttilio Rao
22045fe58019SAttilio Rao return err;
22055fe58019SAttilio Rao }
22065fe58019SAttilio Rao
22075fe58019SAttilio Rao /*
22085fe58019SAttilio Rao struct vnop_rmdir_args {
22095fe58019SAttilio Rao struct vnode *a_dvp;
22105fe58019SAttilio Rao struct vnode *a_vp;
22115fe58019SAttilio Rao struct componentname *a_cnp;
22125fe58019SAttilio Rao } *ap;
22135fe58019SAttilio Rao */
22145fe58019SAttilio Rao static int
fuse_vnop_rmdir(struct vop_rmdir_args * ap)22155fe58019SAttilio Rao fuse_vnop_rmdir(struct vop_rmdir_args *ap)
22165fe58019SAttilio Rao {
22175fe58019SAttilio Rao struct vnode *dvp = ap->a_dvp;
22185fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
22195fe58019SAttilio Rao
22205fe58019SAttilio Rao int err;
22215fe58019SAttilio Rao
22225fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
22235fe58019SAttilio Rao return ENXIO;
22245fe58019SAttilio Rao }
22255fe58019SAttilio Rao if (VTOFUD(vp) == VTOFUD(dvp)) {
22265fe58019SAttilio Rao return EINVAL;
22275fe58019SAttilio Rao }
22285fe58019SAttilio Rao err = fuse_internal_remove(dvp, vp, ap->a_cnp, FUSE_RMDIR);
22295fe58019SAttilio Rao
22305fe58019SAttilio Rao return err;
22315fe58019SAttilio Rao }
22325fe58019SAttilio Rao
22335fe58019SAttilio Rao /*
22345fe58019SAttilio Rao struct vnop_setattr_args {
22355fe58019SAttilio Rao struct vnode *a_vp;
22365fe58019SAttilio Rao struct vattr *a_vap;
22375fe58019SAttilio Rao struct ucred *a_cred;
22385fe58019SAttilio Rao struct thread *a_td;
22395fe58019SAttilio Rao };
22405fe58019SAttilio Rao */
22415fe58019SAttilio Rao static int
fuse_vnop_setattr(struct vop_setattr_args * ap)22425fe58019SAttilio Rao fuse_vnop_setattr(struct vop_setattr_args *ap)
22435fe58019SAttilio Rao {
22445fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
22455fe58019SAttilio Rao struct vattr *vap = ap->a_vap;
22465fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
22475fe58019SAttilio Rao struct thread *td = curthread;
2248474ba6faSAlan Somers struct mount *mp;
2249474ba6faSAlan Somers struct fuse_data *data;
22504e83d655SAlan Somers struct vattr old_va;
2251474ba6faSAlan Somers int dataflags;
22524e83d655SAlan Somers int err = 0, err2;
2253ff4fbdf5SAlan Somers accmode_t accmode = 0;
2254a90e32deSAlan Somers bool checkperm;
2255a2bdd737SAlan Somers bool drop_suid = false;
22565fe58019SAttilio Rao
2257474ba6faSAlan Somers mp = vnode_mount(vp);
2258474ba6faSAlan Somers data = fuse_get_mpdata(mp);
2259474ba6faSAlan Somers dataflags = data->dataflags;
2260a90e32deSAlan Somers checkperm = dataflags & FSESS_DEFAULT_PERMISSIONS;
2261474ba6faSAlan Somers
22625fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
22635fe58019SAttilio Rao return ENXIO;
22645fe58019SAttilio Rao }
22655fe58019SAttilio Rao
22665fe58019SAttilio Rao if (vap->va_uid != (uid_t)VNOVAL) {
2267a90e32deSAlan Somers if (checkperm) {
2268474ba6faSAlan Somers /* Only root may change a file's owner */
2269474ba6faSAlan Somers err = priv_check_cred(cred, PRIV_VFS_CHOWN);
22704e83d655SAlan Somers if (err) {
22714e83d655SAlan Somers /* As a special case, allow the null chown */
22724e83d655SAlan Somers err2 = fuse_internal_getattr(vp, &old_va, cred,
22734e83d655SAlan Somers td);
22744e83d655SAlan Somers if (err2)
22754e83d655SAlan Somers return (err2);
22764e83d655SAlan Somers if (vap->va_uid != old_va.va_uid)
22774e83d655SAlan Somers return err;
2278a2bdd737SAlan Somers drop_suid = true;
227931223e68SPawel Jakub Dawidek }
2280ff4fc43aSPawel Jakub Dawidek }
2281ff4fbdf5SAlan Somers accmode |= VADMIN;
22825fe58019SAttilio Rao }
22835fe58019SAttilio Rao if (vap->va_gid != (gid_t)VNOVAL) {
2284a2bdd737SAlan Somers if (checkperm && priv_check_cred(cred, PRIV_VFS_CHOWN))
2285a2bdd737SAlan Somers drop_suid = true;
228631223e68SPawel Jakub Dawidek if (checkperm && !groupmember(vap->va_gid, cred)) {
2287474ba6faSAlan Somers /*
2288474ba6faSAlan Somers * Non-root users may only chgrp to one of their own
2289474ba6faSAlan Somers * groups
2290474ba6faSAlan Somers */
2291474ba6faSAlan Somers err = priv_check_cred(cred, PRIV_VFS_CHOWN);
22924e83d655SAlan Somers if (err) {
22934e83d655SAlan Somers /* As a special case, allow the null chgrp */
22944e83d655SAlan Somers err2 = fuse_internal_getattr(vp, &old_va, cred,
22954e83d655SAlan Somers td);
22964e83d655SAlan Somers if (err2)
22974e83d655SAlan Somers return (err2);
22984e83d655SAlan Somers if (vap->va_gid != old_va.va_gid)
2299a90e32deSAlan Somers return err;
230031223e68SPawel Jakub Dawidek }
2301ff4fc43aSPawel Jakub Dawidek }
2302ff4fbdf5SAlan Somers accmode |= VADMIN;
23035fe58019SAttilio Rao }
23045fe58019SAttilio Rao if (vap->va_size != VNOVAL) {
2305a90e32deSAlan Somers switch (vp->v_type) {
2306a90e32deSAlan Somers case VDIR:
2307a90e32deSAlan Somers return (EISDIR);
2308a90e32deSAlan Somers case VLNK:
2309a90e32deSAlan Somers case VREG:
2310a90e32deSAlan Somers if (vfs_isrdonly(mp))
2311a90e32deSAlan Somers return (EROFS);
23120a192b3aSAlan Somers err = vn_rlimit_trunc(vap->va_size, td);
23130a192b3aSAlan Somers if (err)
23140a192b3aSAlan Somers return (err);
2315a90e32deSAlan Somers break;
2316a90e32deSAlan Somers default:
2317a90e32deSAlan Somers /*
2318a90e32deSAlan Somers * According to POSIX, the result is unspecified
2319a90e32deSAlan Somers * for file types other than regular files,
2320a90e32deSAlan Somers * directories and shared memory objects. We
2321a90e32deSAlan Somers * don't support shared memory objects in the file
2322a90e32deSAlan Somers * system, and have dubious support for truncating
2323a90e32deSAlan Somers * symlinks. Just ignore the request in other cases.
2324a90e32deSAlan Somers */
2325a90e32deSAlan Somers return (0);
2326a90e32deSAlan Somers }
23273fa12789SAlan Somers /* Don't set accmode. Permission to trunc is checked upstack */
23285fe58019SAttilio Rao }
2329d943c93eSAlan Somers if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
2330d943c93eSAlan Somers if (vap->va_vaflags & VA_UTIMES_NULL)
2331d943c93eSAlan Somers accmode |= VWRITE;
2332d943c93eSAlan Somers else
2333ff4fbdf5SAlan Somers accmode |= VADMIN;
2334d943c93eSAlan Somers }
2335a2bdd737SAlan Somers if (drop_suid) {
2336a2bdd737SAlan Somers if (vap->va_mode != (mode_t)VNOVAL)
2337a2bdd737SAlan Somers vap->va_mode &= ~(S_ISUID | S_ISGID);
2338a2bdd737SAlan Somers else {
2339a2bdd737SAlan Somers err = fuse_internal_getattr(vp, &old_va, cred, td);
2340a2bdd737SAlan Somers if (err)
2341a2bdd737SAlan Somers return (err);
2342a2bdd737SAlan Somers vap->va_mode = old_va.va_mode & ~(S_ISUID | S_ISGID);
2343a2bdd737SAlan Somers }
2344a2bdd737SAlan Somers }
23455fe58019SAttilio Rao if (vap->va_mode != (mode_t)VNOVAL) {
2346e5ff3a7eSAlan Somers /* Only root may set the sticky bit on non-directories */
2347a90e32deSAlan Somers if (checkperm && vp->v_type != VDIR && (vap->va_mode & S_ISTXT)
2348a90e32deSAlan Somers && priv_check_cred(cred, PRIV_VFS_STICKYFILE))
2349a90e32deSAlan Somers return EFTYPE;
23508cfb4431SAlan Somers if (checkperm && (vap->va_mode & S_ISGID)) {
23518cfb4431SAlan Somers err = fuse_internal_getattr(vp, &old_va, cred, td);
23528cfb4431SAlan Somers if (err)
23538cfb4431SAlan Somers return (err);
23548cfb4431SAlan Somers if (!groupmember(old_va.va_gid, cred)) {
23558cfb4431SAlan Somers err = priv_check_cred(cred, PRIV_VFS_SETGID);
23568cfb4431SAlan Somers if (err)
23578cfb4431SAlan Somers return (err);
23588cfb4431SAlan Somers }
23598cfb4431SAlan Somers }
2360ff4fbdf5SAlan Somers accmode |= VADMIN;
23615fe58019SAttilio Rao }
23625fe58019SAttilio Rao
2363a90e32deSAlan Somers if (vfs_isrdonly(mp))
2364a90e32deSAlan Somers return EROFS;
2365a90e32deSAlan Somers
2366bfcb817bSAlan Somers if (checkperm) {
2367666f8543SAlan Somers err = fuse_internal_access(vp, accmode, td, cred);
2368bfcb817bSAlan Somers } else {
2369bfcb817bSAlan Somers err = 0;
2370bfcb817bSAlan Somers }
2371ff4fbdf5SAlan Somers if (err)
23725fe58019SAttilio Rao return err;
2373a90e32deSAlan Somers else
2374a90e32deSAlan Somers return fuse_internal_setattr(vp, vap, td, cred);
23755fe58019SAttilio Rao }
23765fe58019SAttilio Rao
23775fe58019SAttilio Rao /*
23785fe58019SAttilio Rao struct vnop_strategy_args {
23795fe58019SAttilio Rao struct vnode *a_vp;
23805fe58019SAttilio Rao struct buf *a_bp;
23815fe58019SAttilio Rao };
23825fe58019SAttilio Rao */
23835fe58019SAttilio Rao static int
fuse_vnop_strategy(struct vop_strategy_args * ap)23845fe58019SAttilio Rao fuse_vnop_strategy(struct vop_strategy_args *ap)
23855fe58019SAttilio Rao {
23865fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
23875fe58019SAttilio Rao struct buf *bp = ap->a_bp;
23885fe58019SAttilio Rao
23895fe58019SAttilio Rao if (!vp || fuse_isdeadfs(vp)) {
23905fe58019SAttilio Rao bp->b_ioflags |= BIO_ERROR;
23915fe58019SAttilio Rao bp->b_error = ENXIO;
23925fe58019SAttilio Rao bufdone(bp);
239398852a32SAlan Somers return 0;
23945fe58019SAttilio Rao }
23955fe58019SAttilio Rao
23965fe58019SAttilio Rao /*
2397f203d173SAlan Somers * VOP_STRATEGY always returns zero and signals error via bp->b_ioflags.
2398f203d173SAlan Somers * fuse_io_strategy sets bp's error fields
23995fe58019SAttilio Rao */
2400f203d173SAlan Somers (void)fuse_io_strategy(vp, bp);
2401f203d173SAlan Somers
24025fe58019SAttilio Rao return 0;
24035fe58019SAttilio Rao }
24045fe58019SAttilio Rao
24055fe58019SAttilio Rao /*
24065fe58019SAttilio Rao struct vnop_symlink_args {
24075fe58019SAttilio Rao struct vnode *a_dvp;
24085fe58019SAttilio Rao struct vnode **a_vpp;
24095fe58019SAttilio Rao struct componentname *a_cnp;
24105fe58019SAttilio Rao struct vattr *a_vap;
24115fe58019SAttilio Rao char *a_target;
24125fe58019SAttilio Rao };
24135fe58019SAttilio Rao */
24145fe58019SAttilio Rao static int
fuse_vnop_symlink(struct vop_symlink_args * ap)24155fe58019SAttilio Rao fuse_vnop_symlink(struct vop_symlink_args *ap)
24165fe58019SAttilio Rao {
24175fe58019SAttilio Rao struct vnode *dvp = ap->a_dvp;
24185fe58019SAttilio Rao struct vnode **vpp = ap->a_vpp;
24195fe58019SAttilio Rao struct componentname *cnp = ap->a_cnp;
24201493c2eeSBrooks Davis const char *target = ap->a_target;
24215fe58019SAttilio Rao
24225fe58019SAttilio Rao struct fuse_dispatcher fdi;
24235fe58019SAttilio Rao
24245fe58019SAttilio Rao int err;
24255fe58019SAttilio Rao size_t len;
24265fe58019SAttilio Rao
24275fe58019SAttilio Rao if (fuse_isdeadfs(dvp)) {
24285fe58019SAttilio Rao return ENXIO;
24295fe58019SAttilio Rao }
24305fe58019SAttilio Rao /*
24315fe58019SAttilio Rao * Unlike the other creator type calls, here we have to create a message
24325fe58019SAttilio Rao * where the name of the new entry comes first, and the data describing
24335fe58019SAttilio Rao * the entry comes second.
24345fe58019SAttilio Rao * Hence we can't rely on our handy fuse_internal_newentry() routine,
24355fe58019SAttilio Rao * but put together the message manually and just call the core part.
24365fe58019SAttilio Rao */
24375fe58019SAttilio Rao
24385fe58019SAttilio Rao len = strlen(target) + 1;
24395fe58019SAttilio Rao fdisp_init(&fdi, len + cnp->cn_namelen + 1);
24405fe58019SAttilio Rao fdisp_make_vp(&fdi, FUSE_SYMLINK, dvp, curthread, NULL);
24415fe58019SAttilio Rao
24425fe58019SAttilio Rao memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
24435fe58019SAttilio Rao ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
24445fe58019SAttilio Rao memcpy((char *)fdi.indata + cnp->cn_namelen + 1, target, len);
24455fe58019SAttilio Rao
24465fe58019SAttilio Rao err = fuse_internal_newentry_core(dvp, vpp, cnp, VLNK, &fdi);
24475fe58019SAttilio Rao fdisp_destroy(&fdi);
24485fe58019SAttilio Rao return err;
24495fe58019SAttilio Rao }
24505fe58019SAttilio Rao
24515fe58019SAttilio Rao /*
24525fe58019SAttilio Rao struct vnop_write_args {
24535fe58019SAttilio Rao struct vnode *a_vp;
24545fe58019SAttilio Rao struct uio *a_uio;
24555fe58019SAttilio Rao int a_ioflag;
24565fe58019SAttilio Rao struct ucred *a_cred;
24575fe58019SAttilio Rao };
24585fe58019SAttilio Rao */
24595fe58019SAttilio Rao static int
fuse_vnop_write(struct vop_write_args * ap)24605fe58019SAttilio Rao fuse_vnop_write(struct vop_write_args *ap)
24615fe58019SAttilio Rao {
24625fe58019SAttilio Rao struct vnode *vp = ap->a_vp;
24635fe58019SAttilio Rao struct uio *uio = ap->a_uio;
24645fe58019SAttilio Rao int ioflag = ap->a_ioflag;
24655fe58019SAttilio Rao struct ucred *cred = ap->a_cred;
2466f8d4af10SAlan Somers pid_t pid = curthread->td_proc->p_pid;
2467dc433e15SAlan Somers struct fuse_filehandle *fufh;
2468dc433e15SAlan Somers int err;
2469dc433e15SAlan Somers bool closefufh = false, directio;
2470dc433e15SAlan Somers
2471dc433e15SAlan Somers MPASS(vp->v_type == VREG || vp->v_type == VDIR);
24725fe58019SAttilio Rao
24735fe58019SAttilio Rao if (fuse_isdeadfs(vp)) {
24745fe58019SAttilio Rao return ENXIO;
24755fe58019SAttilio Rao }
24765fe58019SAttilio Rao
2477ead063e0SEdward Tomasz Napierala if (VTOFUD(vp)->flag & FN_DIRECTIO) {
2478ead063e0SEdward Tomasz Napierala ioflag |= IO_DIRECT;
2479ead063e0SEdward Tomasz Napierala }
2480ead063e0SEdward Tomasz Napierala
2481dc433e15SAlan Somers err = fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
2482dc433e15SAlan Somers if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
2483dc433e15SAlan Somers /*
2484dc433e15SAlan Somers * nfsd will do I/O without first doing VOP_OPEN. We
2485dc433e15SAlan Somers * must implicitly open the file here
2486dc433e15SAlan Somers */
2487dc433e15SAlan Somers err = fuse_filehandle_open(vp, FWRITE, &fufh, curthread, cred);
2488dc433e15SAlan Somers closefufh = true;
2489dc433e15SAlan Somers }
2490dc433e15SAlan Somers if (err) {
2491dc433e15SAlan Somers SDT_PROBE3(fusefs, , vnops, filehandles_closed, vp, uio, cred);
2492dc433e15SAlan Somers return err;
2493dc433e15SAlan Somers }
2494dc433e15SAlan Somers
2495dc433e15SAlan Somers /*
2496dc433e15SAlan Somers * Ideally, when the daemon asks for direct io at open time, the
2497dc433e15SAlan Somers * standard file flag should be set according to this, so that would
2498dc433e15SAlan Somers * just change the default mode, which later on could be changed via
2499dc433e15SAlan Somers * fcntl(2).
2500dc433e15SAlan Somers * But this doesn't work, the O_DIRECT flag gets cleared at some point
2501dc433e15SAlan Somers * (don't know where). So to make any use of the Fuse direct_io option,
2502dc433e15SAlan Somers * we hardwire it into the file's private data (similarly to Linux,
2503dc433e15SAlan Somers * btw.).
2504dc433e15SAlan Somers */
2505dc433e15SAlan Somers directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));
2506dc433e15SAlan Somers
2507dc433e15SAlan Somers fuse_vnode_update(vp, FN_MTIMECHANGE | FN_CTIMECHANGE);
2508dc433e15SAlan Somers if (directio) {
2509dc433e15SAlan Somers off_t start, end, filesize;
2510dc433e15SAlan Somers bool pages = (ioflag & IO_VMIO) != 0;
2511dc433e15SAlan Somers
2512dc433e15SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1, "direct write of vnode");
2513dc433e15SAlan Somers
2514dc433e15SAlan Somers err = fuse_vnode_size(vp, &filesize, cred, curthread);
2515dc433e15SAlan Somers if (err)
2516dc433e15SAlan Somers goto out;
2517dc433e15SAlan Somers
2518dc433e15SAlan Somers start = uio->uio_offset;
2519dc433e15SAlan Somers end = start + uio->uio_resid;
2520dc433e15SAlan Somers if (!pages) {
2521dc433e15SAlan Somers err = fuse_inval_buf_range(vp, filesize, start,
2522dc433e15SAlan Somers end);
2523dc433e15SAlan Somers if (err)
2524dc433e15SAlan Somers goto out;
2525dc433e15SAlan Somers }
2526dc433e15SAlan Somers err = fuse_write_directbackend(vp, uio, cred, fufh,
2527dc433e15SAlan Somers filesize, ioflag, pages);
2528dc433e15SAlan Somers } else {
2529dc433e15SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1,
2530dc433e15SAlan Somers "buffered write of vnode");
2531dc433e15SAlan Somers if (!fsess_opt_writeback(vnode_mount(vp)))
2532dc433e15SAlan Somers ioflag |= IO_SYNC;
2533dc433e15SAlan Somers err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag, pid);
2534dc433e15SAlan Somers }
2535dc433e15SAlan Somers fuse_internal_clear_suid_on_write(vp, cred, uio->uio_td);
2536dc433e15SAlan Somers
2537dc433e15SAlan Somers out:
2538dc433e15SAlan Somers if (closefufh)
2539dc433e15SAlan Somers fuse_filehandle_close(vp, fufh, curthread, cred);
2540dc433e15SAlan Somers
2541dc433e15SAlan Somers return (err);
25425fe58019SAttilio Rao }
25435fe58019SAttilio Rao
2544b9e20197SAlan Somers static daddr_t
fuse_gbp_getblkno(struct vnode * vp,vm_ooffset_t off)2545b9e20197SAlan Somers fuse_gbp_getblkno(struct vnode *vp, vm_ooffset_t off)
2546b9e20197SAlan Somers {
2547b9e20197SAlan Somers const int biosize = fuse_iosize(vp);
2548b9e20197SAlan Somers
2549b9e20197SAlan Somers return (off / biosize);
2550b9e20197SAlan Somers }
2551b9e20197SAlan Somers
2552b9e20197SAlan Somers static int
fuse_gbp_getblksz(struct vnode * vp,daddr_t lbn,long * blksz)25534f917847SAlan Somers fuse_gbp_getblksz(struct vnode *vp, daddr_t lbn, long *blksz)
2554b9e20197SAlan Somers {
2555b9e20197SAlan Somers off_t filesize;
25564f917847SAlan Somers int err;
2557b9e20197SAlan Somers const int biosize = fuse_iosize(vp);
2558b9e20197SAlan Somers
2559b9e20197SAlan Somers err = fuse_vnode_size(vp, &filesize, NULL, NULL);
25604f917847SAlan Somers if (err) {
25614f917847SAlan Somers /* This will turn into a SIGBUS */
25624f917847SAlan Somers return (EIO);
25634f917847SAlan Somers } else if ((off_t)lbn * biosize >= filesize) {
25644f917847SAlan Somers *blksz = 0;
2565b9e20197SAlan Somers } else if ((off_t)(lbn + 1) * biosize > filesize) {
25664f917847SAlan Somers *blksz = filesize - (off_t)lbn *biosize;
2567b9e20197SAlan Somers } else {
25684f917847SAlan Somers *blksz = biosize;
2569b9e20197SAlan Somers }
2570197a4f29SKonstantin Belousov return (0);
2571b9e20197SAlan Somers }
2572b9e20197SAlan Somers
25735fe58019SAttilio Rao /*
25745fe58019SAttilio Rao struct vnop_getpages_args {
25755fe58019SAttilio Rao struct vnode *a_vp;
25765fe58019SAttilio Rao vm_page_t *a_m;
25775fe58019SAttilio Rao int a_count;
25785fe58019SAttilio Rao int a_reqpage;
25795fe58019SAttilio Rao };
25805fe58019SAttilio Rao */
25815fe58019SAttilio Rao static int
fuse_vnop_getpages(struct vop_getpages_args * ap)25825fe58019SAttilio Rao fuse_vnop_getpages(struct vop_getpages_args *ap)
25835fe58019SAttilio Rao {
2584b9e20197SAlan Somers struct vnode *vp = ap->a_vp;
25855fe58019SAttilio Rao
25865fe58019SAttilio Rao if (!fsess_opt_mmap(vnode_mount(vp))) {
2587419e7ff6SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1,
2588cf169498SAlan Somers "called on non-cacheable vnode??\n");
25895fe58019SAttilio Rao return (VM_PAGER_ERROR);
25905fe58019SAttilio Rao }
25915fe58019SAttilio Rao
2592b9e20197SAlan Somers return (vfs_bio_getpages(vp, ap->a_m, ap->a_count, ap->a_rbehind,
2593b9e20197SAlan Somers ap->a_rahead, fuse_gbp_getblkno, fuse_gbp_getblksz));
25945fe58019SAttilio Rao }
25955fe58019SAttilio Rao
259604660064SFedor Uporov static const char extattr_namespace_separator = '.';
259704660064SFedor Uporov
259804660064SFedor Uporov /*
259904660064SFedor Uporov struct vop_getextattr_args {
260004660064SFedor Uporov struct vop_generic_args a_gen;
260104660064SFedor Uporov struct vnode *a_vp;
260204660064SFedor Uporov int a_attrnamespace;
260304660064SFedor Uporov const char *a_name;
260404660064SFedor Uporov struct uio *a_uio;
260504660064SFedor Uporov size_t *a_size;
260604660064SFedor Uporov struct ucred *a_cred;
260704660064SFedor Uporov struct thread *a_td;
260804660064SFedor Uporov };
260904660064SFedor Uporov */
261004660064SFedor Uporov static int
fuse_vnop_getextattr(struct vop_getextattr_args * ap)261104660064SFedor Uporov fuse_vnop_getextattr(struct vop_getextattr_args *ap)
261204660064SFedor Uporov {
261304660064SFedor Uporov struct vnode *vp = ap->a_vp;
261404660064SFedor Uporov struct uio *uio = ap->a_uio;
261528f4f623SFedor Uporov struct fuse_dispatcher fdi;
261604660064SFedor Uporov struct fuse_getxattr_in *get_xattr_in;
261704660064SFedor Uporov struct fuse_getxattr_out *get_xattr_out;
261804660064SFedor Uporov struct mount *mp = vnode_mount(vp);
261904660064SFedor Uporov struct thread *td = ap->a_td;
262004660064SFedor Uporov struct ucred *cred = ap->a_cred;
262128f4f623SFedor Uporov char *prefix;
262228f4f623SFedor Uporov char *attr_str;
262328f4f623SFedor Uporov size_t len;
262428f4f623SFedor Uporov int err;
262504660064SFedor Uporov
262604660064SFedor Uporov if (fuse_isdeadfs(vp))
262728f4f623SFedor Uporov return (ENXIO);
262804660064SFedor Uporov
262937df9d3bSAlan Somers if (fsess_not_impl(mp, FUSE_GETXATTR))
26301f4a83f9SAlan Somers return EOPNOTSUPP;
26311f4a83f9SAlan Somers
2632666f8543SAlan Somers err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
2633ff4fbdf5SAlan Somers if (err)
2634ff4fbdf5SAlan Somers return err;
2635ff4fbdf5SAlan Somers
263604660064SFedor Uporov /* Default to looking for user attributes. */
263704660064SFedor Uporov if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
263804660064SFedor Uporov prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
263904660064SFedor Uporov else
264004660064SFedor Uporov prefix = EXTATTR_NAMESPACE_USER_STRING;
264104660064SFedor Uporov
264204660064SFedor Uporov len = strlen(prefix) + sizeof(extattr_namespace_separator) +
264304660064SFedor Uporov strlen(ap->a_name) + 1;
264404660064SFedor Uporov
264504660064SFedor Uporov fdisp_init(&fdi, len + sizeof(*get_xattr_in));
264604660064SFedor Uporov fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, td, cred);
264704660064SFedor Uporov
264804660064SFedor Uporov get_xattr_in = fdi.indata;
264904660064SFedor Uporov /*
265004660064SFedor Uporov * Check to see whether we're querying the available size or
265104660064SFedor Uporov * issuing the actual request. If we pass in 0, we get back struct
265204660064SFedor Uporov * fuse_getxattr_out. If we pass in a non-zero size, we get back
265304660064SFedor Uporov * that much data, without the struct fuse_getxattr_out header.
265404660064SFedor Uporov */
2655493b4a8cSFedor Uporov if (uio == NULL)
265604660064SFedor Uporov get_xattr_in->size = 0;
265704660064SFedor Uporov else
265804660064SFedor Uporov get_xattr_in->size = uio->uio_resid;
265904660064SFedor Uporov
266004660064SFedor Uporov attr_str = (char *)fdi.indata + sizeof(*get_xattr_in);
266104660064SFedor Uporov snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
266204660064SFedor Uporov ap->a_name);
266304660064SFedor Uporov
266404660064SFedor Uporov err = fdisp_wait_answ(&fdi);
266504660064SFedor Uporov if (err != 0) {
26661f4a83f9SAlan Somers if (err == ENOSYS) {
266704660064SFedor Uporov fsess_set_notimpl(mp, FUSE_GETXATTR);
26681f4a83f9SAlan Somers err = EOPNOTSUPP;
26691f4a83f9SAlan Somers }
267004660064SFedor Uporov goto out;
267104660064SFedor Uporov }
267204660064SFedor Uporov
267304660064SFedor Uporov get_xattr_out = fdi.answ;
267404660064SFedor Uporov
2675493b4a8cSFedor Uporov if (ap->a_size != NULL)
267604660064SFedor Uporov *ap->a_size = get_xattr_out->size;
2677493b4a8cSFedor Uporov
2678493b4a8cSFedor Uporov if (uio != NULL)
267904660064SFedor Uporov err = uiomove(fdi.answ, fdi.iosize, uio);
268004660064SFedor Uporov
268104660064SFedor Uporov out:
268204660064SFedor Uporov fdisp_destroy(&fdi);
268304660064SFedor Uporov return (err);
268404660064SFedor Uporov }
268504660064SFedor Uporov
268604660064SFedor Uporov /*
268704660064SFedor Uporov struct vop_setextattr_args {
268804660064SFedor Uporov struct vop_generic_args a_gen;
268904660064SFedor Uporov struct vnode *a_vp;
269004660064SFedor Uporov int a_attrnamespace;
269104660064SFedor Uporov const char *a_name;
269204660064SFedor Uporov struct uio *a_uio;
269304660064SFedor Uporov struct ucred *a_cred;
269404660064SFedor Uporov struct thread *a_td;
269504660064SFedor Uporov };
269604660064SFedor Uporov */
269704660064SFedor Uporov static int
fuse_vnop_setextattr(struct vop_setextattr_args * ap)269804660064SFedor Uporov fuse_vnop_setextattr(struct vop_setextattr_args *ap)
269904660064SFedor Uporov {
270004660064SFedor Uporov struct vnode *vp = ap->a_vp;
270104660064SFedor Uporov struct uio *uio = ap->a_uio;
270228f4f623SFedor Uporov struct fuse_dispatcher fdi;
270304660064SFedor Uporov struct fuse_setxattr_in *set_xattr_in;
270404660064SFedor Uporov struct mount *mp = vnode_mount(vp);
270528f4f623SFedor Uporov struct thread *td = ap->a_td;
270628f4f623SFedor Uporov struct ucred *cred = ap->a_cred;
270704660064SFedor Uporov char *prefix;
270804660064SFedor Uporov size_t len;
270904660064SFedor Uporov char *attr_str;
271028f4f623SFedor Uporov int err;
271104660064SFedor Uporov
271204660064SFedor Uporov if (fuse_isdeadfs(vp))
271328f4f623SFedor Uporov return (ENXIO);
271404660064SFedor Uporov
271537df9d3bSAlan Somers if (fsess_not_impl(mp, FUSE_SETXATTR))
27161f4a83f9SAlan Somers return EOPNOTSUPP;
27171f4a83f9SAlan Somers
2718666f8543SAlan Somers if (vfs_isrdonly(mp))
2719666f8543SAlan Somers return EROFS;
2720666f8543SAlan Somers
2721ff4fbdf5SAlan Somers /* Deleting xattrs must use VOP_DELETEEXTATTR instead */
27221f4a83f9SAlan Somers if (ap->a_uio == NULL) {
27231f4a83f9SAlan Somers /*
27241f4a83f9SAlan Somers * If we got here as fallback from VOP_DELETEEXTATTR, then
27251f4a83f9SAlan Somers * return EOPNOTSUPP.
27261f4a83f9SAlan Somers */
272737df9d3bSAlan Somers if (fsess_not_impl(mp, FUSE_REMOVEXATTR))
27281f4a83f9SAlan Somers return (EOPNOTSUPP);
27291f4a83f9SAlan Somers else
2730ff4fbdf5SAlan Somers return (EINVAL);
27311f4a83f9SAlan Somers }
2732ff4fbdf5SAlan Somers
2733666f8543SAlan Somers err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td,
2734666f8543SAlan Somers VWRITE);
2735ff4fbdf5SAlan Somers if (err)
2736ff4fbdf5SAlan Somers return err;
2737ff4fbdf5SAlan Somers
273804660064SFedor Uporov /* Default to looking for user attributes. */
273904660064SFedor Uporov if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
274004660064SFedor Uporov prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
274104660064SFedor Uporov else
274204660064SFedor Uporov prefix = EXTATTR_NAMESPACE_USER_STRING;
274304660064SFedor Uporov
274404660064SFedor Uporov len = strlen(prefix) + sizeof(extattr_namespace_separator) +
274504660064SFedor Uporov strlen(ap->a_name) + 1;
274604660064SFedor Uporov
274704660064SFedor Uporov fdisp_init(&fdi, len + sizeof(*set_xattr_in) + uio->uio_resid);
274804660064SFedor Uporov fdisp_make_vp(&fdi, FUSE_SETXATTR, vp, td, cred);
274904660064SFedor Uporov
275004660064SFedor Uporov set_xattr_in = fdi.indata;
275104660064SFedor Uporov set_xattr_in->size = uio->uio_resid;
275204660064SFedor Uporov
275304660064SFedor Uporov attr_str = (char *)fdi.indata + sizeof(*set_xattr_in);
275404660064SFedor Uporov snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
275504660064SFedor Uporov ap->a_name);
275604660064SFedor Uporov
275704660064SFedor Uporov err = uiomove((char *)fdi.indata + sizeof(*set_xattr_in) + len,
275804660064SFedor Uporov uio->uio_resid, uio);
275904660064SFedor Uporov if (err != 0) {
276004660064SFedor Uporov goto out;
276104660064SFedor Uporov }
276204660064SFedor Uporov
276304660064SFedor Uporov err = fdisp_wait_answ(&fdi);
276404660064SFedor Uporov
27651f4a83f9SAlan Somers if (err == ENOSYS) {
276604660064SFedor Uporov fsess_set_notimpl(mp, FUSE_SETXATTR);
27671f4a83f9SAlan Somers err = EOPNOTSUPP;
276804660064SFedor Uporov }
2769f0f7fc1bSAlan Somers if (err == ERESTART) {
2770f0f7fc1bSAlan Somers /* Can't restart after calling uiomove */
2771f0f7fc1bSAlan Somers err = EINTR;
2772f0f7fc1bSAlan Somers }
277304660064SFedor Uporov
277404660064SFedor Uporov out:
277504660064SFedor Uporov fdisp_destroy(&fdi);
277604660064SFedor Uporov return (err);
277704660064SFedor Uporov }
277804660064SFedor Uporov
277904660064SFedor Uporov /*
278004660064SFedor Uporov * The Linux / FUSE extended attribute list is simply a collection of
278104660064SFedor Uporov * NUL-terminated strings. The FreeBSD extended attribute list is a single
278204660064SFedor Uporov * byte length followed by a non-NUL terminated string. So, this allows
278304660064SFedor Uporov * conversion of the Linux / FUSE format to the FreeBSD format in place.
278404660064SFedor Uporov * Linux attribute names are reported with the namespace as a prefix (e.g.
278504660064SFedor Uporov * "user.attribute_name"), but in FreeBSD they are reported without the
278604660064SFedor Uporov * namespace prefix (e.g. "attribute_name"). So, we're going from:
278704660064SFedor Uporov *
278804660064SFedor Uporov * user.attr_name1\0user.attr_name2\0
278904660064SFedor Uporov *
279004660064SFedor Uporov * to:
279104660064SFedor Uporov *
279204660064SFedor Uporov * <num>attr_name1<num>attr_name2
279304660064SFedor Uporov *
279404660064SFedor Uporov * Where "<num>" is a single byte number of characters in the attribute name.
279504660064SFedor Uporov *
279604660064SFedor Uporov * Args:
279704660064SFedor Uporov * prefix - exattr namespace prefix string
279804660064SFedor Uporov * list, list_len - input list with namespace prefixes
279904660064SFedor Uporov * bsd_list, bsd_list_len - output list compatible with bsd vfs
280004660064SFedor Uporov */
280104660064SFedor Uporov static int
fuse_xattrlist_convert(char * prefix,const char * list,int list_len,char * bsd_list,int * bsd_list_len)280204660064SFedor Uporov fuse_xattrlist_convert(char *prefix, const char *list, int list_len,
280304660064SFedor Uporov char *bsd_list, int *bsd_list_len)
280404660064SFedor Uporov {
280504660064SFedor Uporov int len, pos, dist_to_next, prefix_len;
280604660064SFedor Uporov
280704660064SFedor Uporov pos = 0;
280804660064SFedor Uporov *bsd_list_len = 0;
280904660064SFedor Uporov prefix_len = strlen(prefix);
281004660064SFedor Uporov
281104660064SFedor Uporov while (pos < list_len && list[pos] != '\0') {
281204660064SFedor Uporov dist_to_next = strlen(&list[pos]) + 1;
281304660064SFedor Uporov if (bcmp(&list[pos], prefix, prefix_len) == 0 &&
281404660064SFedor Uporov list[pos + prefix_len] == extattr_namespace_separator) {
281504660064SFedor Uporov len = dist_to_next -
281604660064SFedor Uporov (prefix_len + sizeof(extattr_namespace_separator)) - 1;
281704660064SFedor Uporov if (len >= EXTATTR_MAXNAMELEN)
281804660064SFedor Uporov return (ENAMETOOLONG);
281904660064SFedor Uporov
282004660064SFedor Uporov bsd_list[*bsd_list_len] = len;
282104660064SFedor Uporov memcpy(&bsd_list[*bsd_list_len + 1],
282204660064SFedor Uporov &list[pos + prefix_len +
282304660064SFedor Uporov sizeof(extattr_namespace_separator)], len);
282404660064SFedor Uporov
282504660064SFedor Uporov *bsd_list_len += len + 1;
282604660064SFedor Uporov }
282704660064SFedor Uporov
282804660064SFedor Uporov pos += dist_to_next;
282904660064SFedor Uporov }
283004660064SFedor Uporov
283104660064SFedor Uporov return (0);
283204660064SFedor Uporov }
283304660064SFedor Uporov
283404660064SFedor Uporov /*
28355e633330SAlan Somers * List extended attributes
28365e633330SAlan Somers *
28375e633330SAlan Somers * The FUSE_LISTXATTR operation is based on Linux's listxattr(2) syscall, which
28385e633330SAlan Somers * has a number of differences compared to its FreeBSD equivalent,
28395e633330SAlan Somers * extattr_list_file:
28405e633330SAlan Somers *
28415e633330SAlan Somers * - FUSE_LISTXATTR returns all extended attributes across all namespaces,
28425e633330SAlan Somers * whereas listxattr(2) only returns attributes for a single namespace
28435e633330SAlan Somers * - FUSE_LISTXATTR prepends each attribute name with "namespace."
28445e633330SAlan Somers * - If the provided buffer is not large enough to hold the result,
28455e633330SAlan Somers * FUSE_LISTXATTR should return ERANGE, whereas listxattr is expected to
28465e633330SAlan Somers * return as many results as will fit.
28475e633330SAlan Somers */
28485e633330SAlan Somers /*
284904660064SFedor Uporov struct vop_listextattr_args {
285004660064SFedor Uporov struct vop_generic_args a_gen;
285104660064SFedor Uporov struct vnode *a_vp;
285204660064SFedor Uporov int a_attrnamespace;
285304660064SFedor Uporov struct uio *a_uio;
285404660064SFedor Uporov size_t *a_size;
285504660064SFedor Uporov struct ucred *a_cred;
285604660064SFedor Uporov struct thread *a_td;
285704660064SFedor Uporov };
285804660064SFedor Uporov */
285904660064SFedor Uporov static int
fuse_vnop_listextattr(struct vop_listextattr_args * ap)286004660064SFedor Uporov fuse_vnop_listextattr(struct vop_listextattr_args *ap)
286104660064SFedor Uporov {
286204660064SFedor Uporov struct vnode *vp = ap->a_vp;
286304660064SFedor Uporov struct uio *uio = ap->a_uio;
286428f4f623SFedor Uporov struct fuse_dispatcher fdi;
2865493b4a8cSFedor Uporov struct fuse_listxattr_in *list_xattr_in;
2866493b4a8cSFedor Uporov struct fuse_listxattr_out *list_xattr_out;
286704660064SFedor Uporov struct mount *mp = vnode_mount(vp);
286828f4f623SFedor Uporov struct thread *td = ap->a_td;
286928f4f623SFedor Uporov struct ucred *cred = ap->a_cred;
287004660064SFedor Uporov char *prefix;
287104660064SFedor Uporov char *bsd_list = NULL;
2872493b4a8cSFedor Uporov char *linux_list;
287304660064SFedor Uporov int bsd_list_len;
2874493b4a8cSFedor Uporov int linux_list_len;
287528f4f623SFedor Uporov int err;
287604660064SFedor Uporov
287704660064SFedor Uporov if (fuse_isdeadfs(vp))
287828f4f623SFedor Uporov return (ENXIO);
287904660064SFedor Uporov
288037df9d3bSAlan Somers if (fsess_not_impl(mp, FUSE_LISTXATTR))
28811f4a83f9SAlan Somers return EOPNOTSUPP;
28821f4a83f9SAlan Somers
2883666f8543SAlan Somers err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
2884ff4fbdf5SAlan Somers if (err)
2885ff4fbdf5SAlan Somers return err;
2886ff4fbdf5SAlan Somers
288704660064SFedor Uporov /*
288804660064SFedor Uporov * Add space for a NUL and the period separator if enabled.
288904660064SFedor Uporov * Default to looking for user attributes.
289004660064SFedor Uporov */
289104660064SFedor Uporov if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
289204660064SFedor Uporov prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
289304660064SFedor Uporov else
289404660064SFedor Uporov prefix = EXTATTR_NAMESPACE_USER_STRING;
289504660064SFedor Uporov
28963a79e8e7SAlan Somers fdisp_init(&fdi, sizeof(*list_xattr_in));
289704660064SFedor Uporov fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
289804660064SFedor Uporov
2899493b4a8cSFedor Uporov /*
2900493b4a8cSFedor Uporov * Retrieve Linux / FUSE compatible list size.
2901493b4a8cSFedor Uporov */
2902493b4a8cSFedor Uporov list_xattr_in = fdi.indata;
2903493b4a8cSFedor Uporov list_xattr_in->size = 0;
290404660064SFedor Uporov
290504660064SFedor Uporov err = fdisp_wait_answ(&fdi);
290604660064SFedor Uporov if (err != 0) {
29071f4a83f9SAlan Somers if (err == ENOSYS) {
290804660064SFedor Uporov fsess_set_notimpl(mp, FUSE_LISTXATTR);
29091f4a83f9SAlan Somers err = EOPNOTSUPP;
29101f4a83f9SAlan Somers }
291104660064SFedor Uporov goto out;
291204660064SFedor Uporov }
291304660064SFedor Uporov
2914493b4a8cSFedor Uporov list_xattr_out = fdi.answ;
2915493b4a8cSFedor Uporov linux_list_len = list_xattr_out->size;
2916493b4a8cSFedor Uporov if (linux_list_len == 0) {
2917493b4a8cSFedor Uporov if (ap->a_size != NULL)
2918493b4a8cSFedor Uporov *ap->a_size = linux_list_len;
291904660064SFedor Uporov goto out;
292004660064SFedor Uporov }
292104660064SFedor Uporov
292204660064SFedor Uporov /*
2923493b4a8cSFedor Uporov * Retrieve Linux / FUSE compatible list values.
2924493b4a8cSFedor Uporov */
292512292a99SAlan Somers fdisp_refresh_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
2926493b4a8cSFedor Uporov list_xattr_in = fdi.indata;
29275e633330SAlan Somers list_xattr_in->size = linux_list_len;
2928493b4a8cSFedor Uporov
2929493b4a8cSFedor Uporov err = fdisp_wait_answ(&fdi);
29305e633330SAlan Somers if (err == ERANGE) {
29315e633330SAlan Somers /*
29325e633330SAlan Somers * Race detected. The attribute list must've grown since the
29335e633330SAlan Somers * first FUSE_LISTXATTR call. Start over. Go all the way back
29345e633330SAlan Somers * to userland so we can process signals, if necessary, before
29355e633330SAlan Somers * restarting.
29365e633330SAlan Somers */
29375e633330SAlan Somers err = ERESTART;
29385e633330SAlan Somers goto out;
29395e633330SAlan Somers } else if (err != 0)
2940493b4a8cSFedor Uporov goto out;
2941493b4a8cSFedor Uporov
2942493b4a8cSFedor Uporov linux_list = fdi.answ;
29435e633330SAlan Somers /* FUSE doesn't allow the server to return more data than requested */
29445e633330SAlan Somers if (fdi.iosize > linux_list_len) {
29450b9a5c6fSAlan Somers struct fuse_data *data = fuse_get_mpdata(mp);
29460b9a5c6fSAlan Somers
29470b9a5c6fSAlan Somers fuse_warn(data, FSESS_WARN_LSEXTATTR_LONG,
29480b9a5c6fSAlan Somers "server returned "
29495e633330SAlan Somers "more extended attribute data than requested; "
29500b9a5c6fSAlan Somers "should've returned ERANGE instead.");
29515e633330SAlan Somers } else {
29525e633330SAlan Somers /* But returning less data is fine */
2953493b4a8cSFedor Uporov linux_list_len = fdi.iosize;
29545e633330SAlan Somers }
2955493b4a8cSFedor Uporov
2956493b4a8cSFedor Uporov /*
2957493b4a8cSFedor Uporov * Retrieve the BSD compatible list values.
295804660064SFedor Uporov * The Linux / FUSE attribute list format isn't the same
295904660064SFedor Uporov * as FreeBSD's format. So we need to transform it into
296004660064SFedor Uporov * FreeBSD's format before giving it to the user.
296104660064SFedor Uporov */
2962493b4a8cSFedor Uporov bsd_list = malloc(linux_list_len, M_TEMP, M_WAITOK);
2963493b4a8cSFedor Uporov err = fuse_xattrlist_convert(prefix, linux_list, linux_list_len,
296404660064SFedor Uporov bsd_list, &bsd_list_len);
296504660064SFedor Uporov if (err != 0)
296604660064SFedor Uporov goto out;
296704660064SFedor Uporov
2968493b4a8cSFedor Uporov if (ap->a_size != NULL)
2969493b4a8cSFedor Uporov *ap->a_size = bsd_list_len;
2970493b4a8cSFedor Uporov
2971493b4a8cSFedor Uporov if (uio != NULL)
297204660064SFedor Uporov err = uiomove(bsd_list, bsd_list_len, uio);
297304660064SFedor Uporov
297404660064SFedor Uporov out:
297504660064SFedor Uporov free(bsd_list, M_TEMP);
297604660064SFedor Uporov fdisp_destroy(&fdi);
297704660064SFedor Uporov return (err);
297804660064SFedor Uporov }
297904660064SFedor Uporov
298004660064SFedor Uporov /*
298189d57b94SAlan Somers struct vop_deallocate_args {
298289d57b94SAlan Somers struct vop_generic_args a_gen;
298389d57b94SAlan Somers struct vnode *a_vp;
298489d57b94SAlan Somers off_t *a_offset;
298589d57b94SAlan Somers off_t *a_len;
298689d57b94SAlan Somers int a_flags;
298789d57b94SAlan Somers int a_ioflag;
298889d57b94SAlan Somers struct ucred *a_cred;
298989d57b94SAlan Somers };
299089d57b94SAlan Somers */
299189d57b94SAlan Somers static int
fuse_vnop_deallocate(struct vop_deallocate_args * ap)299289d57b94SAlan Somers fuse_vnop_deallocate(struct vop_deallocate_args *ap)
299389d57b94SAlan Somers {
299489d57b94SAlan Somers struct vnode *vp = ap->a_vp;
299589d57b94SAlan Somers struct mount *mp = vnode_mount(vp);
299689d57b94SAlan Somers struct fuse_filehandle *fufh;
299789d57b94SAlan Somers struct fuse_dispatcher fdi;
299889d57b94SAlan Somers struct fuse_fallocate_in *ffi;
299989d57b94SAlan Somers struct ucred *cred = ap->a_cred;
300089d57b94SAlan Somers pid_t pid = curthread->td_proc->p_pid;
300189d57b94SAlan Somers off_t *len = ap->a_len;
300289d57b94SAlan Somers off_t *offset = ap->a_offset;
300389d57b94SAlan Somers int ioflag = ap->a_ioflag;
300489d57b94SAlan Somers off_t filesize;
300589d57b94SAlan Somers int err;
300689d57b94SAlan Somers bool closefufh = false;
300789d57b94SAlan Somers
300889d57b94SAlan Somers if (fuse_isdeadfs(vp))
300989d57b94SAlan Somers return (ENXIO);
301089d57b94SAlan Somers
301189d57b94SAlan Somers if (vfs_isrdonly(mp))
301289d57b94SAlan Somers return (EROFS);
301389d57b94SAlan Somers
301489d57b94SAlan Somers if (fsess_not_impl(mp, FUSE_FALLOCATE))
301589d57b94SAlan Somers goto fallback;
301689d57b94SAlan Somers
301789d57b94SAlan Somers err = fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
301889d57b94SAlan Somers if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
301989d57b94SAlan Somers /*
302089d57b94SAlan Somers * nfsd will do I/O without first doing VOP_OPEN. We
302189d57b94SAlan Somers * must implicitly open the file here
302289d57b94SAlan Somers */
302389d57b94SAlan Somers err = fuse_filehandle_open(vp, FWRITE, &fufh, curthread, cred);
302489d57b94SAlan Somers closefufh = true;
302589d57b94SAlan Somers }
302689d57b94SAlan Somers if (err)
302789d57b94SAlan Somers return (err);
302889d57b94SAlan Somers
302989d57b94SAlan Somers fuse_vnode_update(vp, FN_MTIMECHANGE | FN_CTIMECHANGE);
303089d57b94SAlan Somers
303189d57b94SAlan Somers err = fuse_vnode_size(vp, &filesize, cred, curthread);
303289d57b94SAlan Somers if (err)
303389d57b94SAlan Somers goto out;
303489d57b94SAlan Somers fuse_inval_buf_range(vp, filesize, *offset, *offset + *len);
303589d57b94SAlan Somers
303689d57b94SAlan Somers fdisp_init(&fdi, sizeof(*ffi));
303789d57b94SAlan Somers fdisp_make_vp(&fdi, FUSE_FALLOCATE, vp, curthread, cred);
303889d57b94SAlan Somers ffi = fdi.indata;
303989d57b94SAlan Somers ffi->fh = fufh->fh_id;
304089d57b94SAlan Somers ffi->offset = *offset;
304189d57b94SAlan Somers ffi->length = *len;
304289d57b94SAlan Somers /*
304389d57b94SAlan Somers * FreeBSD's fspacectl is equivalent to Linux's fallocate with
304489d57b94SAlan Somers * mode == FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE
304589d57b94SAlan Somers */
304689d57b94SAlan Somers ffi->mode = FUSE_FALLOC_FL_PUNCH_HOLE | FUSE_FALLOC_FL_KEEP_SIZE;
304789d57b94SAlan Somers err = fdisp_wait_answ(&fdi);
304889d57b94SAlan Somers
304989d57b94SAlan Somers if (err == ENOSYS) {
30501bdf879bSAlan Somers fdisp_destroy(&fdi);
305189d57b94SAlan Somers fsess_set_notimpl(mp, FUSE_FALLOCATE);
305289d57b94SAlan Somers goto fallback;
305389d57b94SAlan Somers } else if (err == EOPNOTSUPP) {
305489d57b94SAlan Somers /*
305589d57b94SAlan Somers * The file system server does not support FUSE_FALLOCATE with
305689d57b94SAlan Somers * the supplied mode for this particular file.
305789d57b94SAlan Somers */
30581bdf879bSAlan Somers fdisp_destroy(&fdi);
305989d57b94SAlan Somers goto fallback;
306089d57b94SAlan Somers } else if (!err) {
306189d57b94SAlan Somers /*
306289d57b94SAlan Somers * Clip the returned offset to EoF. Do it here rather than
306389d57b94SAlan Somers * before FUSE_FALLOCATE just in case the kernel's cached file
306489d57b94SAlan Somers * size is out of date. Unfortunately, FUSE does not return
306589d57b94SAlan Somers * any information about filesize from that operation.
306689d57b94SAlan Somers */
306789d57b94SAlan Somers *offset = MIN(*offset + *len, filesize);
306889d57b94SAlan Somers *len = 0;
306989d57b94SAlan Somers fuse_vnode_undirty_cached_timestamps(vp, false);
307089d57b94SAlan Somers fuse_internal_clear_suid_on_write(vp, cred, curthread);
307189d57b94SAlan Somers
307289d57b94SAlan Somers if (ioflag & IO_SYNC)
307389d57b94SAlan Somers err = fuse_internal_fsync(vp, curthread, MNT_WAIT,
307489d57b94SAlan Somers false);
307589d57b94SAlan Somers }
307689d57b94SAlan Somers
30771bdf879bSAlan Somers fdisp_destroy(&fdi);
3078f93a50d6SAlan Somers out:
307989d57b94SAlan Somers if (closefufh)
308089d57b94SAlan Somers fuse_filehandle_close(vp, fufh, curthread, cred);
308189d57b94SAlan Somers
308289d57b94SAlan Somers return (err);
308389d57b94SAlan Somers
308489d57b94SAlan Somers fallback:
308589d57b94SAlan Somers if (closefufh)
308689d57b94SAlan Somers fuse_filehandle_close(vp, fufh, curthread, cred);
308789d57b94SAlan Somers
308889d57b94SAlan Somers return (vop_stddeallocate(ap));
308989d57b94SAlan Somers }
309089d57b94SAlan Somers
309189d57b94SAlan Somers /*
309204660064SFedor Uporov struct vop_deleteextattr_args {
309304660064SFedor Uporov struct vop_generic_args a_gen;
309404660064SFedor Uporov struct vnode *a_vp;
309504660064SFedor Uporov int a_attrnamespace;
309604660064SFedor Uporov const char *a_name;
309704660064SFedor Uporov struct ucred *a_cred;
309804660064SFedor Uporov struct thread *a_td;
309904660064SFedor Uporov };
310004660064SFedor Uporov */
310104660064SFedor Uporov static int
fuse_vnop_deleteextattr(struct vop_deleteextattr_args * ap)310204660064SFedor Uporov fuse_vnop_deleteextattr(struct vop_deleteextattr_args *ap)
310304660064SFedor Uporov {
310404660064SFedor Uporov struct vnode *vp = ap->a_vp;
310528f4f623SFedor Uporov struct fuse_dispatcher fdi;
310604660064SFedor Uporov struct mount *mp = vnode_mount(vp);
310728f4f623SFedor Uporov struct thread *td = ap->a_td;
310828f4f623SFedor Uporov struct ucred *cred = ap->a_cred;
310904660064SFedor Uporov char *prefix;
311004660064SFedor Uporov size_t len;
311104660064SFedor Uporov char *attr_str;
311204660064SFedor Uporov int err;
311304660064SFedor Uporov
311404660064SFedor Uporov if (fuse_isdeadfs(vp))
311528f4f623SFedor Uporov return (ENXIO);
311604660064SFedor Uporov
311737df9d3bSAlan Somers if (fsess_not_impl(mp, FUSE_REMOVEXATTR))
31181f4a83f9SAlan Somers return EOPNOTSUPP;
31191f4a83f9SAlan Somers
3120666f8543SAlan Somers if (vfs_isrdonly(mp))
3121666f8543SAlan Somers return EROFS;
3122666f8543SAlan Somers
3123666f8543SAlan Somers err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td,
3124666f8543SAlan Somers VWRITE);
3125ff4fbdf5SAlan Somers if (err)
3126ff4fbdf5SAlan Somers return err;
3127ff4fbdf5SAlan Somers
312804660064SFedor Uporov /* Default to looking for user attributes. */
312904660064SFedor Uporov if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
313004660064SFedor Uporov prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
313104660064SFedor Uporov else
313204660064SFedor Uporov prefix = EXTATTR_NAMESPACE_USER_STRING;
313304660064SFedor Uporov
313404660064SFedor Uporov len = strlen(prefix) + sizeof(extattr_namespace_separator) +
313504660064SFedor Uporov strlen(ap->a_name) + 1;
313604660064SFedor Uporov
313704660064SFedor Uporov fdisp_init(&fdi, len);
313804660064SFedor Uporov fdisp_make_vp(&fdi, FUSE_REMOVEXATTR, vp, td, cred);
313904660064SFedor Uporov
314004660064SFedor Uporov attr_str = fdi.indata;
314104660064SFedor Uporov snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
314204660064SFedor Uporov ap->a_name);
314304660064SFedor Uporov
314404660064SFedor Uporov err = fdisp_wait_answ(&fdi);
31451f4a83f9SAlan Somers if (err == ENOSYS) {
314604660064SFedor Uporov fsess_set_notimpl(mp, FUSE_REMOVEXATTR);
31471f4a83f9SAlan Somers err = EOPNOTSUPP;
314804660064SFedor Uporov }
314904660064SFedor Uporov
315004660064SFedor Uporov fdisp_destroy(&fdi);
315104660064SFedor Uporov return (err);
315204660064SFedor Uporov }
315304660064SFedor Uporov
31545fe58019SAttilio Rao /*
31555fe58019SAttilio Rao struct vnop_print_args {
31565fe58019SAttilio Rao struct vnode *a_vp;
31575fe58019SAttilio Rao };
31585fe58019SAttilio Rao */
31595fe58019SAttilio Rao static int
fuse_vnop_print(struct vop_print_args * ap)31605fe58019SAttilio Rao fuse_vnop_print(struct vop_print_args *ap)
31615fe58019SAttilio Rao {
31625fe58019SAttilio Rao struct fuse_vnode_data *fvdat = VTOFUD(ap->a_vp);
31635fe58019SAttilio Rao
31645fe58019SAttilio Rao printf("nodeid: %ju, parent nodeid: %ju, nlookup: %ju, flag: %#x\n",
31655fe58019SAttilio Rao (uintmax_t)VTOILLU(ap->a_vp), (uintmax_t)fvdat->parent_nid,
31665fe58019SAttilio Rao (uintmax_t)fvdat->nlookup,
31675fe58019SAttilio Rao fvdat->flag);
31685fe58019SAttilio Rao
31695fe58019SAttilio Rao return 0;
31705fe58019SAttilio Rao }
3171e5b50fe7SAlan Somers
3172e5b50fe7SAlan Somers /*
3173e5b50fe7SAlan Somers * Get an NFS filehandle for a FUSE file.
3174e5b50fe7SAlan Somers *
3175e5b50fe7SAlan Somers * This will only work for FUSE file systems that guarantee the uniqueness of
31760d2bf489SAlan Somers * nodeid:generation, which most don't.
3177e5b50fe7SAlan Somers */
3178e5b50fe7SAlan Somers /*
3179e5b50fe7SAlan Somers vop_vptofh {
3180e5b50fe7SAlan Somers IN struct vnode *a_vp;
3181e5b50fe7SAlan Somers IN struct fid *a_fhp;
3182e5b50fe7SAlan Somers };
3183e5b50fe7SAlan Somers */
3184e5b50fe7SAlan Somers static int
fuse_vnop_vptofh(struct vop_vptofh_args * ap)3185e5b50fe7SAlan Somers fuse_vnop_vptofh(struct vop_vptofh_args *ap)
3186e5b50fe7SAlan Somers {
3187e5b50fe7SAlan Somers struct vnode *vp = ap->a_vp;
3188e5b50fe7SAlan Somers struct fuse_vnode_data *fvdat = VTOFUD(vp);
3189e5b50fe7SAlan Somers struct fuse_fid *fhp = (struct fuse_fid *)(ap->a_fhp);
3190e5b50fe7SAlan Somers _Static_assert(sizeof(struct fuse_fid) <= sizeof(struct fid),
3191e5b50fe7SAlan Somers "FUSE fid type is too big");
3192e5b50fe7SAlan Somers struct mount *mp = vnode_mount(vp);
3193e5b50fe7SAlan Somers struct fuse_data *data = fuse_get_mpdata(mp);
3194e5b50fe7SAlan Somers struct vattr va;
3195e5b50fe7SAlan Somers int err;
3196e5b50fe7SAlan Somers
319700134a07SAlan Somers if (!(data->dataflags & FSESS_EXPORT_SUPPORT)) {
319800134a07SAlan Somers /* NFS requires lookups for "." and ".." */
319900134a07SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1,
320000134a07SAlan Somers "VOP_VPTOFH without FUSE_EXPORT_SUPPORT");
3201e5b50fe7SAlan Somers return EOPNOTSUPP;
320200134a07SAlan Somers }
320300134a07SAlan Somers if ((mp->mnt_flag & MNT_EXPORTED) &&
3204*f0f596bdSCismonX fsess_is_impl(mp, FUSE_OPENDIR))
320500134a07SAlan Somers {
320600134a07SAlan Somers /*
320700134a07SAlan Somers * NFS is stateless, so nfsd must reopen a directory on every
320800134a07SAlan Somers * call to VOP_READDIR, passing in the d_off field from the
3209*f0f596bdSCismonX * final dirent of the previous invocation. But if the server
3210*f0f596bdSCismonX * implements FUSE_OPENDIR, the FUSE protocol does not
321100134a07SAlan Somers * guarantee that d_off will be valid after a directory is
321200134a07SAlan Somers * closed and reopened. So prohibit exporting FUSE file
3213*f0f596bdSCismonX * systems that implement FUSE_OPENDIR.
321400134a07SAlan Somers *
321500134a07SAlan Somers * But userspace NFS servers don't have this problem.
321600134a07SAlan Somers */
321700134a07SAlan Somers SDT_PROBE2(fusefs, , vnops, trace, 1,
3218*f0f596bdSCismonX "VOP_VPTOFH with FUSE_OPENDIR");
321900134a07SAlan Somers return EOPNOTSUPP;
322000134a07SAlan Somers }
3221e5b50fe7SAlan Somers
3222e5b50fe7SAlan Somers err = fuse_internal_getattr(vp, &va, curthread->td_ucred, curthread);
3223e5b50fe7SAlan Somers if (err)
3224e5b50fe7SAlan Somers return err;
3225e5b50fe7SAlan Somers
3226e5b50fe7SAlan Somers /*ip = VTOI(ap->a_vp);*/
3227e5b50fe7SAlan Somers /*ufhp = (struct ufid *)ap->a_fhp;*/
3228e5b50fe7SAlan Somers fhp->len = sizeof(struct fuse_fid);
3229e5b50fe7SAlan Somers fhp->nid = fvdat->nid;
3230e5b50fe7SAlan Somers if (fvdat->generation <= UINT32_MAX)
3231e5b50fe7SAlan Somers fhp->gen = fvdat->generation;
3232e5b50fe7SAlan Somers else
3233e5b50fe7SAlan Somers return EOVERFLOW;
3234e5b50fe7SAlan Somers return (0);
3235e5b50fe7SAlan Somers }
3236