2017-11-20 19:43:44 +00:00
|
|
|
/*-
|
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
*
|
2012-10-13 23:54:26 +00:00
|
|
|
* Copyright (c) 2007-2009 Google Inc. and Amit Singh
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are
|
|
|
|
* met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above
|
|
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
|
|
* in the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* * Neither the name of Google Inc. nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived from
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Csaba Henk.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2019-02-28 19:27:54 +00:00
|
|
|
#include <sys/param.h>
|
2012-10-13 23:54:26 +00:00
|
|
|
#include <sys/module.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/lock.h>
|
2013-03-09 02:32:23 +00:00
|
|
|
#include <sys/rwlock.h>
|
2012-10-13 23:54:26 +00:00
|
|
|
#include <sys/sx.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/namei.h>
|
2017-10-14 19:02:52 +00:00
|
|
|
#include <sys/extattr.h>
|
2012-10-13 23:54:26 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/unistd.h>
|
|
|
|
#include <sys/filedesc.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/fcntl.h>
|
|
|
|
#include <sys/dirent.h>
|
|
|
|
#include <sys/bio.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/sysctl.h>
|
2017-04-17 17:20:48 +00:00
|
|
|
#include <sys/vmmeter.h>
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
#include <vm/vm.h>
|
|
|
|
#include <vm/vm_extern.h>
|
|
|
|
#include <vm/pmap.h>
|
|
|
|
#include <vm/vm_map.h>
|
|
|
|
#include <vm/vm_page.h>
|
|
|
|
#include <vm/vm_param.h>
|
|
|
|
#include <vm/vm_object.h>
|
|
|
|
#include <vm/vm_pager.h>
|
|
|
|
#include <vm/vnode_pager.h>
|
|
|
|
#include <vm/vm_object.h>
|
|
|
|
|
|
|
|
#include "fuse.h"
|
|
|
|
#include "fuse_file.h"
|
|
|
|
#include "fuse_internal.h"
|
|
|
|
#include "fuse_ipc.h"
|
|
|
|
#include "fuse_node.h"
|
|
|
|
#include "fuse_io.h"
|
|
|
|
|
|
|
|
#include <sys/priv.h>
|
|
|
|
|
2019-03-26 03:02:45 +00:00
|
|
|
/* Maximum number of hardlinks to a single FUSE file */
|
|
|
|
#define FUSE_LINK_MAX UINT32_MAX
|
|
|
|
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROVIDER_DECLARE(fusefs);
|
2019-02-28 19:27:54 +00:00
|
|
|
/*
|
|
|
|
* Fuse trace probe:
|
|
|
|
* arg0: verbosity. Higher numbers give more verbose messages
|
|
|
|
* arg1: Textual message
|
|
|
|
*/
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE_DEFINE2(fusefs, , vnops, trace, "int", "char*");
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
/* vnode ops */
|
|
|
|
static vop_access_t fuse_vnop_access;
|
2019-04-12 23:22:27 +00:00
|
|
|
static vop_advlock_t fuse_vnop_advlock;
|
2019-04-29 01:40:35 +00:00
|
|
|
static vop_close_t fuse_fifo_close;
|
2012-10-13 23:54:26 +00:00
|
|
|
static vop_close_t fuse_vnop_close;
|
|
|
|
static vop_create_t fuse_vnop_create;
|
2017-10-14 19:02:52 +00:00
|
|
|
static vop_deleteextattr_t fuse_vnop_deleteextattr;
|
2019-03-21 23:01:56 +00:00
|
|
|
static vop_fdatasync_t fuse_vnop_fdatasync;
|
2012-10-13 23:54:26 +00:00
|
|
|
static vop_fsync_t fuse_vnop_fsync;
|
|
|
|
static vop_getattr_t fuse_vnop_getattr;
|
2017-10-14 19:02:52 +00:00
|
|
|
static vop_getextattr_t fuse_vnop_getextattr;
|
2012-10-13 23:54:26 +00:00
|
|
|
static vop_inactive_t fuse_vnop_inactive;
|
|
|
|
static vop_link_t fuse_vnop_link;
|
2017-10-14 19:02:52 +00:00
|
|
|
static vop_listextattr_t fuse_vnop_listextattr;
|
2012-10-13 23:54:26 +00:00
|
|
|
static vop_lookup_t fuse_vnop_lookup;
|
|
|
|
static vop_mkdir_t fuse_vnop_mkdir;
|
|
|
|
static vop_mknod_t fuse_vnop_mknod;
|
|
|
|
static vop_open_t fuse_vnop_open;
|
2017-12-19 19:09:06 +00:00
|
|
|
static vop_pathconf_t fuse_vnop_pathconf;
|
2012-10-13 23:54:26 +00:00
|
|
|
static vop_read_t fuse_vnop_read;
|
|
|
|
static vop_readdir_t fuse_vnop_readdir;
|
|
|
|
static vop_readlink_t fuse_vnop_readlink;
|
|
|
|
static vop_reclaim_t fuse_vnop_reclaim;
|
|
|
|
static vop_remove_t fuse_vnop_remove;
|
|
|
|
static vop_rename_t fuse_vnop_rename;
|
|
|
|
static vop_rmdir_t fuse_vnop_rmdir;
|
|
|
|
static vop_setattr_t fuse_vnop_setattr;
|
2017-10-14 19:02:52 +00:00
|
|
|
static vop_setextattr_t fuse_vnop_setextattr;
|
2012-10-13 23:54:26 +00:00
|
|
|
static vop_strategy_t fuse_vnop_strategy;
|
|
|
|
static vop_symlink_t fuse_vnop_symlink;
|
|
|
|
static vop_write_t fuse_vnop_write;
|
|
|
|
static vop_getpages_t fuse_vnop_getpages;
|
|
|
|
static vop_putpages_t fuse_vnop_putpages;
|
|
|
|
static vop_print_t fuse_vnop_print;
|
|
|
|
|
2019-04-29 01:40:35 +00:00
|
|
|
struct vop_vector fuse_fifoops = {
|
|
|
|
.vop_default = &fifo_specops,
|
|
|
|
.vop_access = fuse_vnop_access,
|
|
|
|
.vop_close = fuse_fifo_close,
|
|
|
|
.vop_fsync = fuse_vnop_fsync,
|
|
|
|
.vop_getattr = fuse_vnop_getattr,
|
|
|
|
.vop_inactive = fuse_vnop_inactive,
|
|
|
|
.vop_pathconf = fuse_vnop_pathconf,
|
|
|
|
.vop_print = fuse_vnop_print,
|
|
|
|
.vop_read = VOP_PANIC,
|
|
|
|
.vop_reclaim = fuse_vnop_reclaim,
|
|
|
|
.vop_setattr = fuse_vnop_setattr,
|
|
|
|
.vop_write = VOP_PANIC,
|
|
|
|
};
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
struct vop_vector fuse_vnops = {
|
|
|
|
.vop_default = &default_vnodeops,
|
|
|
|
.vop_access = fuse_vnop_access,
|
2019-04-12 23:22:27 +00:00
|
|
|
.vop_advlock = fuse_vnop_advlock,
|
2012-10-13 23:54:26 +00:00
|
|
|
.vop_close = fuse_vnop_close,
|
|
|
|
.vop_create = fuse_vnop_create,
|
2017-10-14 19:02:52 +00:00
|
|
|
.vop_deleteextattr = fuse_vnop_deleteextattr,
|
2012-10-13 23:54:26 +00:00
|
|
|
.vop_fsync = fuse_vnop_fsync,
|
2019-03-21 23:01:56 +00:00
|
|
|
.vop_fdatasync = fuse_vnop_fdatasync,
|
2012-10-13 23:54:26 +00:00
|
|
|
.vop_getattr = fuse_vnop_getattr,
|
2017-10-14 19:02:52 +00:00
|
|
|
.vop_getextattr = fuse_vnop_getextattr,
|
2012-10-13 23:54:26 +00:00
|
|
|
.vop_inactive = fuse_vnop_inactive,
|
|
|
|
.vop_link = fuse_vnop_link,
|
2017-10-14 19:02:52 +00:00
|
|
|
.vop_listextattr = fuse_vnop_listextattr,
|
2012-10-13 23:54:26 +00:00
|
|
|
.vop_lookup = fuse_vnop_lookup,
|
|
|
|
.vop_mkdir = fuse_vnop_mkdir,
|
|
|
|
.vop_mknod = fuse_vnop_mknod,
|
|
|
|
.vop_open = fuse_vnop_open,
|
2017-12-19 19:09:06 +00:00
|
|
|
.vop_pathconf = fuse_vnop_pathconf,
|
2012-10-13 23:54:26 +00:00
|
|
|
.vop_read = fuse_vnop_read,
|
|
|
|
.vop_readdir = fuse_vnop_readdir,
|
|
|
|
.vop_readlink = fuse_vnop_readlink,
|
|
|
|
.vop_reclaim = fuse_vnop_reclaim,
|
|
|
|
.vop_remove = fuse_vnop_remove,
|
|
|
|
.vop_rename = fuse_vnop_rename,
|
|
|
|
.vop_rmdir = fuse_vnop_rmdir,
|
|
|
|
.vop_setattr = fuse_vnop_setattr,
|
2017-10-14 19:02:52 +00:00
|
|
|
.vop_setextattr = fuse_vnop_setextattr,
|
2012-10-13 23:54:26 +00:00
|
|
|
.vop_strategy = fuse_vnop_strategy,
|
|
|
|
.vop_symlink = fuse_vnop_symlink,
|
|
|
|
.vop_write = fuse_vnop_write,
|
|
|
|
.vop_getpages = fuse_vnop_getpages,
|
|
|
|
.vop_putpages = fuse_vnop_putpages,
|
|
|
|
.vop_print = fuse_vnop_print,
|
|
|
|
};
|
|
|
|
|
|
|
|
static u_long fuse_lookup_cache_hits = 0;
|
|
|
|
|
2019-03-20 21:48:43 +00:00
|
|
|
SYSCTL_ULONG(_vfs_fusefs, OID_AUTO, lookup_cache_hits, CTLFLAG_RD,
|
2019-02-20 02:48:59 +00:00
|
|
|
&fuse_lookup_cache_hits, 0, "number of positive cache hits in lookup");
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
static u_long fuse_lookup_cache_misses = 0;
|
|
|
|
|
2019-03-20 21:48:43 +00:00
|
|
|
SYSCTL_ULONG(_vfs_fusefs, OID_AUTO, lookup_cache_misses, CTLFLAG_RD,
|
2019-02-20 02:48:59 +00:00
|
|
|
&fuse_lookup_cache_misses, 0, "number of cache misses in lookup");
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: This feature is highly experimental and can bring to instabilities,
|
|
|
|
* needs revisiting before to be enabled by default.
|
|
|
|
*/
|
|
|
|
static int fuse_reclaim_revoked = 0;
|
|
|
|
|
2019-03-20 21:48:43 +00:00
|
|
|
SYSCTL_INT(_vfs_fusefs, OID_AUTO, reclaim_revoked, CTLFLAG_RW,
|
2012-10-13 23:54:26 +00:00
|
|
|
&fuse_reclaim_revoked, 0, "");
|
|
|
|
|
Allocate pager bufs from UMA instead of 80-ish mutex protected linked list.
o In vm_pager_bufferinit() create pbuf_zone and start accounting on how many
pbufs are we going to have set.
In various subsystems that are going to utilize pbufs create private zones
via call to pbuf_zsecond_create(). The latter calls uma_zsecond_create(),
and sets a limit on created zone. After startup preallocate pbufs according
to requirements of all pbuf zones.
Subsystems that used to have a private limit with old allocator now have
private pbuf zones: md(4), fusefs, NFS client, smbfs, VFS cluster, FFS,
swap, vnode pager.
The following subsystems use shared pbuf zone: cam(4), nvme(4), physio(9),
aio(4). They should have their private limits, but changing that is out of
scope of this commit.
o Fetch tunable value of kern.nswbuf from init_param2() and while here move
NSWBUF_MIN to opt_param.h and eliminate opt_swap.h, that was holding only
this option.
Default values aren't touched by this commit, but they probably should be
reviewed wrt to modern hardware.
This change removes a tight bottleneck from sendfile(2) operation, that
uses pbufs in vnode pager. Other pagers also would benefit from faster
allocation.
Together with: gallatin
Tested by: pho
2019-01-15 01:02:16 +00:00
|
|
|
uma_zone_t fuse_pbuf_zone;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
#define fuse_vm_page_lock(m) vm_page_lock((m));
|
|
|
|
#define fuse_vm_page_unlock(m) vm_page_unlock((m));
|
|
|
|
#define fuse_vm_page_lock_queues() ((void)0)
|
|
|
|
#define fuse_vm_page_unlock_queues() ((void)0)
|
|
|
|
|
2019-04-10 21:10:21 +00:00
|
|
|
/* Check permission for extattr operations, much like extattr_check_cred */
|
|
|
|
static int
|
|
|
|
fuse_extattr_check_cred(struct vnode *vp, int ns, struct ucred *cred,
|
|
|
|
struct thread *td, accmode_t accmode)
|
|
|
|
{
|
|
|
|
struct mount *mp = vnode_mount(vp);
|
|
|
|
struct fuse_data *data = fuse_get_mpdata(mp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Kernel-invoked always succeeds.
|
|
|
|
*/
|
|
|
|
if (cred == NOCRED)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not allow privileged processes in jail to directly manipulate
|
|
|
|
* system attributes.
|
|
|
|
*/
|
|
|
|
switch (ns) {
|
|
|
|
case EXTATTR_NAMESPACE_SYSTEM:
|
|
|
|
if (data->dataflags & FSESS_DEFAULT_PERMISSIONS) {
|
|
|
|
return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM));
|
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case EXTATTR_NAMESPACE_USER:
|
|
|
|
return (fuse_internal_access(vp, accmode, td, cred));
|
|
|
|
default:
|
|
|
|
return (EPERM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-01 20:42:15 +00:00
|
|
|
/* Get a filehandle for a directory */
|
|
|
|
static int
|
|
|
|
fuse_filehandle_get_dir(struct vnode *vp, struct fuse_filehandle **fufhp,
|
|
|
|
struct ucred *cred, pid_t pid)
|
|
|
|
{
|
2019-04-03 19:59:45 +00:00
|
|
|
if (fuse_filehandle_get(vp, FREAD, fufhp, cred, pid) == 0)
|
2019-04-01 20:42:15 +00:00
|
|
|
return 0;
|
2019-04-03 19:59:45 +00:00
|
|
|
return fuse_filehandle_get(vp, FEXEC, fufhp, cred, pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send FUSE_FLUSH for this vnode */
|
|
|
|
static int
|
|
|
|
fuse_flush(struct vnode *vp, struct ucred *cred, pid_t pid, int fflag)
|
|
|
|
{
|
|
|
|
struct fuse_flush_in *ffi;
|
|
|
|
struct fuse_filehandle *fufh;
|
|
|
|
struct fuse_dispatcher fdi;
|
|
|
|
struct thread *td = curthread;
|
|
|
|
struct mount *mp = vnode_mount(vp);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!fsess_isimpl(vnode_mount(vp), FUSE_FLUSH))
|
|
|
|
return 0;
|
|
|
|
|
2019-04-12 23:22:27 +00:00
|
|
|
err = fuse_filehandle_getrw(vp, fflag, &fufh, cred, pid);
|
2019-04-03 19:59:45 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
fdisp_init(&fdi, sizeof(*ffi));
|
|
|
|
fdisp_make_vp(&fdi, FUSE_FLUSH, vp, td, cred);
|
|
|
|
ffi = fdi.indata;
|
|
|
|
ffi->fh = fufh->fh_id;
|
2019-04-12 23:22:27 +00:00
|
|
|
/*
|
|
|
|
* If the file has a POSIX lock then we're supposed to set lock_owner.
|
|
|
|
* If not, then lock_owner is undefined. So we may as well always set
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
ffi->lock_owner = td->td_proc->p_pid;
|
2019-04-03 19:59:45 +00:00
|
|
|
|
|
|
|
err = fdisp_wait_answ(&fdi);
|
|
|
|
if (err == ENOSYS) {
|
|
|
|
fsess_set_notimpl(mp, FUSE_FLUSH);
|
|
|
|
err = 0;
|
|
|
|
}
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return err;
|
2019-04-01 20:42:15 +00:00
|
|
|
}
|
|
|
|
|
2019-04-29 01:40:35 +00:00
|
|
|
/* Close wrapper for fifos. */
|
|
|
|
static int
|
|
|
|
fuse_fifo_close(struct vop_close_args *ap)
|
|
|
|
{
|
|
|
|
return (fifo_specops.vop_close(ap));
|
|
|
|
}
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
/*
|
|
|
|
struct vnop_access_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vnode *a_vp;
|
2012-10-13 23:54:26 +00:00
|
|
|
#if VOP_ACCESS_TAKES_ACCMODE_T
|
2019-02-20 02:49:26 +00:00
|
|
|
accmode_t a_accmode;
|
2012-10-13 23:54:26 +00:00
|
|
|
#else
|
2019-02-20 02:49:26 +00:00
|
|
|
int a_mode;
|
2012-10-13 23:54:26 +00:00
|
|
|
#endif
|
2019-02-20 02:49:26 +00:00
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
2012-10-13 23:54:26 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_access(struct vop_access_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
int accmode = ap->a_accmode;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
|
|
|
|
|
|
|
struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
|
|
|
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
if (vnode_isvroot(vp)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
if (!(data->dataflags & FSESS_INITED)) {
|
|
|
|
if (vnode_isvroot(vp)) {
|
2018-12-11 19:32:16 +00:00
|
|
|
if (priv_check_cred(cred, PRIV_VFS_ADMIN) ||
|
2012-10-13 23:54:26 +00:00
|
|
|
(fuse_match_cred(data->daemoncred, cred) == 0)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EBADF;
|
|
|
|
}
|
|
|
|
if (vnode_islnk(vp)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-10 21:10:21 +00:00
|
|
|
err = fuse_internal_access(vp, accmode, ap->a_td, ap->a_cred);
|
2012-10-13 23:54:26 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-04-12 23:22:27 +00:00
|
|
|
/*
|
|
|
|
* struct vop_advlock_args {
|
|
|
|
* struct vop_generic_args a_gen;
|
|
|
|
* struct vnode *a_vp;
|
|
|
|
* void *a_id;
|
|
|
|
* int a_op;
|
|
|
|
* struct flock *a_fl;
|
|
|
|
* int a_flags;
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_advlock(struct vop_advlock_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct flock *fl = ap->a_fl;
|
|
|
|
struct thread *td = curthread;
|
|
|
|
struct ucred *cred = td->td_ucred;
|
|
|
|
pid_t pid = td->td_proc->p_pid;
|
|
|
|
struct fuse_filehandle *fufh;
|
|
|
|
struct fuse_dispatcher fdi;
|
|
|
|
struct fuse_lk_in *fli;
|
|
|
|
struct fuse_lk_out *flo;
|
|
|
|
enum fuse_opcode op;
|
|
|
|
int dataflags, err;
|
|
|
|
|
|
|
|
dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(dataflags & FSESS_POSIX_LOCKS))
|
|
|
|
return vop_stdadvlock(ap);
|
|
|
|
|
|
|
|
err = fuse_filehandle_get_anyflags(vp, &fufh, cred, pid);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
fdisp_init(&fdi, sizeof(*fli));
|
|
|
|
|
|
|
|
switch(ap->a_op) {
|
|
|
|
case F_GETLK:
|
|
|
|
op = FUSE_GETLK;
|
|
|
|
break;
|
|
|
|
case F_SETLK:
|
|
|
|
op = FUSE_SETLK;
|
|
|
|
break;
|
|
|
|
case F_SETLKW:
|
|
|
|
op = FUSE_SETLKW;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fdisp_make_vp(&fdi, op, vp, td, cred);
|
|
|
|
fli = fdi.indata;
|
|
|
|
fli->fh = fufh->fh_id;
|
|
|
|
fli->owner = fl->l_pid;
|
|
|
|
fli->lk.start = fl->l_start;
|
|
|
|
if (fl->l_len != 0)
|
|
|
|
fli->lk.end = fl->l_start + fl->l_len - 1;
|
|
|
|
else
|
|
|
|
fli->lk.end = INT64_MAX;
|
|
|
|
fli->lk.type = fl->l_type;
|
|
|
|
fli->lk.pid = fl->l_pid;
|
|
|
|
|
|
|
|
err = fdisp_wait_answ(&fdi);
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
|
|
|
|
if (err == 0 && op == FUSE_GETLK) {
|
|
|
|
flo = fdi.answ;
|
|
|
|
fl->l_type = flo->lk.type;
|
|
|
|
fl->l_pid = flo->lk.pid;
|
|
|
|
if (flo->lk.type != F_UNLCK) {
|
|
|
|
fl->l_start = flo->lk.start;
|
|
|
|
if (flo->lk.end == INT64_MAX)
|
|
|
|
fl->l_len = 0;
|
|
|
|
else
|
|
|
|
fl->l_len = flo->lk.end - flo->lk.start + 1;
|
|
|
|
fl->l_start = flo->lk.start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
/*
|
2019-04-03 19:59:45 +00:00
|
|
|
struct vop_close_args {
|
2012-10-13 23:54:26 +00:00
|
|
|
struct vnode *a_vp;
|
|
|
|
int a_fflag;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_close(struct vop_close_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
|
|
|
int fflag = ap->a_fflag;
|
2019-04-01 20:42:15 +00:00
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
pid_t pid = td->td_proc->p_pid;
|
2019-04-03 19:59:45 +00:00
|
|
|
int err = 0;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-03 20:57:43 +00:00
|
|
|
if (fuse_isdeadfs(vp))
|
2012-10-13 23:54:26 +00:00
|
|
|
return 0;
|
2019-04-03 20:57:43 +00:00
|
|
|
if (vnode_isdir(vp))
|
2012-10-13 23:54:26 +00:00
|
|
|
return 0;
|
2019-04-03 20:57:43 +00:00
|
|
|
if (fflag & IO_NDELAY)
|
2012-10-13 23:54:26 +00:00
|
|
|
return 0;
|
2019-04-03 20:57:43 +00:00
|
|
|
|
2019-04-03 19:59:45 +00:00
|
|
|
err = fuse_flush(vp, cred, pid, fflag);
|
2019-04-01 14:23:43 +00:00
|
|
|
/* TODO: close the file handle, if we're sure it's no longer used */
|
2012-10-13 23:54:26 +00:00
|
|
|
if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
|
2019-04-01 20:42:15 +00:00
|
|
|
fuse_vnode_savesize(vp, cred, td->td_proc->p_pid);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
2019-04-03 19:59:45 +00:00
|
|
|
return err;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
2019-03-23 00:22:29 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
struct fuse_mknod_in *fmni;
|
|
|
|
|
|
|
|
fdisp_init(fdip, sizeof(*fmni) + cnp->cn_namelen + 1);
|
|
|
|
*op = FUSE_MKNOD;
|
|
|
|
fdisp_make(fdip, *op, vnode_mount(dvp), parentnid, td, cred);
|
|
|
|
fmni = fdip->indata;
|
|
|
|
fmni->mode = mode;
|
|
|
|
fmni->rdev = 0;
|
|
|
|
memcpy((char *)fdip->indata + sizeof(*fmni), cnp->cn_nameptr,
|
|
|
|
cnp->cn_namelen);
|
|
|
|
((char *)fdip->indata)[sizeof(*fmni) + cnp->cn_namelen] = '\0';
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
/*
|
|
|
|
struct vnop_create_args {
|
|
|
|
struct vnode *a_dvp;
|
|
|
|
struct vnode **a_vpp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
struct vattr *a_vap;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_create(struct vop_create_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct thread *td = cnp->cn_thread;
|
|
|
|
struct ucred *cred = cnp->cn_cred;
|
|
|
|
|
|
|
|
struct fuse_open_in *foi;
|
|
|
|
struct fuse_entry_out *feo;
|
2019-03-23 00:22:29 +00:00
|
|
|
struct fuse_open_out *foo;
|
|
|
|
struct fuse_dispatcher fdi, fdi2;
|
2012-10-13 23:54:26 +00:00
|
|
|
struct fuse_dispatcher *fdip = &fdi;
|
2019-03-23 00:22:29 +00:00
|
|
|
struct fuse_dispatcher *fdip2 = NULL;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
struct mount *mp = vnode_mount(dvp);
|
|
|
|
uint64_t parentnid = VTOFUD(dvp)->nid;
|
|
|
|
mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
2019-03-23 00:22:29 +00:00
|
|
|
enum fuse_opcode op;
|
2019-04-02 18:09:40 +00:00
|
|
|
int flags;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* VOP_CREATE doesn't tell us the open(2) flags, so we guess. Only a
|
|
|
|
* writable mode makes sense, and we might as well include readability
|
|
|
|
* too.
|
|
|
|
*/
|
|
|
|
flags = O_RDWR;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (fuse_isdeadfs(dvp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
bzero(&fdi, sizeof(fdi));
|
|
|
|
|
2019-04-29 16:24:51 +00:00
|
|
|
if ((vap->va_type != VREG && vap->va_type != VSOCK))
|
2016-05-18 22:23:20 +00:00
|
|
|
return (EINVAL);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (!fsess_isimpl(mp, FUSE_CREATE)) {
|
2019-03-23 00:22:29 +00:00
|
|
|
/* Fallback to FUSE_MKNOD/FUSE_OPEN */
|
|
|
|
fdisp_make_mknod_for_fallback(fdip, cnp, dvp, parentnid, td,
|
|
|
|
cred, mode, &op);
|
|
|
|
} else {
|
|
|
|
/* Use FUSE_CREATE */
|
|
|
|
op = FUSE_CREATE;
|
|
|
|
fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
|
|
|
|
fdisp_make(fdip, op, vnode_mount(dvp), parentnid, td, cred);
|
|
|
|
foi = fdip->indata;
|
|
|
|
foi->mode = mode;
|
2019-04-02 18:09:40 +00:00
|
|
|
foi->flags = O_CREAT | flags;
|
2019-03-23 00:22:29 +00:00
|
|
|
memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
|
|
|
|
cnp->cn_namelen);
|
|
|
|
((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = fdisp_wait_answ(fdip);
|
|
|
|
|
2012-11-03 23:32:32 +00:00
|
|
|
if (err) {
|
2019-03-23 00:22:29 +00:00
|
|
|
if (err == ENOSYS && op == FUSE_CREATE) {
|
2012-11-03 23:32:32 +00:00
|
|
|
fsess_set_notimpl(mp, FUSE_CREATE);
|
2019-03-23 00:22:29 +00:00
|
|
|
fdisp_make_mknod_for_fallback(fdip, cnp, dvp,
|
|
|
|
parentnid, td, cred, mode, &op);
|
|
|
|
err = fdisp_wait_answ(fdip);
|
|
|
|
}
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
2016-05-18 22:23:20 +00:00
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
feo = fdip->answ;
|
|
|
|
|
2019-04-29 16:24:51 +00:00
|
|
|
if ((err = fuse_internal_checkentry(feo, vap->va_type))) {
|
2012-10-13 23:54:26 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2019-03-23 00:22:29 +00:00
|
|
|
|
|
|
|
if (op == FUSE_CREATE) {
|
|
|
|
foo = (struct fuse_open_out*)(feo + 1);
|
|
|
|
} else {
|
|
|
|
/* Issue a separate FUSE_OPEN */
|
|
|
|
fdip2 = &fdi2;
|
|
|
|
fdisp_init(fdip2, sizeof(*foi));
|
|
|
|
fdisp_make(fdip2, FUSE_OPEN, vnode_mount(dvp), feo->nodeid, td,
|
|
|
|
cred);
|
|
|
|
foi = fdip2->indata;
|
|
|
|
foi->mode = mode;
|
2019-04-02 18:09:40 +00:00
|
|
|
foi->flags = flags;
|
2019-03-23 00:22:29 +00:00
|
|
|
err = fdisp_wait_answ(fdip2);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
foo = fdip2->answ;
|
|
|
|
}
|
2019-04-29 16:24:51 +00:00
|
|
|
err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, vap->va_type);
|
2012-10-13 23:54:26 +00:00
|
|
|
if (err) {
|
|
|
|
struct fuse_release_in *fri;
|
|
|
|
uint64_t nodeid = feo->nodeid;
|
2019-03-23 00:22:29 +00:00
|
|
|
uint64_t fh_id = foo->fh;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
fdisp_init(fdip, sizeof(*fri));
|
|
|
|
fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, td, cred);
|
|
|
|
fri = fdip->indata;
|
|
|
|
fri->fh = fh_id;
|
2019-04-02 18:09:40 +00:00
|
|
|
fri->flags = flags;
|
2012-10-13 23:54:26 +00:00
|
|
|
fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
|
2019-04-19 21:50:23 +00:00
|
|
|
fuse_insert_message(fdip->tick, false);
|
2019-03-23 00:22:29 +00:00
|
|
|
goto out;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
ASSERT_VOP_ELOCKED(*vpp, "fuse_vnop_create");
|
2019-04-08 18:45:41 +00:00
|
|
|
fuse_internal_cache_attrs(*vpp, &feo->attr, feo->attr_valid,
|
|
|
|
feo->attr_valid_nsec, NULL);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-04 20:30:14 +00:00
|
|
|
fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, td, cred, foo);
|
2019-03-23 00:22:29 +00:00
|
|
|
fuse_vnode_open(*vpp, foo->open_flags, td);
|
2012-10-13 23:54:26 +00:00
|
|
|
cache_purge_negative(dvp);
|
|
|
|
|
|
|
|
out:
|
2019-03-23 00:22:29 +00:00
|
|
|
if (fdip2)
|
|
|
|
fdisp_destroy(fdip2);
|
2012-10-13 23:54:26 +00:00
|
|
|
fdisp_destroy(fdip);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-03-21 23:01:56 +00:00
|
|
|
struct vnop_fdatasync_args {
|
|
|
|
struct vop_generic_args a_gen;
|
|
|
|
struct vnode * a_vp;
|
|
|
|
struct thread * a_td;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_fdatasync(struct vop_fdatasync_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
int waitfor = MNT_WAIT;
|
|
|
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ((err = vop_stdfdatasync_buf(ap)))
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return fuse_internal_fsync(vp, td, waitfor, true);
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_fsync_args {
|
2019-03-21 23:01:56 +00:00
|
|
|
struct vop_generic_args a_gen;
|
2012-10-13 23:54:26 +00:00
|
|
|
struct vnode * a_vp;
|
|
|
|
int a_waitfor;
|
|
|
|
struct thread * a_td;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_fsync(struct vop_fsync_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct thread *td = ap->a_td;
|
2019-03-21 22:17:10 +00:00
|
|
|
int waitfor = ap->a_waitfor;
|
2019-03-21 23:01:56 +00:00
|
|
|
int err = 0;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ((err = vop_stdfsync(ap)))
|
|
|
|
return err;
|
|
|
|
|
2019-03-21 23:01:56 +00:00
|
|
|
return fuse_internal_fsync(vp, td, waitfor, false);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_getattr_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct vattr *a_vap;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_getattr(struct vop_getattr_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
|
|
|
struct thread *td = curthread;
|
|
|
|
|
|
|
|
int err = 0;
|
|
|
|
int dataflags;
|
|
|
|
|
|
|
|
dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags;
|
|
|
|
|
|
|
|
/* Note that we are not bailing out on a dead file system just yet. */
|
|
|
|
|
|
|
|
if (!(dataflags & FSESS_INITED)) {
|
|
|
|
if (!vnode_isvroot(vp)) {
|
|
|
|
fdata_set_dead(fuse_get_mpdata(vnode_mount(vp)));
|
|
|
|
err = ENOTCONN;
|
|
|
|
return err;
|
|
|
|
} else {
|
|
|
|
goto fake;
|
|
|
|
}
|
|
|
|
}
|
2019-04-08 18:45:41 +00:00
|
|
|
err = fuse_internal_getattr(vp, vap, cred, td);
|
|
|
|
if (err == ENOTCONN && vnode_isvroot(vp)) {
|
|
|
|
/* see comment in fuse_vfsop_statfs() */
|
|
|
|
goto fake;
|
|
|
|
} else {
|
|
|
|
return err;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fake:
|
|
|
|
bzero(vap, sizeof(*vap));
|
|
|
|
vap->va_type = vnode_vtype(vp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_inactive_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct thread *a_td;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_inactive(struct vop_inactive_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
|
|
|
|
struct fuse_vnode_data *fvdat = VTOFUD(vp);
|
2019-04-01 14:23:43 +00:00
|
|
|
struct fuse_filehandle *fufh, *fufh_tmp;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-01 14:23:43 +00:00
|
|
|
int need_flush = 1;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-01 14:23:43 +00:00
|
|
|
LIST_FOREACH_SAFE(fufh, &fvdat->handles, next, fufh_tmp) {
|
|
|
|
if (need_flush && vp->v_type == VREG) {
|
|
|
|
if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
|
2019-04-01 20:42:15 +00:00
|
|
|
fuse_vnode_savesize(vp, NULL, 0);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
2019-04-01 14:23:43 +00:00
|
|
|
if (fuse_data_cache_invalidate ||
|
|
|
|
(fvdat->flag & FN_REVOKED) != 0)
|
|
|
|
fuse_io_invalbuf(vp, td);
|
|
|
|
else
|
|
|
|
fuse_io_flushbuf(vp, MNT_WAIT, td);
|
|
|
|
need_flush = 0;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
2019-04-01 14:23:43 +00:00
|
|
|
fuse_filehandle_close(vp, fufh, td, NULL);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((fvdat->flag & FN_REVOKED) != 0 && fuse_reclaim_revoked) {
|
|
|
|
vrecycle(vp);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_link_args {
|
|
|
|
struct vnode *a_tdvp;
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_link(struct vop_link_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct vnode *tdvp = ap->a_tdvp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
|
|
|
|
struct vattr *vap = VTOVA(vp);
|
|
|
|
|
|
|
|
struct fuse_dispatcher fdi;
|
|
|
|
struct fuse_entry_out *feo;
|
|
|
|
struct fuse_link_in fli;
|
|
|
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
if (vnode_mount(tdvp) != vnode_mount(vp)) {
|
|
|
|
return EXDEV;
|
|
|
|
}
|
FUSE: Respect userspace FS "do-not-cache" of file attributes
The FUSE protocol demands that kernel implementations cache user filesystem
file attributes (vattr data) for a maximum period of time in the range of
[0, ULONG_MAX] seconds. In practice, typical requests are for 0, 1, or 10
seconds; or "a long time" to represent indefinite caching.
Historically, FreeBSD FUSE has ignored this client directive entirely. This
works fine for local-only filesystems, but causes consistency issues with
multi-writer network filesystems.
For now, respect 0 second cache TTLs and do not cache such metadata.
Non-zero metadata caching TTLs in the range [0.000000001, ULONG_MAX] seconds
are still cached indefinitely, because it is unclear how a userspace
filesystem could do anything sensible with those semantics even if
implemented.
In the future, as an optimization, we should implement notify_inval_entry,
etc, which provide userspace filesystems a way of evicting the kernel cache.
One potentially bogus access to invalid cached attribute data was left in
fuse_io_strategy. It is restricted behind the undocumented and non-default
"vfs.fuse.fix_broken_io" sysctl or "brokenio" mount option; maybe these are
deadcode and can be eliminated?
Some minor APIs changed to facilitate this:
1. Attribute cache validity is tracked in FUSE inodes ("fuse_vnode_data").
2. cache_attrs() respects the provided TTL and only caches in the FUSE
inode if TTL > 0. It also grows an "out" argument, which, if non-NULL,
stores the translated fuse_attr (even if not suitable for caching).
3. FUSE VTOVA(vp) returns NULL if the vnode's cache is invalid, to help
avoid programming mistakes.
4. A VOP_LINK check for potential nlink overflow prior to invoking the FUSE
link op was weakened (only performed when we have a valid attr cache). The
check is racy in a multi-writer network filesystem anyway -- classic TOCTOU.
We have to trust any userspace filesystem that rejects local caching to
account for it correctly.
PR: 230258 (inspired by; does not fix)
2019-02-15 22:49:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a seatbelt check to protect naive userspace filesystems from
|
|
|
|
* themselves and the limitations of the FUSE IPC protocol. If a
|
|
|
|
* filesystem does not allow attribute caching, assume it is capable of
|
|
|
|
* validating that nlink does not overflow.
|
|
|
|
*/
|
|
|
|
if (vap != NULL && vap->va_nlink >= FUSE_LINK_MAX)
|
2012-10-13 23:54:26 +00:00
|
|
|
return EMLINK;
|
|
|
|
fli.oldnodeid = VTOI(vp);
|
|
|
|
|
|
|
|
fdisp_init(&fdi, 0);
|
|
|
|
fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp,
|
|
|
|
FUSE_LINK, &fli, sizeof(fli), &fdi);
|
|
|
|
if ((err = fdisp_wait_answ(&fdi))) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
feo = fdi.answ;
|
|
|
|
|
|
|
|
err = fuse_internal_checkentry(feo, vnode_vtype(vp));
|
|
|
|
out:
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-04-11 17:20:15 +00:00
|
|
|
struct fuse_lookup_alloc_arg {
|
|
|
|
struct fuse_entry_out *feo;
|
|
|
|
struct componentname *cnp;
|
|
|
|
uint64_t nid;
|
|
|
|
enum vtype vtyp;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Callback for vn_get_ino */
|
|
|
|
static int
|
|
|
|
fuse_lookup_alloc(struct mount *mp, void *arg, int lkflags, struct vnode **vpp)
|
|
|
|
{
|
|
|
|
struct fuse_lookup_alloc_arg *flaa = arg;
|
|
|
|
|
|
|
|
return fuse_vnode_get(mp, flaa->feo, flaa->nid, NULL, vpp, flaa->cnp,
|
|
|
|
flaa->vtyp);
|
|
|
|
}
|
|
|
|
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE_DEFINE3(fusefs, , vnops, cache_lookup,
|
2019-04-09 17:23:34 +00:00
|
|
|
"int", "struct timespec*", "struct timespec*");
|
2012-10-13 23:54:26 +00:00
|
|
|
/*
|
|
|
|
struct vnop_lookup_args {
|
|
|
|
struct vnodeop_desc *a_desc;
|
|
|
|
struct vnode *a_dvp;
|
|
|
|
struct vnode **a_vpp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
fuse_vnop_lookup(struct vop_lookup_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct thread *td = cnp->cn_thread;
|
|
|
|
struct ucred *cred = cnp->cn_cred;
|
|
|
|
|
|
|
|
int nameiop = cnp->cn_nameiop;
|
|
|
|
int flags = cnp->cn_flags;
|
|
|
|
int wantparent = flags & (LOCKPARENT | WANTPARENT);
|
|
|
|
int islastcn = flags & ISLASTCN;
|
|
|
|
struct mount *mp = vnode_mount(dvp);
|
|
|
|
|
|
|
|
int err = 0;
|
|
|
|
int lookup_err = 0;
|
|
|
|
struct vnode *vp = NULL;
|
|
|
|
|
|
|
|
struct fuse_dispatcher fdi;
|
2019-04-11 05:11:02 +00:00
|
|
|
bool did_lookup = false;
|
2019-04-09 21:22:02 +00:00
|
|
|
struct fuse_entry_out *feo = NULL;
|
2019-04-11 05:11:02 +00:00
|
|
|
enum vtype vtyp; /* vnode type of target */
|
|
|
|
off_t filesize; /* filesize of target */
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
uint64_t nid;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(dvp)) {
|
|
|
|
*vpp = NULL;
|
|
|
|
return ENXIO;
|
|
|
|
}
|
2019-04-10 21:46:59 +00:00
|
|
|
if (!vnode_isdir(dvp))
|
2012-10-13 23:54:26 +00:00
|
|
|
return ENOTDIR;
|
2019-04-10 21:46:59 +00:00
|
|
|
|
|
|
|
if (islastcn && vfs_isrdonly(mp) && (nameiop != LOOKUP))
|
2012-10-13 23:54:26 +00:00
|
|
|
return EROFS;
|
|
|
|
|
2019-04-10 21:46:59 +00:00
|
|
|
if ((err = fuse_internal_access(dvp, VEXEC, td, cred)))
|
|
|
|
return err;
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
if (flags & ISDOTDOT) {
|
|
|
|
nid = VTOFUD(dvp)->parent_nid;
|
2019-04-11 05:11:02 +00:00
|
|
|
if (nid == 0)
|
2012-10-13 23:54:26 +00:00
|
|
|
return ENOENT;
|
2019-04-11 05:11:02 +00:00
|
|
|
/* .. is obviously a directory */
|
|
|
|
vtyp = VDIR;
|
|
|
|
filesize = 0;
|
2012-10-13 23:54:26 +00:00
|
|
|
} else if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') {
|
|
|
|
nid = VTOI(dvp);
|
2019-04-11 05:11:02 +00:00
|
|
|
/* . is obviously a directory */
|
|
|
|
vtyp = VDIR;
|
|
|
|
filesize = 0;
|
2019-04-09 17:23:34 +00:00
|
|
|
} else {
|
2019-04-09 00:47:38 +00:00
|
|
|
struct timespec now, timeout;
|
|
|
|
|
|
|
|
err = cache_lookup(dvp, vpp, cnp, &timeout, NULL);
|
2019-04-09 17:23:34 +00:00
|
|
|
getnanouptime(&now);
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE3(fusefs, , vnops, cache_lookup, err, &timeout, &now);
|
2019-04-09 00:47:38 +00:00
|
|
|
switch (err) {
|
|
|
|
case -1: /* positive match */
|
2019-04-09 17:23:34 +00:00
|
|
|
if (timespeccmp(&timeout, &now, >)) {
|
2019-04-09 00:47:38 +00:00
|
|
|
atomic_add_acq_long(&fuse_lookup_cache_hits, 1);
|
|
|
|
} else {
|
|
|
|
/* Cache timeout */
|
|
|
|
atomic_add_acq_long(&fuse_lookup_cache_misses,
|
|
|
|
1);
|
2019-04-11 21:13:54 +00:00
|
|
|
cache_purge(*vpp);
|
2019-04-09 17:23:34 +00:00
|
|
|
if (dvp != *vpp)
|
|
|
|
vput(*vpp);
|
|
|
|
else
|
|
|
|
vrele(*vpp);
|
|
|
|
*vpp = NULL;
|
2019-04-09 00:47:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case 0: /* no match in cache */
|
|
|
|
atomic_add_acq_long(&fuse_lookup_cache_misses, 1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENOENT: /* negative match */
|
|
|
|
getnanouptime(&now);
|
2019-04-09 17:23:34 +00:00
|
|
|
if (timespeccmp(&timeout, &now, <=)) {
|
2019-04-09 00:47:38 +00:00
|
|
|
/* Cache timeout */
|
2019-04-09 17:23:34 +00:00
|
|
|
cache_purge_negative(dvp);
|
2019-04-09 00:47:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
|
|
return err;
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-11 05:11:02 +00:00
|
|
|
nid = VTOI(dvp);
|
|
|
|
fdisp_init(&fdi, cnp->cn_namelen + 1);
|
|
|
|
fdisp_make(&fdi, FUSE_LOOKUP, mp, nid, td, cred);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
((char *)fdi.indata)[cnp->cn_namelen] = '\0';
|
2019-04-11 05:11:02 +00:00
|
|
|
lookup_err = fdisp_wait_answ(&fdi);
|
|
|
|
did_lookup = true;
|
|
|
|
|
|
|
|
if (!lookup_err) {
|
|
|
|
/* lookup call succeeded */
|
|
|
|
nid = ((struct fuse_entry_out *)fdi.answ)->nodeid;
|
|
|
|
feo = (struct fuse_entry_out *)fdi.answ;
|
|
|
|
if (nid == 0) {
|
|
|
|
/* zero nodeid means ENOENT and cache it */
|
|
|
|
struct timespec timeout;
|
|
|
|
|
|
|
|
fdi.answ_stat = ENOENT;
|
|
|
|
lookup_err = ENOENT;
|
|
|
|
if (cnp->cn_flags & MAKEENTRY) {
|
|
|
|
fuse_validity_2_timespec(feo, &timeout);
|
|
|
|
cache_enter_time(dvp, *vpp, cnp,
|
|
|
|
&timeout, NULL);
|
|
|
|
}
|
|
|
|
} else if (nid == FUSE_ROOT_ID) {
|
|
|
|
lookup_err = EINVAL;
|
2019-04-09 21:22:02 +00:00
|
|
|
}
|
2019-04-11 05:11:02 +00:00
|
|
|
vtyp = IFTOVT(feo->attr.mode);
|
|
|
|
filesize = feo->attr.size;
|
|
|
|
}
|
|
|
|
if (lookup_err && (!fdi.answ_stat || lookup_err != ENOENT)) {
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return lookup_err;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* lookup_err, if non-zero, must be ENOENT at this point */
|
|
|
|
|
|
|
|
if (lookup_err) {
|
2019-04-10 17:31:00 +00:00
|
|
|
/* Entry not found */
|
|
|
|
if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
|
2019-04-10 21:10:21 +00:00
|
|
|
err = fuse_internal_access(dvp, VWRITE, td, cred);
|
2019-04-11 21:00:40 +00:00
|
|
|
if (!err) {
|
|
|
|
/*
|
|
|
|
* Set the SAVENAME flag to hold onto the
|
|
|
|
* pathname for use later in VOP_CREATE or
|
|
|
|
* VOP_RENAME.
|
|
|
|
*/
|
|
|
|
cnp->cn_flags |= SAVENAME;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-11 21:00:40 +00:00
|
|
|
err = EJUSTRETURN;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = ENOENT;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-04-10 17:31:00 +00:00
|
|
|
/* Entry was found */
|
2012-10-13 23:54:26 +00:00
|
|
|
if (flags & ISDOTDOT) {
|
2019-04-11 17:20:15 +00:00
|
|
|
struct fuse_lookup_alloc_arg flaa;
|
|
|
|
|
|
|
|
flaa.nid = nid;
|
|
|
|
flaa.feo = feo;
|
|
|
|
flaa.cnp = cnp;
|
|
|
|
flaa.vtyp = vtyp;
|
|
|
|
err = vn_vget_ino_gen(dvp, fuse_lookup_alloc, &flaa, 0,
|
|
|
|
&vp);
|
2012-10-13 23:54:26 +00:00
|
|
|
*vpp = vp;
|
|
|
|
} else if (nid == VTOI(dvp)) {
|
|
|
|
vref(dvp);
|
|
|
|
*vpp = dvp;
|
|
|
|
} else {
|
2019-02-15 22:55:13 +00:00
|
|
|
struct fuse_vnode_data *fvdat;
|
|
|
|
|
2019-02-15 22:50:31 +00:00
|
|
|
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
|
2019-04-11 05:11:02 +00:00
|
|
|
&vp, cnp, vtyp);
|
2019-04-11 21:00:40 +00:00
|
|
|
if (err)
|
2012-10-13 23:54:26 +00:00
|
|
|
goto out;
|
2019-04-11 21:00:40 +00:00
|
|
|
*vpp = vp;
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
fuse_vnode_setparent(vp, dvp);
|
2019-02-15 22:55:13 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In the case where we are looking up a FUSE node
|
|
|
|
* represented by an existing cached vnode, and the
|
|
|
|
* true size reported by FUSE_LOOKUP doesn't match
|
|
|
|
* the vnode's cached size, fix the vnode cache to
|
|
|
|
* match the real object size.
|
|
|
|
*
|
2019-04-11 21:00:40 +00:00
|
|
|
* We can get here:
|
|
|
|
* * following attribute cache expiration, or
|
|
|
|
* * due a bug in the daemon, or
|
|
|
|
* * the first time that we looked up the file.
|
2019-02-15 22:55:13 +00:00
|
|
|
*/
|
|
|
|
fvdat = VTOFUD(vp);
|
|
|
|
if (vnode_isreg(vp) &&
|
2019-04-11 05:11:02 +00:00
|
|
|
filesize != fvdat->filesize) {
|
2019-02-15 22:55:13 +00:00
|
|
|
/*
|
|
|
|
* The FN_SIZECHANGE flag reflects a dirty
|
|
|
|
* append. If userspace lets us know our cache
|
|
|
|
* is invalid, that write was lost. (Dirty
|
|
|
|
* writes that do not cause append are also
|
|
|
|
* lost, but we don't detect them here.)
|
|
|
|
*
|
|
|
|
* XXX: Maybe disable WB caching on this mount.
|
|
|
|
*/
|
|
|
|
if (fvdat->flag & FN_SIZECHANGE)
|
|
|
|
printf("%s: WB cache incoherent on "
|
|
|
|
"%s!\n", __func__,
|
|
|
|
vnode_mount(vp)->mnt_stat.f_mntonname);
|
|
|
|
|
2019-04-11 05:11:02 +00:00
|
|
|
(void)fuse_vnode_setsize(vp, cred, filesize);
|
2019-02-15 22:55:13 +00:00
|
|
|
fvdat->flag &= ~FN_SIZECHANGE;
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-11 21:00:40 +00:00
|
|
|
MPASS(feo != NULL);
|
2019-04-11 05:11:02 +00:00
|
|
|
fuse_internal_cache_attrs(*vpp, &feo->attr,
|
|
|
|
feo->attr_valid, feo->attr_valid_nsec, NULL);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-11 21:00:40 +00:00
|
|
|
if ((nameiop == DELETE || nameiop == RENAME) &&
|
|
|
|
islastcn)
|
|
|
|
{
|
|
|
|
struct vattr dvattr;
|
|
|
|
|
|
|
|
err = fuse_internal_access(dvp, VWRITE, td,
|
|
|
|
cred);
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
/*
|
|
|
|
* if the parent's sticky bit is set, check
|
|
|
|
* whether we're allowed to remove the file.
|
|
|
|
* Need to figure out the vnode locking to make
|
|
|
|
* this work.
|
|
|
|
*/
|
|
|
|
fuse_internal_getattr(dvp, &dvattr, cred, td);
|
|
|
|
if ((dvattr.va_mode & S_ISTXT) &&
|
|
|
|
fuse_internal_access(dvp, VADMIN, td,
|
|
|
|
cred) &&
|
|
|
|
fuse_internal_access(*vpp, VADMIN, td,
|
|
|
|
cred)) {
|
|
|
|
err = EPERM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-11 21:00:40 +00:00
|
|
|
if (islastcn && (
|
|
|
|
(nameiop == DELETE) ||
|
|
|
|
(nameiop == RENAME && wantparent))) {
|
|
|
|
cnp->cn_flags |= SAVENAME;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
2019-04-11 21:00:40 +00:00
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-11 21:00:40 +00:00
|
|
|
out:
|
|
|
|
if (err) {
|
|
|
|
if (vp != NULL && dvp != vp)
|
|
|
|
vput(vp);
|
|
|
|
else if (vp != NULL)
|
|
|
|
vrele(vp);
|
|
|
|
*vpp = NULL;
|
|
|
|
}
|
2019-04-11 05:11:02 +00:00
|
|
|
if (did_lookup)
|
|
|
|
fdisp_destroy(&fdi);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_mkdir_args {
|
|
|
|
struct vnode *a_dvp;
|
|
|
|
struct vnode **a_vpp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
struct vattr *a_vap;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_mkdir(struct vop_mkdir_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
|
|
|
|
struct fuse_mkdir_in fmdi;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(dvp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
|
|
|
|
2012-11-08 00:32:49 +00:00
|
|
|
return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
|
|
|
|
sizeof(fmdi), VDIR));
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_mknod_args {
|
|
|
|
struct vnode *a_dvp;
|
|
|
|
struct vnode **a_vpp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
struct vattr *a_vap;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_mknod(struct vop_mknod_args *ap)
|
|
|
|
{
|
|
|
|
|
2019-03-22 19:08:48 +00:00
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct fuse_mknod_in fmni;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-03-22 19:08:48 +00:00
|
|
|
if (fuse_isdeadfs(dvp))
|
|
|
|
return ENXIO;
|
|
|
|
|
|
|
|
fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode);
|
|
|
|
fmni.rdev = vap->va_rdev;
|
|
|
|
return (fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni,
|
|
|
|
sizeof(fmni), vap->va_type));
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_open_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
int a_mode;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
|
|
|
int a_fdidx; / struct file *a_fp;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_open(struct vop_open_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
2019-04-02 18:09:40 +00:00
|
|
|
int a_mode = ap->a_mode;
|
2012-10-13 23:54:26 +00:00
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
2019-04-01 20:42:15 +00:00
|
|
|
pid_t pid = td->td_proc->p_pid;
|
2012-10-13 23:54:26 +00:00
|
|
|
struct fuse_vnode_data *fvdat;
|
|
|
|
|
2019-03-22 19:08:48 +00:00
|
|
|
if (fuse_isdeadfs(vp))
|
2012-10-13 23:54:26 +00:00
|
|
|
return ENXIO;
|
2019-03-22 19:08:48 +00:00
|
|
|
if (vp->v_type == VCHR || vp->v_type == VBLK || vp->v_type == VFIFO)
|
|
|
|
return (EOPNOTSUPP);
|
2019-04-02 18:09:40 +00:00
|
|
|
if ((a_mode & (FREAD | FWRITE | FEXEC)) == 0)
|
2019-03-06 22:56:49 +00:00
|
|
|
return EINVAL;
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
fvdat = VTOFUD(vp);
|
|
|
|
|
2019-04-02 18:09:40 +00:00
|
|
|
if (fuse_filehandle_validrw(vp, a_mode, cred, pid)) {
|
2019-03-30 00:57:07 +00:00
|
|
|
fuse_vnode_open(vp, 0, td);
|
2012-10-13 23:54:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-02 18:09:40 +00:00
|
|
|
return fuse_filehandle_open(vp, a_mode, NULL, td, cred);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
2017-12-19 19:09:06 +00:00
|
|
|
static int
|
|
|
|
fuse_vnop_pathconf(struct vop_pathconf_args *ap)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (ap->a_name) {
|
|
|
|
case _PC_FILESIZEBITS:
|
|
|
|
*ap->a_retval = 64;
|
|
|
|
return (0);
|
2017-12-19 19:51:36 +00:00
|
|
|
case _PC_NAME_MAX:
|
|
|
|
*ap->a_retval = NAME_MAX;
|
|
|
|
return (0);
|
|
|
|
case _PC_LINK_MAX:
|
2017-12-19 22:40:54 +00:00
|
|
|
*ap->a_retval = MIN(LONG_MAX, FUSE_LINK_MAX);
|
2017-12-19 19:51:36 +00:00
|
|
|
return (0);
|
2017-12-19 19:09:06 +00:00
|
|
|
case _PC_SYMLINK_MAX:
|
|
|
|
*ap->a_retval = MAXPATHLEN;
|
|
|
|
return (0);
|
|
|
|
case _PC_NO_TRUNC:
|
|
|
|
*ap->a_retval = 1;
|
|
|
|
return (0);
|
|
|
|
default:
|
|
|
|
return (vop_stdpathconf(ap));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
/*
|
|
|
|
struct vnop_read_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct uio *a_uio;
|
|
|
|
int a_ioflag;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_read(struct vop_read_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
|
|
|
int ioflag = ap->a_ioflag;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
2019-04-01 20:42:15 +00:00
|
|
|
pid_t pid = curthread->td_proc->p_pid;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
2015-03-02 19:04:27 +00:00
|
|
|
|
|
|
|
if (VTOFUD(vp)->flag & FN_DIRECTIO) {
|
|
|
|
ioflag |= IO_DIRECT;
|
|
|
|
}
|
|
|
|
|
2019-04-26 19:47:43 +00:00
|
|
|
return fuse_io_dispatch(vp, uio, ioflag, false, cred, pid);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_readdir_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct uio *a_uio;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
int *a_eofflag;
|
|
|
|
int *ncookies;
|
|
|
|
u_long **a_cookies;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_readdir(struct vop_readdir_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
|
|
|
struct fuse_filehandle *fufh = NULL;
|
|
|
|
struct fuse_iov cookediov;
|
|
|
|
int err = 0;
|
2019-04-01 20:42:15 +00:00
|
|
|
pid_t pid = curthread->td_proc->p_pid;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
if ( /* XXXIP ((uio_iovcnt(uio) > 1)) || */
|
|
|
|
(uio_resid(uio) < sizeof(struct dirent))) {
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-04-03 20:57:43 +00:00
|
|
|
err = fuse_filehandle_get_dir(vp, &fufh, cred, pid);
|
|
|
|
if (err)
|
2012-10-13 23:54:26 +00:00
|
|
|
return (err);
|
|
|
|
#define DIRCOOKEDSIZE FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + MAXNAMLEN + 1)
|
|
|
|
fiov_init(&cookediov, DIRCOOKEDSIZE);
|
|
|
|
|
|
|
|
err = fuse_internal_readdir(vp, uio, fufh, &cookediov);
|
|
|
|
|
|
|
|
fiov_teardown(&cookediov);
|
2019-04-01 14:23:43 +00:00
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_readlink_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct uio *a_uio;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_readlink(struct vop_readlink_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
|
|
|
|
|
|
|
struct fuse_dispatcher fdi;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
if (!vnode_islnk(vp)) {
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
fdisp_init(&fdi, 0);
|
|
|
|
err = fdisp_simple_putget_vp(&fdi, FUSE_READLINK, vp, curthread, cred);
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (((char *)fdi.answ)[0] == '/' &&
|
|
|
|
fuse_get_mpdata(vnode_mount(vp))->dataflags & FSESS_PUSH_SYMLINKS_IN) {
|
|
|
|
char *mpth = vnode_mount(vp)->mnt_stat.f_mntonname;
|
|
|
|
|
|
|
|
err = uiomove(mpth, strlen(mpth), uio);
|
|
|
|
}
|
|
|
|
if (!err) {
|
|
|
|
err = uiomove(fdi.answ, fdi.iosize, uio);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_reclaim_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct thread *a_td;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_reclaim(struct vop_reclaim_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
struct fuse_vnode_data *fvdat = VTOFUD(vp);
|
2019-04-01 14:23:43 +00:00
|
|
|
struct fuse_filehandle *fufh, *fufh_tmp;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (!fvdat) {
|
|
|
|
panic("FUSE: no vnode data during recycling");
|
|
|
|
}
|
2019-04-01 14:23:43 +00:00
|
|
|
LIST_FOREACH_SAFE(fufh, &fvdat->handles, next, fufh_tmp) {
|
|
|
|
printf("FUSE: vnode being reclaimed with open fufh "
|
2019-04-02 18:09:40 +00:00
|
|
|
"(type=%#x)", fufh->fufh_type);
|
2019-04-01 14:23:43 +00:00
|
|
|
fuse_filehandle_close(vp, fufh, td, NULL);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((!fuse_isdeadfs(vp)) && (fvdat->nlookup)) {
|
|
|
|
fuse_internal_forget_send(vnode_mount(vp), td, NULL, VTOI(vp),
|
|
|
|
fvdat->nlookup);
|
|
|
|
}
|
|
|
|
fuse_vnode_setparent(vp, NULL);
|
|
|
|
cache_purge(vp);
|
|
|
|
vfs_hash_remove(vp);
|
|
|
|
vnode_destroy_vobject(vp);
|
|
|
|
fuse_vnode_destroy(vp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_remove_args {
|
|
|
|
struct vnode *a_dvp;
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_remove(struct vop_remove_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
|
|
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
if (vnode_isdir(vp)) {
|
|
|
|
return EPERM;
|
|
|
|
}
|
|
|
|
cache_purge(vp);
|
|
|
|
|
|
|
|
err = fuse_internal_remove(dvp, vp, cnp, FUSE_UNLINK);
|
|
|
|
|
2012-11-08 00:32:49 +00:00
|
|
|
if (err == 0)
|
2012-10-13 23:54:26 +00:00
|
|
|
fuse_internal_vnode_disappear(vp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_rename_args {
|
|
|
|
struct vnode *a_fdvp;
|
|
|
|
struct vnode *a_fvp;
|
|
|
|
struct componentname *a_fcnp;
|
|
|
|
struct vnode *a_tdvp;
|
|
|
|
struct vnode *a_tvp;
|
|
|
|
struct componentname *a_tcnp;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_rename(struct vop_rename_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *fdvp = ap->a_fdvp;
|
|
|
|
struct vnode *fvp = ap->a_fvp;
|
|
|
|
struct componentname *fcnp = ap->a_fcnp;
|
|
|
|
struct vnode *tdvp = ap->a_tdvp;
|
|
|
|
struct vnode *tvp = ap->a_tvp;
|
|
|
|
struct componentname *tcnp = ap->a_tcnp;
|
|
|
|
struct fuse_data *data;
|
|
|
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(fdvp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
if (fvp->v_mount != tdvp->v_mount ||
|
|
|
|
(tvp && fvp->v_mount != tvp->v_mount)) {
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE2(fusefs, , vnops, trace, 1, "cross-device rename");
|
2012-10-13 23:54:26 +00:00
|
|
|
err = EXDEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
cache_purge(fvp);
|
|
|
|
|
|
|
|
/*
|
2019-02-20 02:49:26 +00:00
|
|
|
* FUSE library is expected to check if target directory is not
|
|
|
|
* under the source directory in the file system tree.
|
|
|
|
* Linux performs this check at VFS level.
|
|
|
|
*/
|
2012-10-13 23:54:26 +00:00
|
|
|
data = fuse_get_mpdata(vnode_mount(tdvp));
|
|
|
|
sx_xlock(&data->rename_lock);
|
|
|
|
err = fuse_internal_rename(fdvp, fcnp, tdvp, tcnp);
|
|
|
|
if (err == 0) {
|
2012-11-08 00:32:49 +00:00
|
|
|
if (tdvp != fdvp)
|
2012-10-13 23:54:26 +00:00
|
|
|
fuse_vnode_setparent(fvp, tdvp);
|
|
|
|
if (tvp != NULL)
|
|
|
|
fuse_vnode_setparent(tvp, NULL);
|
|
|
|
}
|
|
|
|
sx_unlock(&data->rename_lock);
|
|
|
|
|
|
|
|
if (tvp != NULL && tvp != fvp) {
|
|
|
|
cache_purge(tvp);
|
|
|
|
}
|
|
|
|
if (vnode_isdir(fvp)) {
|
|
|
|
if ((tvp != NULL) && vnode_isdir(tvp)) {
|
|
|
|
cache_purge(tdvp);
|
|
|
|
}
|
|
|
|
cache_purge(fdvp);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (tdvp == tvp) {
|
|
|
|
vrele(tdvp);
|
|
|
|
} else {
|
|
|
|
vput(tdvp);
|
|
|
|
}
|
|
|
|
if (tvp != NULL) {
|
|
|
|
vput(tvp);
|
|
|
|
}
|
|
|
|
vrele(fdvp);
|
|
|
|
vrele(fvp);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_rmdir_args {
|
|
|
|
struct vnode *a_dvp;
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
} *ap;
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_rmdir(struct vop_rmdir_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
if (VTOFUD(vp) == VTOFUD(dvp)) {
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
err = fuse_internal_remove(dvp, vp, ap->a_cnp, FUSE_RMDIR);
|
|
|
|
|
2012-11-08 00:32:49 +00:00
|
|
|
if (err == 0)
|
2012-10-13 23:54:26 +00:00
|
|
|
fuse_internal_vnode_disappear(vp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_setattr_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct vattr *a_vap;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_setattr(struct vop_setattr_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct vattr *vap = ap->a_vap;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
|
|
|
struct thread *td = curthread;
|
|
|
|
struct fuse_dispatcher fdi;
|
|
|
|
struct fuse_setattr_in *fsai;
|
2019-05-01 00:00:49 +00:00
|
|
|
struct mount *mp;
|
2019-04-01 20:42:15 +00:00
|
|
|
pid_t pid = td->td_proc->p_pid;
|
2019-05-01 00:00:49 +00:00
|
|
|
struct fuse_data *data;
|
|
|
|
int dataflags;
|
2012-10-13 23:54:26 +00:00
|
|
|
int err = 0;
|
|
|
|
enum vtype vtyp;
|
|
|
|
int sizechanged = 0;
|
|
|
|
uint64_t newsize = 0;
|
2019-04-10 17:31:00 +00:00
|
|
|
accmode_t accmode = 0;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-05-01 00:00:49 +00:00
|
|
|
mp = vnode_mount(vp);
|
|
|
|
data = fuse_get_mpdata(mp);
|
|
|
|
dataflags = data->dataflags;
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
fdisp_init(&fdi, sizeof(*fsai));
|
|
|
|
fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
|
|
|
|
fsai = fdi.indata;
|
|
|
|
fsai->valid = 0;
|
|
|
|
|
|
|
|
if (vap->va_uid != (uid_t)VNOVAL) {
|
2019-05-01 00:00:49 +00:00
|
|
|
if (dataflags & FSESS_DEFAULT_PERMISSIONS) {
|
|
|
|
/* Only root may change a file's owner */
|
|
|
|
err = priv_check_cred(cred, PRIV_VFS_CHOWN);
|
|
|
|
if (err)
|
2019-05-01 17:24:53 +00:00
|
|
|
goto out;
|
2019-05-01 00:00:49 +00:00
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
fsai->uid = vap->va_uid;
|
|
|
|
fsai->valid |= FATTR_UID;
|
2019-04-10 17:31:00 +00:00
|
|
|
accmode |= VADMIN;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
if (vap->va_gid != (gid_t)VNOVAL) {
|
2019-05-01 00:00:49 +00:00
|
|
|
if (dataflags & FSESS_DEFAULT_PERMISSIONS &&
|
|
|
|
!groupmember(vap->va_gid, cred))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Non-root users may only chgrp to one of their own
|
|
|
|
* groups
|
|
|
|
*/
|
|
|
|
err = priv_check_cred(cred, PRIV_VFS_CHOWN);
|
|
|
|
if (err)
|
2019-05-01 17:24:53 +00:00
|
|
|
goto out;
|
2019-05-01 00:00:49 +00:00
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
fsai->gid = vap->va_gid;
|
|
|
|
fsai->valid |= FATTR_GID;
|
2019-04-10 17:31:00 +00:00
|
|
|
accmode |= VADMIN;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
if (vap->va_size != VNOVAL) {
|
|
|
|
|
|
|
|
struct fuse_filehandle *fufh = NULL;
|
|
|
|
|
|
|
|
/*Truncate to a new value. */
|
2019-04-10 17:31:00 +00:00
|
|
|
fsai->size = vap->va_size;
|
2012-10-13 23:54:26 +00:00
|
|
|
sizechanged = 1;
|
|
|
|
newsize = vap->va_size;
|
|
|
|
fsai->valid |= FATTR_SIZE;
|
2019-04-10 17:31:00 +00:00
|
|
|
accmode |= VWRITE;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-04-03 19:59:45 +00:00
|
|
|
fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
|
2012-10-13 23:54:26 +00:00
|
|
|
if (fufh) {
|
|
|
|
fsai->fh = fufh->fh_id;
|
|
|
|
fsai->valid |= FATTR_FH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (vap->va_atime.tv_sec != VNOVAL) {
|
|
|
|
fsai->atime = vap->va_atime.tv_sec;
|
|
|
|
fsai->atimensec = vap->va_atime.tv_nsec;
|
|
|
|
fsai->valid |= FATTR_ATIME;
|
2019-04-10 17:31:00 +00:00
|
|
|
accmode |= VADMIN;
|
|
|
|
/*
|
|
|
|
* TODO: only require VWRITE if UTIMENS_NULL is set. PR 237181
|
|
|
|
*/
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
if (vap->va_mtime.tv_sec != VNOVAL) {
|
|
|
|
fsai->mtime = vap->va_mtime.tv_sec;
|
|
|
|
fsai->mtimensec = vap->va_mtime.tv_nsec;
|
|
|
|
fsai->valid |= FATTR_MTIME;
|
2019-04-10 17:31:00 +00:00
|
|
|
accmode |= VADMIN;
|
|
|
|
/*
|
|
|
|
* TODO: only require VWRITE if UTIMENS_NULL is set. PR 237181
|
|
|
|
*/
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
if (vap->va_mode != (mode_t)VNOVAL) {
|
2019-05-04 16:27:58 +00:00
|
|
|
/* Only root may set the sticky bit on non-directories */
|
|
|
|
if (dataflags & FSESS_DEFAULT_PERMISSIONS &&
|
|
|
|
vp->v_type != VDIR && (vap->va_mode & S_ISTXT))
|
|
|
|
{
|
|
|
|
if (priv_check_cred(cred, PRIV_VFS_STICKYFILE)) {
|
|
|
|
err = EFTYPE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
fsai->mode = vap->va_mode & ALLPERMS;
|
|
|
|
fsai->valid |= FATTR_MODE;
|
2019-04-10 17:31:00 +00:00
|
|
|
accmode |= VADMIN;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
if (!fsai->valid) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vtyp = vnode_vtype(vp);
|
|
|
|
|
|
|
|
if (fsai->valid & FATTR_SIZE && vtyp == VDIR) {
|
|
|
|
err = EISDIR;
|
|
|
|
goto out;
|
|
|
|
}
|
2019-05-06 15:20:18 +00:00
|
|
|
if (vfs_isrdonly(vnode_mount(vp))) {
|
2012-10-13 23:54:26 +00:00
|
|
|
err = EROFS;
|
|
|
|
goto out;
|
|
|
|
}
|
2019-04-10 21:10:21 +00:00
|
|
|
err = fuse_internal_access(vp, accmode, td, cred);
|
2019-04-10 17:31:00 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2012-11-08 00:32:49 +00:00
|
|
|
if ((err = fdisp_wait_answ(&fdi)))
|
2012-10-13 23:54:26 +00:00
|
|
|
goto out;
|
|
|
|
vtyp = IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode);
|
|
|
|
|
|
|
|
if (vnode_vtype(vp) != vtyp) {
|
|
|
|
if (vnode_vtype(vp) == VNON && vtyp != VNON) {
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE2(fusefs, , vnops, trace, 1, "FUSE: Dang! "
|
2019-02-28 19:27:54 +00:00
|
|
|
"vnode_vtype is VNON and vtype isn't.");
|
2012-10-13 23:54:26 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* STALE vnode, ditch
|
|
|
|
*
|
2019-02-28 19:27:54 +00:00
|
|
|
* The vnode has changed its type "behind our back".
|
|
|
|
* There's nothing really we can do, so let us just
|
|
|
|
* force an internal revocation and tell the caller to
|
|
|
|
* try again, if interested.
|
2012-10-13 23:54:26 +00:00
|
|
|
*/
|
|
|
|
fuse_internal_vnode_disappear(vp);
|
|
|
|
err = EAGAIN;
|
|
|
|
}
|
|
|
|
}
|
2019-03-19 17:49:15 +00:00
|
|
|
if (err == 0) {
|
|
|
|
struct fuse_attr_out *fao = (struct fuse_attr_out*)fdi.answ;
|
|
|
|
fuse_internal_cache_attrs(vp, &fao->attr, fao->attr_valid,
|
|
|
|
fao->attr_valid_nsec, NULL);
|
|
|
|
}
|
2019-02-15 22:51:09 +00:00
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
out:
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
if (!err && sizechanged) {
|
|
|
|
fuse_vnode_setsize(vp, cred, newsize);
|
|
|
|
VTOFUD(vp)->flag &= ~FN_SIZECHANGE;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_strategy_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct buf *a_bp;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_strategy(struct vop_strategy_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct buf *bp = ap->a_bp;
|
|
|
|
|
|
|
|
if (!vp || fuse_isdeadfs(vp)) {
|
|
|
|
bp->b_ioflags |= BIO_ERROR;
|
|
|
|
bp->b_error = ENXIO;
|
|
|
|
bufdone(bp);
|
2019-03-28 21:57:42 +00:00
|
|
|
return 0;
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
2019-03-27 16:45:30 +00:00
|
|
|
if (bp->b_iocmd == BIO_WRITE) {
|
|
|
|
int err;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2019-03-27 16:45:30 +00:00
|
|
|
err = fuse_vnode_refreshsize(vp, NOCRED);
|
|
|
|
if (err) {
|
|
|
|
bp->b_ioflags |= BIO_ERROR;
|
|
|
|
bp->b_error = err;
|
2019-03-28 21:57:42 +00:00
|
|
|
bufdone(bp);
|
2019-03-27 16:45:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
/*
|
2019-03-27 16:45:30 +00:00
|
|
|
* VOP_STRATEGY always returns zero and signals error via bp->b_ioflags.
|
|
|
|
* fuse_io_strategy sets bp's error fields
|
2012-10-13 23:54:26 +00:00
|
|
|
*/
|
2019-03-27 16:45:30 +00:00
|
|
|
(void)fuse_io_strategy(vp, bp);
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_symlink_args {
|
|
|
|
struct vnode *a_dvp;
|
|
|
|
struct vnode **a_vpp;
|
|
|
|
struct componentname *a_cnp;
|
|
|
|
struct vattr *a_vap;
|
|
|
|
char *a_target;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_symlink(struct vop_symlink_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *dvp = ap->a_dvp;
|
|
|
|
struct vnode **vpp = ap->a_vpp;
|
|
|
|
struct componentname *cnp = ap->a_cnp;
|
2018-11-02 14:42:36 +00:00
|
|
|
const char *target = ap->a_target;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
struct fuse_dispatcher fdi;
|
|
|
|
|
|
|
|
int err;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(dvp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
|
|
|
/*
|
2019-02-20 02:49:26 +00:00
|
|
|
* Unlike the other creator type calls, here we have to create a message
|
|
|
|
* where the name of the new entry comes first, and the data describing
|
|
|
|
* the entry comes second.
|
|
|
|
* Hence we can't rely on our handy fuse_internal_newentry() routine,
|
|
|
|
* but put together the message manually and just call the core part.
|
|
|
|
*/
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
len = strlen(target) + 1;
|
|
|
|
fdisp_init(&fdi, len + cnp->cn_namelen + 1);
|
|
|
|
fdisp_make_vp(&fdi, FUSE_SYMLINK, dvp, curthread, NULL);
|
|
|
|
|
|
|
|
memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
|
|
|
|
((char *)fdi.indata)[cnp->cn_namelen] = '\0';
|
|
|
|
memcpy((char *)fdi.indata + cnp->cn_namelen + 1, target, len);
|
|
|
|
|
|
|
|
err = fuse_internal_newentry_core(dvp, vpp, cnp, VLNK, &fdi);
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_write_args {
|
|
|
|
struct vnode *a_vp;
|
|
|
|
struct uio *a_uio;
|
|
|
|
int a_ioflag;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_write(struct vop_write_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
|
|
|
int ioflag = ap->a_ioflag;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
2019-04-01 20:42:15 +00:00
|
|
|
pid_t pid = curthread->td_proc->p_pid;
|
2019-03-27 16:45:30 +00:00
|
|
|
int err;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp)) {
|
|
|
|
return ENXIO;
|
|
|
|
}
|
2019-03-27 16:45:30 +00:00
|
|
|
err = fuse_vnode_refreshsize(vp, cred);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2015-03-02 19:04:27 +00:00
|
|
|
if (VTOFUD(vp)->flag & FN_DIRECTIO) {
|
|
|
|
ioflag |= IO_DIRECT;
|
|
|
|
}
|
|
|
|
|
2019-04-26 19:47:43 +00:00
|
|
|
return fuse_io_dispatch(vp, uio, ioflag, false, cred, pid);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE_DEFINE1(fusefs, , vnops, vnop_getpages_error, "int");
|
2012-10-13 23:54:26 +00:00
|
|
|
/*
|
|
|
|
struct vnop_getpages_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vnode *a_vp;
|
|
|
|
vm_page_t *a_m;
|
|
|
|
int a_count;
|
|
|
|
int a_reqpage;
|
2012-10-13 23:54:26 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_getpages(struct vop_getpages_args *ap)
|
|
|
|
{
|
|
|
|
int i, error, nextoff, size, toff, count, npages;
|
|
|
|
struct uio uio;
|
|
|
|
struct iovec iov;
|
|
|
|
vm_offset_t kva;
|
|
|
|
struct buf *bp;
|
|
|
|
struct vnode *vp;
|
|
|
|
struct thread *td;
|
|
|
|
struct ucred *cred;
|
|
|
|
vm_page_t *pages;
|
2019-04-01 20:42:15 +00:00
|
|
|
pid_t pid = curthread->td_proc->p_pid;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
vp = ap->a_vp;
|
|
|
|
KASSERT(vp->v_object, ("objectless vp passed to getpages"));
|
|
|
|
td = curthread; /* XXX */
|
|
|
|
cred = curthread->td_ucred; /* XXX */
|
|
|
|
pages = ap->a_m;
|
2015-12-16 23:48:50 +00:00
|
|
|
npages = ap->a_count;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (!fsess_opt_mmap(vnode_mount(vp))) {
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE2(fusefs, , vnops, trace, 1,
|
2019-02-28 19:27:54 +00:00
|
|
|
"called on non-cacheable vnode??\n");
|
2012-10-13 23:54:26 +00:00
|
|
|
return (VM_PAGER_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
A change to KPI of vm_pager_get_pages() and underlying VOP_GETPAGES().
o With new KPI consumers can request contiguous ranges of pages, and
unlike before, all pages will be kept busied on return, like it was
done before with the 'reqpage' only. Now the reqpage goes away. With
new interface it is easier to implement code protected from race
conditions.
Such arrayed requests for now should be preceeded by a call to
vm_pager_haspage() to make sure that request is possible. This
could be improved later, making vm_pager_haspage() obsolete.
Strenghtening the promises on the business of the array of pages
allows us to remove such hacks as swp_pager_free_nrpage() and
vm_pager_free_nonreq().
o New KPI accepts two integer pointers that may optionally point at
values for read ahead and read behind, that a pager may do, if it
can. These pages are completely owned by pager, and not controlled
by the caller.
This shifts the UFS-specific readahead logic from vm_fault.c, which
should be file system agnostic, into vnode_pager.c. It also removes
one VOP_BMAP() request per hard fault.
Discussed with: kib, alc, jeff, scottl
Sponsored by: Nginx, Inc.
Sponsored by: Netflix
2015-12-16 21:30:45 +00:00
|
|
|
* If the last page is partially valid, just return it and allow
|
|
|
|
* the pager to zero-out the blanks. Partially valid pages can
|
|
|
|
* only occur at the file EOF.
|
|
|
|
*
|
|
|
|
* XXXGL: is that true for FUSE, which is a local filesystem,
|
|
|
|
* but still somewhat disconnected from the kernel?
|
2012-10-13 23:54:26 +00:00
|
|
|
*/
|
2013-03-09 02:32:23 +00:00
|
|
|
VM_OBJECT_WLOCK(vp->v_object);
|
2015-12-16 23:48:50 +00:00
|
|
|
if (pages[npages - 1]->valid != 0 && --npages == 0)
|
|
|
|
goto out;
|
2013-03-09 02:32:23 +00:00
|
|
|
VM_OBJECT_WUNLOCK(vp->v_object);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We use only the kva address for the buffer, but this is extremely
|
2016-04-29 20:51:24 +00:00
|
|
|
* convenient and fast.
|
2012-10-13 23:54:26 +00:00
|
|
|
*/
|
Allocate pager bufs from UMA instead of 80-ish mutex protected linked list.
o In vm_pager_bufferinit() create pbuf_zone and start accounting on how many
pbufs are we going to have set.
In various subsystems that are going to utilize pbufs create private zones
via call to pbuf_zsecond_create(). The latter calls uma_zsecond_create(),
and sets a limit on created zone. After startup preallocate pbufs according
to requirements of all pbuf zones.
Subsystems that used to have a private limit with old allocator now have
private pbuf zones: md(4), fusefs, NFS client, smbfs, VFS cluster, FFS,
swap, vnode pager.
The following subsystems use shared pbuf zone: cam(4), nvme(4), physio(9),
aio(4). They should have their private limits, but changing that is out of
scope of this commit.
o Fetch tunable value of kern.nswbuf from init_param2() and while here move
NSWBUF_MIN to opt_param.h and eliminate opt_swap.h, that was holding only
this option.
Default values aren't touched by this commit, but they probably should be
reviewed wrt to modern hardware.
This change removes a tight bottleneck from sendfile(2) operation, that
uses pbufs in vnode pager. Other pagers also would benefit from faster
allocation.
Together with: gallatin
Tested by: pho
2019-01-15 01:02:16 +00:00
|
|
|
bp = uma_zalloc(fuse_pbuf_zone, M_WAITOK);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
kva = (vm_offset_t)bp->b_data;
|
|
|
|
pmap_qenter(kva, pages, npages);
|
- Remove 'struct vmmeter' from 'struct pcpu', leaving only global vmmeter
in place. To do per-cpu stats, convert all fields that previously were
maintained in the vmmeters that sit in pcpus to counter(9).
- Since some vmmeter stats may be touched at very early stages of boot,
before we have set up UMA and we can do counter_u64_alloc(), provide an
early counter mechanism:
o Leave one spare uint64_t in struct pcpu, named pc_early_dummy_counter.
o Point counter(9) fields of vmmeter to pcpu[0].pc_early_dummy_counter,
so that at early stages of boot, before counters are allocated we already
point to a counter that can be safely written to.
o For sparc64 that required a whole dummy pcpu[MAXCPU] array.
Further related changes:
- Don't include vmmeter.h into pcpu.h.
- vm.stats.vm.v_swappgsout and vm.stats.vm.v_swappgsin changed to 64-bit,
to match kernel representation.
- struct vmmeter hidden under _KERNEL, and only vmstat(1) is an exclusion.
This is based on benno@'s 4-year old patch:
https://lists.freebsd.org/pipermail/freebsd-arch/2013-July/014471.html
Reviewed by: kib, gallatin, marius, lidl
Differential Revision: https://reviews.freebsd.org/D10156
2017-04-17 17:34:47 +00:00
|
|
|
VM_CNT_INC(v_vnodein);
|
|
|
|
VM_CNT_ADD(v_vnodepgsin, npages);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
2015-12-16 23:48:50 +00:00
|
|
|
count = npages << PAGE_SHIFT;
|
2012-10-13 23:54:26 +00:00
|
|
|
iov.iov_base = (caddr_t)kva;
|
|
|
|
iov.iov_len = count;
|
|
|
|
uio.uio_iov = &iov;
|
|
|
|
uio.uio_iovcnt = 1;
|
|
|
|
uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
|
|
|
|
uio.uio_resid = count;
|
|
|
|
uio.uio_segflg = UIO_SYSSPACE;
|
|
|
|
uio.uio_rw = UIO_READ;
|
|
|
|
uio.uio_td = td;
|
|
|
|
|
2019-04-26 19:47:43 +00:00
|
|
|
error = fuse_io_dispatch(vp, &uio, IO_DIRECT, true, cred, pid);
|
2012-10-13 23:54:26 +00:00
|
|
|
pmap_qremove(kva, npages);
|
|
|
|
|
Allocate pager bufs from UMA instead of 80-ish mutex protected linked list.
o In vm_pager_bufferinit() create pbuf_zone and start accounting on how many
pbufs are we going to have set.
In various subsystems that are going to utilize pbufs create private zones
via call to pbuf_zsecond_create(). The latter calls uma_zsecond_create(),
and sets a limit on created zone. After startup preallocate pbufs according
to requirements of all pbuf zones.
Subsystems that used to have a private limit with old allocator now have
private pbuf zones: md(4), fusefs, NFS client, smbfs, VFS cluster, FFS,
swap, vnode pager.
The following subsystems use shared pbuf zone: cam(4), nvme(4), physio(9),
aio(4). They should have their private limits, but changing that is out of
scope of this commit.
o Fetch tunable value of kern.nswbuf from init_param2() and while here move
NSWBUF_MIN to opt_param.h and eliminate opt_swap.h, that was holding only
this option.
Default values aren't touched by this commit, but they probably should be
reviewed wrt to modern hardware.
This change removes a tight bottleneck from sendfile(2) operation, that
uses pbufs in vnode pager. Other pagers also would benefit from faster
allocation.
Together with: gallatin
Tested by: pho
2019-01-15 01:02:16 +00:00
|
|
|
uma_zfree(fuse_pbuf_zone, bp);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (error && (uio.uio_resid == count)) {
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE1(fusefs, , vnops, vnop_getpages_error, error);
|
2012-10-13 23:54:26 +00:00
|
|
|
return VM_PAGER_ERROR;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Calculate the number of bytes read and validate only that number
|
|
|
|
* of bytes. Note that due to pending writes, size may be 0. This
|
|
|
|
* does not mean that the remaining data is invalid!
|
|
|
|
*/
|
|
|
|
|
|
|
|
size = count - uio.uio_resid;
|
2013-03-09 02:32:23 +00:00
|
|
|
VM_OBJECT_WLOCK(vp->v_object);
|
2012-10-13 23:54:26 +00:00
|
|
|
fuse_vm_page_lock_queues();
|
|
|
|
for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
|
|
|
|
vm_page_t m;
|
|
|
|
|
|
|
|
nextoff = toff + PAGE_SIZE;
|
|
|
|
m = pages[i];
|
|
|
|
|
|
|
|
if (nextoff <= size) {
|
|
|
|
/*
|
|
|
|
* Read operation filled an entire page
|
|
|
|
*/
|
|
|
|
m->valid = VM_PAGE_BITS_ALL;
|
|
|
|
KASSERT(m->dirty == 0,
|
|
|
|
("fuse_getpages: page %p is dirty", m));
|
|
|
|
} else if (size > toff) {
|
|
|
|
/*
|
|
|
|
* Read operation filled a partial page.
|
|
|
|
*/
|
|
|
|
m->valid = 0;
|
|
|
|
vm_page_set_valid_range(m, 0, size - toff);
|
|
|
|
KASSERT(m->dirty == 0,
|
|
|
|
("fuse_getpages: page %p is dirty", m));
|
|
|
|
} else {
|
|
|
|
/*
|
2016-04-29 20:51:24 +00:00
|
|
|
* Read operation was short. If no error occurred
|
2012-10-13 23:54:26 +00:00
|
|
|
* we may have hit a zero-fill section. We simply
|
|
|
|
* leave valid set to 0.
|
|
|
|
*/
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fuse_vm_page_unlock_queues();
|
2015-12-16 23:48:50 +00:00
|
|
|
out:
|
2013-03-09 02:32:23 +00:00
|
|
|
VM_OBJECT_WUNLOCK(vp->v_object);
|
2015-12-16 23:48:50 +00:00
|
|
|
if (ap->a_rbehind)
|
|
|
|
*ap->a_rbehind = 0;
|
|
|
|
if (ap->a_rahead)
|
|
|
|
*ap->a_rahead = 0;
|
|
|
|
return (VM_PAGER_OK);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vnop_putpages_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vnode *a_vp;
|
|
|
|
vm_page_t *a_m;
|
|
|
|
int a_count;
|
|
|
|
int a_sync;
|
|
|
|
int *a_rtvals;
|
|
|
|
vm_ooffset_t a_offset;
|
2012-10-13 23:54:26 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_putpages(struct vop_putpages_args *ap)
|
|
|
|
{
|
|
|
|
struct uio uio;
|
|
|
|
struct iovec iov;
|
|
|
|
vm_offset_t kva;
|
|
|
|
struct buf *bp;
|
|
|
|
int i, error, npages, count;
|
|
|
|
off_t offset;
|
|
|
|
int *rtvals;
|
|
|
|
struct vnode *vp;
|
|
|
|
struct thread *td;
|
|
|
|
struct ucred *cred;
|
|
|
|
vm_page_t *pages;
|
|
|
|
vm_ooffset_t fsize;
|
2019-04-01 20:42:15 +00:00
|
|
|
pid_t pid = curthread->td_proc->p_pid;
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
vp = ap->a_vp;
|
|
|
|
KASSERT(vp->v_object, ("objectless vp passed to putpages"));
|
|
|
|
fsize = vp->v_object->un_pager.vnp.vnp_size;
|
|
|
|
td = curthread; /* XXX */
|
|
|
|
cred = curthread->td_ucred; /* XXX */
|
|
|
|
pages = ap->a_m;
|
|
|
|
count = ap->a_count;
|
|
|
|
rtvals = ap->a_rtvals;
|
|
|
|
npages = btoc(count);
|
|
|
|
offset = IDX_TO_OFF(pages[0]->pindex);
|
|
|
|
|
|
|
|
if (!fsess_opt_mmap(vnode_mount(vp))) {
|
2019-04-20 00:04:31 +00:00
|
|
|
SDT_PROBE2(fusefs, , vnops, trace, 1,
|
2019-02-28 19:27:54 +00:00
|
|
|
"called on non-cacheable vnode??\n");
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i < npages; i++)
|
|
|
|
rtvals[i] = VM_PAGER_AGAIN;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When putting pages, do not extend file past EOF.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (offset + count > fsize) {
|
|
|
|
count = fsize - offset;
|
|
|
|
if (count < 0)
|
|
|
|
count = 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We use only the kva address for the buffer, but this is extremely
|
2016-04-29 20:51:24 +00:00
|
|
|
* convenient and fast.
|
2012-10-13 23:54:26 +00:00
|
|
|
*/
|
Allocate pager bufs from UMA instead of 80-ish mutex protected linked list.
o In vm_pager_bufferinit() create pbuf_zone and start accounting on how many
pbufs are we going to have set.
In various subsystems that are going to utilize pbufs create private zones
via call to pbuf_zsecond_create(). The latter calls uma_zsecond_create(),
and sets a limit on created zone. After startup preallocate pbufs according
to requirements of all pbuf zones.
Subsystems that used to have a private limit with old allocator now have
private pbuf zones: md(4), fusefs, NFS client, smbfs, VFS cluster, FFS,
swap, vnode pager.
The following subsystems use shared pbuf zone: cam(4), nvme(4), physio(9),
aio(4). They should have their private limits, but changing that is out of
scope of this commit.
o Fetch tunable value of kern.nswbuf from init_param2() and while here move
NSWBUF_MIN to opt_param.h and eliminate opt_swap.h, that was holding only
this option.
Default values aren't touched by this commit, but they probably should be
reviewed wrt to modern hardware.
This change removes a tight bottleneck from sendfile(2) operation, that
uses pbufs in vnode pager. Other pagers also would benefit from faster
allocation.
Together with: gallatin
Tested by: pho
2019-01-15 01:02:16 +00:00
|
|
|
bp = uma_zalloc(fuse_pbuf_zone, M_WAITOK);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
kva = (vm_offset_t)bp->b_data;
|
|
|
|
pmap_qenter(kva, pages, npages);
|
- Remove 'struct vmmeter' from 'struct pcpu', leaving only global vmmeter
in place. To do per-cpu stats, convert all fields that previously were
maintained in the vmmeters that sit in pcpus to counter(9).
- Since some vmmeter stats may be touched at very early stages of boot,
before we have set up UMA and we can do counter_u64_alloc(), provide an
early counter mechanism:
o Leave one spare uint64_t in struct pcpu, named pc_early_dummy_counter.
o Point counter(9) fields of vmmeter to pcpu[0].pc_early_dummy_counter,
so that at early stages of boot, before counters are allocated we already
point to a counter that can be safely written to.
o For sparc64 that required a whole dummy pcpu[MAXCPU] array.
Further related changes:
- Don't include vmmeter.h into pcpu.h.
- vm.stats.vm.v_swappgsout and vm.stats.vm.v_swappgsin changed to 64-bit,
to match kernel representation.
- struct vmmeter hidden under _KERNEL, and only vmstat(1) is an exclusion.
This is based on benno@'s 4-year old patch:
https://lists.freebsd.org/pipermail/freebsd-arch/2013-July/014471.html
Reviewed by: kib, gallatin, marius, lidl
Differential Revision: https://reviews.freebsd.org/D10156
2017-04-17 17:34:47 +00:00
|
|
|
VM_CNT_INC(v_vnodeout);
|
|
|
|
VM_CNT_ADD(v_vnodepgsout, count);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
iov.iov_base = (caddr_t)kva;
|
|
|
|
iov.iov_len = count;
|
|
|
|
uio.uio_iov = &iov;
|
|
|
|
uio.uio_iovcnt = 1;
|
|
|
|
uio.uio_offset = offset;
|
|
|
|
uio.uio_resid = count;
|
|
|
|
uio.uio_segflg = UIO_SYSSPACE;
|
|
|
|
uio.uio_rw = UIO_WRITE;
|
|
|
|
uio.uio_td = td;
|
|
|
|
|
2019-04-26 19:47:43 +00:00
|
|
|
error = fuse_io_dispatch(vp, &uio, IO_DIRECT, true, cred, pid);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
pmap_qremove(kva, npages);
|
Allocate pager bufs from UMA instead of 80-ish mutex protected linked list.
o In vm_pager_bufferinit() create pbuf_zone and start accounting on how many
pbufs are we going to have set.
In various subsystems that are going to utilize pbufs create private zones
via call to pbuf_zsecond_create(). The latter calls uma_zsecond_create(),
and sets a limit on created zone. After startup preallocate pbufs according
to requirements of all pbuf zones.
Subsystems that used to have a private limit with old allocator now have
private pbuf zones: md(4), fusefs, NFS client, smbfs, VFS cluster, FFS,
swap, vnode pager.
The following subsystems use shared pbuf zone: cam(4), nvme(4), physio(9),
aio(4). They should have their private limits, but changing that is out of
scope of this commit.
o Fetch tunable value of kern.nswbuf from init_param2() and while here move
NSWBUF_MIN to opt_param.h and eliminate opt_swap.h, that was holding only
this option.
Default values aren't touched by this commit, but they probably should be
reviewed wrt to modern hardware.
This change removes a tight bottleneck from sendfile(2) operation, that
uses pbufs in vnode pager. Other pagers also would benefit from faster
allocation.
Together with: gallatin
Tested by: pho
2019-01-15 01:02:16 +00:00
|
|
|
uma_zfree(fuse_pbuf_zone, bp);
|
2012-10-13 23:54:26 +00:00
|
|
|
|
|
|
|
if (!error) {
|
|
|
|
int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE;
|
|
|
|
|
|
|
|
for (i = 0; i < nwritten; i++) {
|
|
|
|
rtvals[i] = VM_PAGER_OK;
|
2013-03-09 02:32:23 +00:00
|
|
|
VM_OBJECT_WLOCK(pages[i]->object);
|
2012-10-13 23:54:26 +00:00
|
|
|
vm_page_undirty(pages[i]);
|
2013-03-09 02:32:23 +00:00
|
|
|
VM_OBJECT_WUNLOCK(pages[i]->object);
|
2012-10-13 23:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return rtvals[0];
|
|
|
|
}
|
|
|
|
|
2017-10-14 19:02:52 +00:00
|
|
|
static const char extattr_namespace_separator = '.';
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vop_getextattr_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vop_generic_args a_gen;
|
|
|
|
struct vnode *a_vp;
|
|
|
|
int a_attrnamespace;
|
|
|
|
const char *a_name;
|
|
|
|
struct uio *a_uio;
|
|
|
|
size_t *a_size;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
2017-10-14 19:02:52 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_getextattr(struct vop_getextattr_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
2018-08-21 18:50:29 +00:00
|
|
|
struct fuse_dispatcher fdi;
|
2017-10-14 19:02:52 +00:00
|
|
|
struct fuse_getxattr_in *get_xattr_in;
|
|
|
|
struct fuse_getxattr_out *get_xattr_out;
|
|
|
|
struct mount *mp = vnode_mount(vp);
|
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
2018-08-21 18:50:29 +00:00
|
|
|
char *prefix;
|
|
|
|
char *attr_str;
|
|
|
|
size_t len;
|
|
|
|
int err;
|
2017-10-14 19:02:52 +00:00
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp))
|
2018-08-21 18:50:29 +00:00
|
|
|
return (ENXIO);
|
2017-10-14 19:02:52 +00:00
|
|
|
|
2019-04-12 00:15:36 +00:00
|
|
|
if (!fsess_isimpl(mp, FUSE_GETXATTR))
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
2019-04-10 21:10:21 +00:00
|
|
|
err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
|
2019-04-10 17:31:00 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-10-14 19:02:52 +00:00
|
|
|
/* Default to looking for user attributes. */
|
|
|
|
if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
|
|
|
|
prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
|
|
|
|
else
|
|
|
|
prefix = EXTATTR_NAMESPACE_USER_STRING;
|
|
|
|
|
|
|
|
len = strlen(prefix) + sizeof(extattr_namespace_separator) +
|
|
|
|
strlen(ap->a_name) + 1;
|
|
|
|
|
|
|
|
fdisp_init(&fdi, len + sizeof(*get_xattr_in));
|
|
|
|
fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, td, cred);
|
|
|
|
|
|
|
|
get_xattr_in = fdi.indata;
|
|
|
|
/*
|
|
|
|
* Check to see whether we're querying the available size or
|
|
|
|
* issuing the actual request. If we pass in 0, we get back struct
|
|
|
|
* fuse_getxattr_out. If we pass in a non-zero size, we get back
|
|
|
|
* that much data, without the struct fuse_getxattr_out header.
|
|
|
|
*/
|
2018-08-21 18:39:47 +00:00
|
|
|
if (uio == NULL)
|
2017-10-14 19:02:52 +00:00
|
|
|
get_xattr_in->size = 0;
|
|
|
|
else
|
|
|
|
get_xattr_in->size = uio->uio_resid;
|
|
|
|
|
|
|
|
attr_str = (char *)fdi.indata + sizeof(*get_xattr_in);
|
|
|
|
snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
|
|
|
|
ap->a_name);
|
|
|
|
|
|
|
|
err = fdisp_wait_answ(&fdi);
|
|
|
|
if (err != 0) {
|
2019-04-12 00:15:36 +00:00
|
|
|
if (err == ENOSYS) {
|
2017-10-14 19:02:52 +00:00
|
|
|
fsess_set_notimpl(mp, FUSE_GETXATTR);
|
2019-04-12 00:15:36 +00:00
|
|
|
err = EOPNOTSUPP;
|
|
|
|
}
|
2017-10-14 19:02:52 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
get_xattr_out = fdi.answ;
|
|
|
|
|
2018-08-21 18:39:47 +00:00
|
|
|
if (ap->a_size != NULL)
|
2017-10-14 19:02:52 +00:00
|
|
|
*ap->a_size = get_xattr_out->size;
|
2018-08-21 18:39:47 +00:00
|
|
|
|
|
|
|
if (uio != NULL)
|
2017-10-14 19:02:52 +00:00
|
|
|
err = uiomove(fdi.answ, fdi.iosize, uio);
|
|
|
|
|
|
|
|
out:
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vop_setextattr_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vop_generic_args a_gen;
|
|
|
|
struct vnode *a_vp;
|
|
|
|
int a_attrnamespace;
|
|
|
|
const char *a_name;
|
|
|
|
struct uio *a_uio;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
2017-10-14 19:02:52 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_setextattr(struct vop_setextattr_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
2018-08-21 18:50:29 +00:00
|
|
|
struct fuse_dispatcher fdi;
|
2017-10-14 19:02:52 +00:00
|
|
|
struct fuse_setxattr_in *set_xattr_in;
|
|
|
|
struct mount *mp = vnode_mount(vp);
|
2018-08-21 18:50:29 +00:00
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
2017-10-14 19:02:52 +00:00
|
|
|
char *prefix;
|
|
|
|
size_t len;
|
|
|
|
char *attr_str;
|
2018-08-21 18:50:29 +00:00
|
|
|
int err;
|
|
|
|
|
2017-10-14 19:02:52 +00:00
|
|
|
if (fuse_isdeadfs(vp))
|
2018-08-21 18:50:29 +00:00
|
|
|
return (ENXIO);
|
2017-10-14 19:02:52 +00:00
|
|
|
|
2019-04-12 00:15:36 +00:00
|
|
|
if (!fsess_isimpl(mp, FUSE_SETXATTR))
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
2019-04-10 21:10:21 +00:00
|
|
|
if (vfs_isrdonly(mp))
|
|
|
|
return EROFS;
|
|
|
|
|
2019-04-10 17:31:00 +00:00
|
|
|
/* Deleting xattrs must use VOP_DELETEEXTATTR instead */
|
2019-04-12 00:15:36 +00:00
|
|
|
if (ap->a_uio == NULL) {
|
|
|
|
/*
|
|
|
|
* If we got here as fallback from VOP_DELETEEXTATTR, then
|
|
|
|
* return EOPNOTSUPP.
|
|
|
|
*/
|
|
|
|
if (!fsess_isimpl(mp, FUSE_REMOVEXATTR))
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
else
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
2019-04-10 17:31:00 +00:00
|
|
|
|
2019-04-10 21:10:21 +00:00
|
|
|
err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td,
|
|
|
|
VWRITE);
|
2019-04-10 17:31:00 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-10-14 19:02:52 +00:00
|
|
|
/* Default to looking for user attributes. */
|
|
|
|
if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
|
|
|
|
prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
|
|
|
|
else
|
|
|
|
prefix = EXTATTR_NAMESPACE_USER_STRING;
|
|
|
|
|
|
|
|
len = strlen(prefix) + sizeof(extattr_namespace_separator) +
|
|
|
|
strlen(ap->a_name) + 1;
|
|
|
|
|
|
|
|
fdisp_init(&fdi, len + sizeof(*set_xattr_in) + uio->uio_resid);
|
|
|
|
fdisp_make_vp(&fdi, FUSE_SETXATTR, vp, td, cred);
|
|
|
|
|
|
|
|
set_xattr_in = fdi.indata;
|
|
|
|
set_xattr_in->size = uio->uio_resid;
|
|
|
|
|
|
|
|
attr_str = (char *)fdi.indata + sizeof(*set_xattr_in);
|
|
|
|
snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
|
|
|
|
ap->a_name);
|
|
|
|
|
|
|
|
err = uiomove((char *)fdi.indata + sizeof(*set_xattr_in) + len,
|
|
|
|
uio->uio_resid, uio);
|
|
|
|
if (err != 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = fdisp_wait_answ(&fdi);
|
|
|
|
|
2019-04-12 00:15:36 +00:00
|
|
|
if (err == ENOSYS) {
|
|
|
|
fsess_set_notimpl(mp, FUSE_SETXATTR);
|
|
|
|
err = EOPNOTSUPP;
|
2017-10-14 19:02:52 +00:00
|
|
|
}
|
2019-04-19 20:31:12 +00:00
|
|
|
if (err == ERESTART) {
|
|
|
|
/* Can't restart after calling uiomove */
|
|
|
|
err = EINTR;
|
|
|
|
}
|
2017-10-14 19:02:52 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Linux / FUSE extended attribute list is simply a collection of
|
|
|
|
* NUL-terminated strings. The FreeBSD extended attribute list is a single
|
|
|
|
* byte length followed by a non-NUL terminated string. So, this allows
|
|
|
|
* conversion of the Linux / FUSE format to the FreeBSD format in place.
|
|
|
|
* Linux attribute names are reported with the namespace as a prefix (e.g.
|
|
|
|
* "user.attribute_name"), but in FreeBSD they are reported without the
|
|
|
|
* namespace prefix (e.g. "attribute_name"). So, we're going from:
|
|
|
|
*
|
|
|
|
* user.attr_name1\0user.attr_name2\0
|
|
|
|
*
|
|
|
|
* to:
|
|
|
|
*
|
|
|
|
* <num>attr_name1<num>attr_name2
|
|
|
|
*
|
|
|
|
* Where "<num>" is a single byte number of characters in the attribute name.
|
|
|
|
*
|
|
|
|
* Args:
|
|
|
|
* prefix - exattr namespace prefix string
|
|
|
|
* list, list_len - input list with namespace prefixes
|
|
|
|
* bsd_list, bsd_list_len - output list compatible with bsd vfs
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_xattrlist_convert(char *prefix, const char *list, int list_len,
|
|
|
|
char *bsd_list, int *bsd_list_len)
|
|
|
|
{
|
|
|
|
int len, pos, dist_to_next, prefix_len;
|
|
|
|
|
|
|
|
pos = 0;
|
|
|
|
*bsd_list_len = 0;
|
|
|
|
prefix_len = strlen(prefix);
|
|
|
|
|
|
|
|
while (pos < list_len && list[pos] != '\0') {
|
|
|
|
dist_to_next = strlen(&list[pos]) + 1;
|
|
|
|
if (bcmp(&list[pos], prefix, prefix_len) == 0 &&
|
|
|
|
list[pos + prefix_len] == extattr_namespace_separator) {
|
|
|
|
len = dist_to_next -
|
|
|
|
(prefix_len + sizeof(extattr_namespace_separator)) - 1;
|
|
|
|
if (len >= EXTATTR_MAXNAMELEN)
|
|
|
|
return (ENAMETOOLONG);
|
|
|
|
|
|
|
|
bsd_list[*bsd_list_len] = len;
|
|
|
|
memcpy(&bsd_list[*bsd_list_len + 1],
|
|
|
|
&list[pos + prefix_len +
|
|
|
|
sizeof(extattr_namespace_separator)], len);
|
|
|
|
|
|
|
|
*bsd_list_len += len + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos += dist_to_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vop_listextattr_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vop_generic_args a_gen;
|
|
|
|
struct vnode *a_vp;
|
|
|
|
int a_attrnamespace;
|
|
|
|
struct uio *a_uio;
|
|
|
|
size_t *a_size;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
2017-10-14 19:02:52 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_listextattr(struct vop_listextattr_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
|
|
|
struct uio *uio = ap->a_uio;
|
2018-08-21 18:50:29 +00:00
|
|
|
struct fuse_dispatcher fdi;
|
2018-08-21 18:39:47 +00:00
|
|
|
struct fuse_listxattr_in *list_xattr_in;
|
|
|
|
struct fuse_listxattr_out *list_xattr_out;
|
2017-10-14 19:02:52 +00:00
|
|
|
struct mount *mp = vnode_mount(vp);
|
2018-08-21 18:50:29 +00:00
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
2017-10-14 19:02:52 +00:00
|
|
|
size_t len;
|
|
|
|
char *prefix;
|
|
|
|
char *attr_str;
|
|
|
|
char *bsd_list = NULL;
|
2018-08-21 18:39:47 +00:00
|
|
|
char *linux_list;
|
2017-10-14 19:02:52 +00:00
|
|
|
int bsd_list_len;
|
2018-08-21 18:39:47 +00:00
|
|
|
int linux_list_len;
|
2018-08-21 18:50:29 +00:00
|
|
|
int err;
|
2017-10-14 19:02:52 +00:00
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp))
|
2018-08-21 18:50:29 +00:00
|
|
|
return (ENXIO);
|
2017-10-14 19:02:52 +00:00
|
|
|
|
2019-04-12 00:15:36 +00:00
|
|
|
if (!fsess_isimpl(mp, FUSE_LISTXATTR))
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
2019-04-10 21:10:21 +00:00
|
|
|
err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td, VREAD);
|
2019-04-10 17:31:00 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-10-14 19:02:52 +00:00
|
|
|
/*
|
|
|
|
* Add space for a NUL and the period separator if enabled.
|
|
|
|
* Default to looking for user attributes.
|
|
|
|
*/
|
|
|
|
if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
|
|
|
|
prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
|
|
|
|
else
|
|
|
|
prefix = EXTATTR_NAMESPACE_USER_STRING;
|
|
|
|
|
|
|
|
len = strlen(prefix) + sizeof(extattr_namespace_separator) + 1;
|
|
|
|
|
2018-08-21 18:39:47 +00:00
|
|
|
fdisp_init(&fdi, sizeof(*list_xattr_in) + len);
|
2017-10-14 19:02:52 +00:00
|
|
|
fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
|
|
|
|
|
2018-08-21 18:39:47 +00:00
|
|
|
/*
|
|
|
|
* Retrieve Linux / FUSE compatible list size.
|
|
|
|
*/
|
|
|
|
list_xattr_in = fdi.indata;
|
|
|
|
list_xattr_in->size = 0;
|
|
|
|
attr_str = (char *)fdi.indata + sizeof(*list_xattr_in);
|
2017-10-14 19:02:52 +00:00
|
|
|
snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator);
|
|
|
|
|
|
|
|
err = fdisp_wait_answ(&fdi);
|
|
|
|
if (err != 0) {
|
2019-04-12 00:15:36 +00:00
|
|
|
if (err == ENOSYS) {
|
2017-10-14 19:02:52 +00:00
|
|
|
fsess_set_notimpl(mp, FUSE_LISTXATTR);
|
2019-04-12 00:15:36 +00:00
|
|
|
err = EOPNOTSUPP;
|
|
|
|
}
|
2017-10-14 19:02:52 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-08-21 18:39:47 +00:00
|
|
|
list_xattr_out = fdi.answ;
|
|
|
|
linux_list_len = list_xattr_out->size;
|
|
|
|
if (linux_list_len == 0) {
|
|
|
|
if (ap->a_size != NULL)
|
|
|
|
*ap->a_size = linux_list_len;
|
2017-10-14 19:02:52 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-08-21 18:39:47 +00:00
|
|
|
/*
|
|
|
|
* Retrieve Linux / FUSE compatible list values.
|
|
|
|
*/
|
2019-04-04 16:51:34 +00:00
|
|
|
fdisp_refresh_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
|
2018-08-21 18:39:47 +00:00
|
|
|
list_xattr_in = fdi.indata;
|
|
|
|
list_xattr_in->size = linux_list_len + sizeof(*list_xattr_out);
|
2019-04-04 16:51:34 +00:00
|
|
|
list_xattr_in->flags = 0;
|
2018-08-21 18:39:47 +00:00
|
|
|
attr_str = (char *)fdi.indata + sizeof(*list_xattr_in);
|
|
|
|
snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator);
|
|
|
|
|
|
|
|
err = fdisp_wait_answ(&fdi);
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
linux_list = fdi.answ;
|
|
|
|
linux_list_len = fdi.iosize;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Retrieve the BSD compatible list values.
|
|
|
|
* The Linux / FUSE attribute list format isn't the same
|
|
|
|
* as FreeBSD's format. So we need to transform it into
|
|
|
|
* FreeBSD's format before giving it to the user.
|
|
|
|
*/
|
|
|
|
bsd_list = malloc(linux_list_len, M_TEMP, M_WAITOK);
|
|
|
|
err = fuse_xattrlist_convert(prefix, linux_list, linux_list_len,
|
|
|
|
bsd_list, &bsd_list_len);
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (ap->a_size != NULL)
|
|
|
|
*ap->a_size = bsd_list_len;
|
2017-10-14 19:02:52 +00:00
|
|
|
|
2018-08-21 18:39:47 +00:00
|
|
|
if (uio != NULL)
|
2017-10-14 19:02:52 +00:00
|
|
|
err = uiomove(bsd_list, bsd_list_len, uio);
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(bsd_list, M_TEMP);
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
struct vop_deleteextattr_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vop_generic_args a_gen;
|
|
|
|
struct vnode *a_vp;
|
|
|
|
int a_attrnamespace;
|
|
|
|
const char *a_name;
|
|
|
|
struct ucred *a_cred;
|
|
|
|
struct thread *a_td;
|
2017-10-14 19:02:52 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_deleteextattr(struct vop_deleteextattr_args *ap)
|
|
|
|
{
|
|
|
|
struct vnode *vp = ap->a_vp;
|
2018-08-21 18:50:29 +00:00
|
|
|
struct fuse_dispatcher fdi;
|
2017-10-14 19:02:52 +00:00
|
|
|
struct mount *mp = vnode_mount(vp);
|
2018-08-21 18:50:29 +00:00
|
|
|
struct thread *td = ap->a_td;
|
|
|
|
struct ucred *cred = ap->a_cred;
|
2017-10-14 19:02:52 +00:00
|
|
|
char *prefix;
|
|
|
|
size_t len;
|
|
|
|
char *attr_str;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (fuse_isdeadfs(vp))
|
2018-08-21 18:50:29 +00:00
|
|
|
return (ENXIO);
|
2017-10-14 19:02:52 +00:00
|
|
|
|
2019-04-12 00:15:36 +00:00
|
|
|
if (!fsess_isimpl(mp, FUSE_REMOVEXATTR))
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
|
2019-04-10 21:10:21 +00:00
|
|
|
if (vfs_isrdonly(mp))
|
|
|
|
return EROFS;
|
|
|
|
|
|
|
|
err = fuse_extattr_check_cred(vp, ap->a_attrnamespace, cred, td,
|
|
|
|
VWRITE);
|
2019-04-10 17:31:00 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-10-14 19:02:52 +00:00
|
|
|
/* Default to looking for user attributes. */
|
|
|
|
if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
|
|
|
|
prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
|
|
|
|
else
|
|
|
|
prefix = EXTATTR_NAMESPACE_USER_STRING;
|
|
|
|
|
|
|
|
len = strlen(prefix) + sizeof(extattr_namespace_separator) +
|
|
|
|
strlen(ap->a_name) + 1;
|
|
|
|
|
|
|
|
fdisp_init(&fdi, len);
|
|
|
|
fdisp_make_vp(&fdi, FUSE_REMOVEXATTR, vp, td, cred);
|
|
|
|
|
|
|
|
attr_str = fdi.indata;
|
|
|
|
snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
|
|
|
|
ap->a_name);
|
|
|
|
|
|
|
|
err = fdisp_wait_answ(&fdi);
|
2019-04-12 00:15:36 +00:00
|
|
|
if (err == ENOSYS) {
|
|
|
|
fsess_set_notimpl(mp, FUSE_REMOVEXATTR);
|
|
|
|
err = EOPNOTSUPP;
|
2017-10-14 19:02:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fdisp_destroy(&fdi);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2012-10-13 23:54:26 +00:00
|
|
|
/*
|
|
|
|
struct vnop_print_args {
|
2019-02-20 02:49:26 +00:00
|
|
|
struct vnode *a_vp;
|
2012-10-13 23:54:26 +00:00
|
|
|
};
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fuse_vnop_print(struct vop_print_args *ap)
|
|
|
|
{
|
|
|
|
struct fuse_vnode_data *fvdat = VTOFUD(ap->a_vp);
|
|
|
|
|
|
|
|
printf("nodeid: %ju, parent nodeid: %ju, nlookup: %ju, flag: %#x\n",
|
|
|
|
(uintmax_t)VTOILLU(ap->a_vp), (uintmax_t)fvdat->parent_nid,
|
|
|
|
(uintmax_t)fvdat->nlookup,
|
|
|
|
fvdat->flag);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|