svn commit: r297887 - projects/pnfs-server/sys/fs/fuse
Rick Macklem
rmacklem at FreeBSD.org
Tue Apr 12 22:58:42 UTC 2016
Author: rmacklem
Date: Tue Apr 12 22:58:40 2016
New Revision: 297887
URL: https://svnweb.freebsd.org/changeset/base/297887
Log:
Check in the fuse changes that allow it to export the file system via NFS.
Modified:
projects/pnfs-server/sys/fs/fuse/fuse_file.c
projects/pnfs-server/sys/fs/fuse/fuse_file.h
projects/pnfs-server/sys/fs/fuse/fuse_internal.c
projects/pnfs-server/sys/fs/fuse/fuse_internal.h
projects/pnfs-server/sys/fs/fuse/fuse_io.c
projects/pnfs-server/sys/fs/fuse/fuse_ipc.c
projects/pnfs-server/sys/fs/fuse/fuse_node.c
projects/pnfs-server/sys/fs/fuse/fuse_node.h
projects/pnfs-server/sys/fs/fuse/fuse_vfsops.c
projects/pnfs-server/sys/fs/fuse/fuse_vnops.c
Modified: projects/pnfs-server/sys/fs/fuse/fuse_file.c
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_file.c Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_file.c Tue Apr 12 22:58:40 2016 (r297887)
@@ -88,6 +88,8 @@ static int fuse_fh_count = 0;
SYSCTL_INT(_vfs_fuse, OID_AUTO, filehandle_count, CTLFLAG_RD,
&fuse_fh_count, 0, "");
+extern int fuse_force_directio;
+
int
fuse_filehandle_open(struct vnode *vp,
fufh_type_t fufh_type,
@@ -141,7 +143,11 @@ fuse_filehandle_open(struct vnode *vp,
foo = fdi.answ;
fuse_filehandle_init(vp, fufh_type, fufhp, foo->fh);
- fuse_vnode_open(vp, foo->open_flags, td);
+
+ if (fufh_type == FUFH_WRONLY || fuse_force_directio > 0)
+ fuse_vnode_open(vp, foo->open_flags | FOPEN_DIRECT_IO, td);
+ else
+ fuse_vnode_open(vp, foo->open_flags, td);
out:
fdisp_destroy(&fdi);
@@ -206,6 +212,28 @@ fuse_filehandle_valid(struct vnode *vp,
return FUFH_IS_VALID(fufh);
}
+/*
+ * Check for a valid file handle, first the type requested, but if that
+ * isn't valid, try for FUFH_RDWR.
+ * Return the FUFH type that is valid or FUFH_INVALID if there are none.
+ * This is a variant of fuse_filehandle_vaild() analygous to
+ * fuse_filehandle_getrw().
+ */
+fufh_type_t
+fuse_filehandle_validrw(struct vnode *vp, fufh_type_t fufh_type)
+{
+ struct fuse_vnode_data *fvdat = VTOFUD(vp);
+ struct fuse_filehandle *fufh;
+
+ fufh = &fvdat->fufh[fufh_type];
+ if (FUFH_IS_VALID(fufh) != 0)
+ return (fufh_type);
+ fufh = &fvdat->fufh[FUFH_RDWR];
+ if (FUFH_IS_VALID(fufh) != 0)
+ return (FUFH_RDWR);
+ return (FUFH_INVALID);
+}
+
int
fuse_filehandle_get(struct vnode *vp, fufh_type_t fufh_type,
struct fuse_filehandle **fufhp)
Modified: projects/pnfs-server/sys/fs/fuse/fuse_file.h
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_file.h Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_file.h Tue Apr 12 22:58:40 2016 (r297887)
@@ -137,6 +137,7 @@ fuse_filehandle_xlate_to_oflags(fufh_typ
}
int fuse_filehandle_valid(struct vnode *vp, fufh_type_t fufh_type);
+fufh_type_t fuse_filehandle_validrw(struct vnode *vp, fufh_type_t fufh_type);
int fuse_filehandle_get(struct vnode *vp, fufh_type_t fufh_type,
struct fuse_filehandle **fufhp);
int fuse_filehandle_getrw(struct vnode *vp, fufh_type_t fufh_type,
Modified: projects/pnfs-server/sys/fs/fuse/fuse_internal.c
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_internal.c Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_internal.c Tue Apr 12 22:58:40 2016 (r297887)
@@ -251,16 +251,19 @@ fuse_internal_fsync(struct vnode *vp,
int
fuse_internal_readdir(struct vnode *vp,
struct uio *uio,
+ off_t startoff,
struct fuse_filehandle *fufh,
- struct fuse_iov *cookediov)
+ struct fuse_iov *cookediov,
+ int *ncookies,
+ u_long *cookies)
{
int err = 0;
struct fuse_dispatcher fdi;
struct fuse_read_in *fri;
+ int fnd_start;
- if (uio_resid(uio) == 0) {
- return 0;
- }
+ if (uio_resid(uio) == 0)
+ return (0);
fdisp_init(&fdi, 0);
/*
@@ -268,8 +271,18 @@ fuse_internal_readdir(struct vnode *vp,
* I/O).
*/
+ /*
+ * fnd_start is set non-zero once the offset in the directory gets
+ * to the startoff. This is done because directories must be read
+ * from the beginning (offset == 0) when fuse_vnop_readdir() needs
+ * to do an open of the directory.
+ * If it is not set non-zero here, it will be set non-zero in
+ * fuse_internal_readdir_processdata() when uio_offset == startoff.
+ */
+ fnd_start = 0;
+ if (uio->uio_offset == startoff)
+ fnd_start = 1;
while (uio_resid(uio) > 0) {
-
fdi.iosize = sizeof(*fri);
fdisp_make_vp(&fdi, FUSE_READDIR, vp, NULL, NULL);
@@ -277,53 +290,67 @@ fuse_internal_readdir(struct vnode *vp,
fri->fh = fufh->fh_id;
fri->offset = uio_offset(uio);
fri->size = min(uio_resid(uio), FUSE_DEFAULT_IOSIZE);
- /* mp->max_read */
- if ((err = fdisp_wait_answ(&fdi))) {
+ if ((err = fdisp_wait_answ(&fdi)) != 0)
break;
- }
- if ((err = fuse_internal_readdir_processdata(uio, fri->size, fdi.answ,
- fdi.iosize, cookediov))) {
+ if ((err = fuse_internal_readdir_processdata(uio, startoff,
+ &fnd_start, fri->size, fdi.answ, fdi.iosize, cookediov,
+ ncookies, &cookies)) != 0)
break;
- }
}
fdisp_destroy(&fdi);
return ((err == -1) ? 0 : err);
}
+/*
+ * Return -1 to indicate that this readdir is finished, 0 if it copied
+ * all the directory data read in and it may be possible to read more
+ * and greater than 0 for a failure.
+ */
int
fuse_internal_readdir_processdata(struct uio *uio,
+ off_t startoff,
+ int *fnd_start,
size_t reqsize,
void *buf,
size_t bufsize,
- void *param)
+ struct fuse_iov *cookediov,
+ int *ncookies,
+ u_long **cookiesp)
{
int err = 0;
- int cou = 0;
int bytesavail;
size_t freclen;
-
struct dirent *de;
struct fuse_dirent *fudge;
- struct fuse_iov *cookediov = param;
+ u_long *cookies;
+ cookies = *cookiesp;
if (bufsize < FUSE_NAME_OFFSET) {
- return -1;
+ return (-1);
}
for (;;) {
if (bufsize < FUSE_NAME_OFFSET) {
- err = -1;
+ /*
+ * Return 0 since we have done at least one loop
+ * iteration and we are at the end of the buffer.
+ * As such, we might be able to read more directory
+ * data.
+ */
+ err = 0;
break;
}
fudge = (struct fuse_dirent *)buf;
freclen = FUSE_DIRENT_SIZE(fudge);
- cou++;
-
if (bufsize < freclen) {
- err = ((cou == 1) ? -1 : 0);
+ /*
+ * This indicates a partial directory entry at the
+ * end of the directory data.
+ */
+ err = -1;
break;
}
#ifdef ZERO_PAD_INCOMPLETE_BUFS
@@ -333,40 +360,60 @@ fuse_internal_readdir_processdata(struct
}
#endif
- if (!fudge->namelen || fudge->namelen > MAXNAMLEN) {
+ if (fudge->namelen == 0 || fudge->namelen > MAXNAMLEN) {
err = EINVAL;
break;
}
- bytesavail = GENERIC_DIRSIZ((struct pseudo_dirent *)
- &fudge->namelen);
+ /* bytesavail is the size of the BSD dirent. */
+ bytesavail = GENERIC_DIRSIZ((struct pseudo_dirent *)
+ &fudge->namelen);
if (bytesavail > uio_resid(uio)) {
+ /* Out of space for the dir so we are done. */
err = -1;
break;
}
- fiov_refresh(cookediov);
- fiov_adjust(cookediov, bytesavail);
- de = (struct dirent *)cookediov->base;
- de->d_fileno = fudge->ino; /* XXX: truncation */
- de->d_reclen = bytesavail;
- de->d_type = fudge->type;
- de->d_namlen = fudge->namelen;
- memcpy((char *)cookediov->base + sizeof(struct dirent) -
- MAXNAMLEN - 1,
- (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
- ((char *)cookediov->base)[bytesavail] = '\0';
+ /*
+ * Don't start to copy the directory entries out until
+ * the requested offset in the directory is found.
+ */
+ if (*fnd_start != 0) {
+ fiov_refresh(cookediov);
+ fiov_adjust(cookediov, bytesavail);
+ de = (struct dirent *)cookediov->base;
+ de->d_fileno = fudge->ino; /* XXX: truncation */
+ de->d_reclen = bytesavail;
+ de->d_type = fudge->type;
+ de->d_namlen = fudge->namelen;
+ memcpy((char *)cookediov->base +
+ sizeof(struct dirent) - MAXNAMLEN - 1,
+ (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
+ ((char *)cookediov->base)[bytesavail] = '\0';
+
+ err = uiomove(cookediov->base, cookediov->len, uio);
+ if (err != 0)
+ break;
+ if (cookies != NULL) {
+ if (*ncookies == 0) {
+ err = -1;
+ break;
+ }
+ *cookies = fudge->off;
+ cookies++;
+ (*ncookies)--;
+ }
+ } else if (startoff == fudge->off)
+ *fnd_start = 1;
- err = uiomove(cookediov->base, cookediov->len, uio);
- if (err) {
- break;
- }
+ /* Move to the next fuse directory entry. */
+ uio_setoffset(uio, fudge->off);
buf = (char *)buf + freclen;
bufsize -= freclen;
- uio_setoffset(uio, fudge->off);
}
+ *cookiesp = cookies;
- return err;
+ return (err);
}
/* remove */
Modified: projects/pnfs-server/sys/fs/fuse/fuse_internal.h
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_internal.h Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_internal.h Tue Apr 12 22:58:40 2016 (r297887)
@@ -261,15 +261,22 @@ struct pseudo_dirent {
int
fuse_internal_readdir(struct vnode *vp,
struct uio *uio,
+ off_t startoff,
struct fuse_filehandle *fufh,
- struct fuse_iov *cookediov);
+ struct fuse_iov *cookediov,
+ int *ncookies,
+ u_long *cookies);
int
fuse_internal_readdir_processdata(struct uio *uio,
+ off_t startoff,
+ int *fnd_start,
size_t reqsize,
void *buf,
size_t bufsize,
- void *param);
+ struct fuse_iov *cookediov,
+ int *ncookies,
+ u_long **cookies);
/* remove */
Modified: projects/pnfs-server/sys/fs/fuse/fuse_io.c
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_io.c Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_io.c Tue Apr 12 22:58:40 2016 (r297887)
@@ -119,15 +119,10 @@ fuse_io_dispatch(struct vnode *vp, struc
{
struct fuse_filehandle *fufh;
int err, directio;
+ fufh_type_t fufhtype;
MPASS(vp->v_type == VREG || vp->v_type == VDIR);
- err = fuse_filehandle_getrw(vp,
- (uio->uio_rw == UIO_READ) ? FUFH_RDONLY : FUFH_WRONLY, &fufh);
- if (err) {
- printf("FUSE: io dispatch: filehandles are closed\n");
- return err;
- }
/*
* Ideally, when the daemon asks for direct io at open time, the
* standard file flag should be set according to this, so that would
@@ -140,6 +135,18 @@ fuse_io_dispatch(struct vnode *vp, struc
*/
directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));
+ if (uio->uio_rw == UIO_READ)
+ fufhtype = FUFH_RDONLY;
+ else if (directio != 0)
+ fufhtype = FUFH_WRONLY;
+ else
+ /* Buffer cache writing might read a block in. */
+ fufhtype = FUFH_RDWR;
+ err = fuse_filehandle_getrw(vp, fufhtype, &fufh);
+ if (err) {
+ printf("FUSE: io dispatch: filehandles are closed\n");
+ return err;
+ }
switch (uio->uio_rw) {
case UIO_READ:
if (directio) {
Modified: projects/pnfs-server/sys/fs/fuse/fuse_ipc.c
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_ipc.c Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_ipc.c Tue Apr 12 22:58:40 2016 (r297887)
@@ -549,6 +549,7 @@ fuse_body_audit(struct fuse_ticket *ftic
{
int err = 0;
enum fuse_opcode opcode;
+ struct fuse_getxattr_in *fgin;
debug_printf("ftick=%p, blen = %zu\n", ftick, blen);
@@ -636,23 +637,23 @@ fuse_body_audit(struct fuse_ticket *ftic
break;
case FUSE_SETXATTR:
- panic("FUSE_SETXATTR implementor has forgotten to define a"
- " response body format check");
+ err = (blen == 0) ? 0 : EINVAL;
break;
case FUSE_GETXATTR:
- panic("FUSE_GETXATTR implementor has forgotten to define a"
- " response body format check");
- break;
-
case FUSE_LISTXATTR:
- panic("FUSE_LISTXATTR implementor has forgotten to define a"
- " response body format check");
+ fgin = (struct fuse_getxattr_in *)
+ ((char *)ftick->tk_ms_fiov.base +
+ sizeof(struct fuse_in_header));
+ if (fgin->size == 0)
+ err = (blen == sizeof(struct fuse_getxattr_out)) ? 0 :
+ EINVAL;
+ else
+ err = (blen <= fgin->size) ? 0 : EINVAL;
break;
case FUSE_REMOVEXATTR:
- panic("FUSE_REMOVEXATTR implementor has forgotten to define a"
- " response body format check");
+ err = (blen == 0) ? 0 : EINVAL;
break;
case FUSE_FLUSH:
@@ -687,15 +688,15 @@ fuse_body_audit(struct fuse_ticket *ftic
break;
case FUSE_GETLK:
- panic("FUSE: no response body format check for FUSE_GETLK");
+ err = (blen == sizeof(struct fuse_lk_out)) ? 0 : EINVAL;
break;
case FUSE_SETLK:
- panic("FUSE: no response body format check for FUSE_SETLK");
+ err = (blen == 0) ? 0 : EINVAL;
break;
case FUSE_SETLKW:
- panic("FUSE: no response body format check for FUSE_SETLKW");
+ err = (blen == 0) ? 0 : EINVAL;
break;
case FUSE_ACCESS:
Modified: projects/pnfs-server/sys/fs/fuse/fuse_node.c
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_node.c Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_node.c Tue Apr 12 22:58:40 2016 (r297887)
@@ -169,7 +169,7 @@ fuse_vnode_hash(uint64_t id)
return (fnv_32_buf(&id, sizeof(id), FNV1_32_INIT));
}
-static int
+int
fuse_vnode_alloc(struct mount *mp,
struct thread *td,
uint64_t nodeid,
@@ -289,7 +289,16 @@ fuse_vnode_open(struct vnode *vp, int32_
* XXXIP: Handle fd based DIRECT_IO
*/
if (fuse_open_flags & FOPEN_DIRECT_IO) {
- VTOFUD(vp)->flag |= FN_DIRECTIO;
+ ASSERT_VOP_ELOCKED(vp, __func__);
+ /*
+ * If switching from buffer cache I/O to direct I/O, the
+ * buffer cache blocks must be invalidated to avoid accessing
+ * stale data in the buffer cache.
+ */
+ if ((VTOFUD(vp)->flag & FN_DIRECTIO) == 0) {
+ VTOFUD(vp)->flag |= FN_DIRECTIO;
+ fuse_io_invalbuf(vp, td);
+ }
} else {
VTOFUD(vp)->flag &= ~FN_DIRECTIO;
}
Modified: projects/pnfs-server/sys/fs/fuse/fuse_node.h
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_node.h Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_node.h Tue Apr 12 22:58:40 2016 (r297887)
@@ -111,6 +111,12 @@ fuse_vnode_setparent(struct vnode *vp, s
void fuse_vnode_destroy(struct vnode *vp);
+int fuse_vnode_alloc(struct mount *mp,
+ struct thread *td,
+ uint64_t nodeid,
+ enum vtype vtyp,
+ struct vnode **vpp);
+
int fuse_vnode_get(struct mount *mp,
uint64_t nodeid,
struct vnode *dvp,
@@ -128,4 +134,21 @@ int fuse_vnode_savesize(struct vnode *vp
int fuse_vnode_setsize(struct vnode *vp, struct ucred *cred, off_t newsize);
+/*
+ * Since making a structure that is a file system specific "struct fid"
+ * is too big, due to alignment issues, this structure is copied into the
+ * fid_data field. fid_data0 in "struct fid" is used for the vnode type.
+ * Until MAXFIDSZ is increased, only the first half of this structure will
+ * fit in fid_data. This is fine for GlusterFS, since it always sets the
+ * "generation" to 0. As such, the rest is #ifdef notyet.
+ */
+struct fuse_fid_data {
+ uint64_t ffid_nid;
+ uint64_t ffid_parent_nid;
+#ifdef notyet
+ uint64_t ffid_nid_gen;
+ uint64_t ffid_parent_nid_gen;
+#endif
+};
+
#endif /* _FUSE_NODE_H_ */
Modified: projects/pnfs-server/sys/fs/fuse/fuse_vfsops.c
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_vfsops.c Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_vfsops.c Tue Apr 12 22:58:40 2016 (r297887)
@@ -105,12 +105,14 @@ static vfs_mount_t fuse_vfsop_mount;
static vfs_unmount_t fuse_vfsop_unmount;
static vfs_root_t fuse_vfsop_root;
static vfs_statfs_t fuse_vfsop_statfs;
+static vfs_fhtovp_t fuse_vfsop_fhtovp;
struct vfsops fuse_vfsops = {
.vfs_mount = fuse_vfsop_mount,
.vfs_unmount = fuse_vfsop_unmount,
.vfs_root = fuse_vfsop_root,
.vfs_statfs = fuse_vfsop_statfs,
+ .vfs_fhtovp = fuse_vfsop_fhtovp,
};
SYSCTL_INT(_vfs_fuse, OID_AUTO, init_backgrounded, CTLFLAG_RD,
@@ -232,8 +234,12 @@ fuse_vfsop_mount(struct mount *mp)
fuse_trace_printf_vfsop();
+ /*
+ * Allow MNT_UPDATE only so the mountd can set exports on the file
+ * system.
+ */
if (mp->mnt_flag & MNT_UPDATE)
- return EOPNOTSUPP;
+ return (0);
MNT_ILOCK(mp);
mp->mnt_flag |= MNT_SYNCHRONOUS;
@@ -532,3 +538,35 @@ fake:
return 0;
}
+
+/*
+ * Translate a file handle into a vnode. fid_data0 is the v_type and
+ * fid_data is "struct fuse_fid" memcpy()'d so that alignment doesn't
+ * make the structure too big.
+ * There is no "generation" field in the file handle. GlusterFS never
+ * sets it. I don't know if other fuse filesystems do set it, but there
+ * is no space for it in fhandle_t.
+ */
+static int
+fuse_vfsop_fhtovp(struct mount *mp, struct fid *fhp, int flags,
+ struct vnode **vpp)
+{
+ struct fuse_fid_data ffid;
+ enum vtype vtyp;
+ int err;
+
+ if (fhp->fid_len != offsetof(struct fid, fid_data) +
+ sizeof(struct fuse_fid_data))
+ return (ESTALE);
+ vtyp = (enum vtype)fhp->fid_data0;
+ if (vtyp != VREG && vtyp != VDIR && vtyp != VLNK)
+ return (ESTALE);
+ memcpy(&ffid, fhp->fid_data, sizeof(ffid));
+ err = fuse_vnode_alloc(mp, curthread, ffid.ffid_nid, vtyp, vpp);
+ if (err != 0)
+ return (ESTALE);
+ if (vtyp == VDIR)
+ VTOFUD(*vpp)->parent_nid = ffid.ffid_parent_nid;
+ return (0);
+}
+
Modified: projects/pnfs-server/sys/fs/fuse/fuse_vnops.c
==============================================================================
--- projects/pnfs-server/sys/fs/fuse/fuse_vnops.c Tue Apr 12 22:55:47 2016 (r297886)
+++ projects/pnfs-server/sys/fs/fuse/fuse_vnops.c Tue Apr 12 22:58:40 2016 (r297887)
@@ -82,6 +82,8 @@ __FBSDID("$FreeBSD$");
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/sysctl.h>
+#include <sys/extattr.h>
+#include <sys/jail.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
@@ -133,6 +135,12 @@ 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;
+static vop_getextattr_t fuse_vnop_getextattr;
+static vop_setextattr_t fuse_vnop_setextattr;
+static vop_listextattr_t fuse_vnop_listextattr;
+static vop_deleteextattr_t fuse_vnop_deleteextattr;
+static vop_advlock_t fuse_vnop_advlock;
+static vop_vptofh_t fuse_vnop_vptofh;
struct vop_vector fuse_vnops = {
.vop_default = &default_vnodeops,
@@ -162,6 +170,12 @@ struct vop_vector fuse_vnops = {
.vop_getpages = fuse_vnop_getpages,
.vop_putpages = fuse_vnop_putpages,
.vop_print = fuse_vnop_print,
+ .vop_getextattr = fuse_vnop_getextattr,
+ .vop_setextattr = fuse_vnop_setextattr,
+ .vop_listextattr = fuse_vnop_listextattr,
+ .vop_deleteextattr = fuse_vnop_deleteextattr,
+ .vop_advlock = fuse_vnop_advlock,
+ .vop_vptofh = fuse_vnop_vptofh,
};
static u_long fuse_lookup_cache_hits = 0;
@@ -190,6 +204,8 @@ SYSCTL_INT(_vfs_fuse, OID_AUTO, reclaim_
int fuse_pbuf_freecnt = -1;
+int fuse_force_directio = 0;
+
#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)
@@ -391,6 +407,8 @@ bringup:
x_fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
x_open_flags = ((struct fuse_open_out *)(feo + 1))->open_flags;
+ if (fuse_force_directio > 0)
+ x_open_flags |= FOPEN_DIRECT_IO;
fuse_filehandle_init(*vpp, FUFH_RDWR, NULL, x_fh_id);
fuse_vnode_open(*vpp, x_open_flags, td);
cache_purge_negative(dvp);
@@ -560,27 +578,39 @@ fuse_vnop_inactive(struct vop_inactive_a
FS_DEBUG("inode=%ju\n", (uintmax_t)VTOI(vp));
- for (type = 0; type < FUFH_MAXTYPE; type++) {
- fufh = &(fvdat->fufh[type]);
- if (FUFH_IS_VALID(fufh)) {
- if (need_flush && vp->v_type == VREG) {
- if ((VTOFUD(vp)->flag & FN_SIZECHANGE) != 0) {
- fuse_vnode_savesize(vp, NULL);
+ /*
+ * For NFS exported mounts, delay buffer cache flushing and the
+ * close until the vnode is reclaimed. This is done since NFS
+ * does the open implicitly when a read/write occurs and then
+ * it reduces the vnode reference count to 0 for each read/write.
+ */
+ if ((vp->v_mount->mnt_flag & MNT_EXPORTED) == 0) {
+ for (type = 0; type < FUFH_MAXTYPE; type++) {
+ fufh = &(fvdat->fufh[type]);
+ if (FUFH_IS_VALID(fufh)) {
+ if (need_flush && vp->v_type == VREG) {
+ if ((VTOFUD(vp)->flag & FN_SIZECHANGE)
+ != 0) {
+ fuse_vnode_savesize(vp, NULL);
+ }
+ 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;
}
- 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;
+ fuse_filehandle_close(vp, type, td, NULL);
}
- fuse_filehandle_close(vp, type, td, NULL);
}
}
+#ifdef notnow
if ((fvdat->flag & FN_REVOKED) != 0 && fuse_reclaim_revoked) {
vrecycle(vp);
}
+#endif
return 0;
}
@@ -1125,6 +1155,7 @@ fuse_vnop_open(struct vop_open_args *ap)
struct fuse_vnode_data *fvdat;
int error, isdir = 0;
+ int32_t fuse_open_flags;
FS_DEBUG2G("inode=%ju mode=0x%x\n", (uintmax_t)VTOI(vp), mode);
@@ -1136,14 +1167,19 @@ fuse_vnop_open(struct vop_open_args *ap)
if (vnode_isdir(vp)) {
isdir = 1;
}
+ fuse_open_flags = 0;
if (isdir) {
fufh_type = FUFH_RDONLY;
} else {
fufh_type = fuse_filehandle_xlate_from_fflags(mode);
+ if (fufh_type == FUFH_WRONLY ||
+ (fvdat->flag & FN_DIRECTIO) != 0 ||
+ fuse_force_directio > 0)
+ fuse_open_flags = FOPEN_DIRECT_IO;
}
if (fuse_filehandle_valid(vp, fufh_type)) {
- fuse_vnode_open(vp, 0, td);
+ fuse_vnode_open(vp, fuse_open_flags, td);
return 0;
}
error = fuse_filehandle_open(vp, fufh_type, NULL, td, cred);
@@ -1165,20 +1201,53 @@ 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;
+ struct ucred *cred = NULL;
+ int err, freecred;
+ gid_t gid;
FS_DEBUG2G("inode=%ju offset=%jd resid=%zd\n",
(uintmax_t)VTOI(vp), uio->uio_offset, uio->uio_resid);
- if (fuse_isdeadfs(vp)) {
- return ENXIO;
- }
+ if (fuse_isdeadfs(vp))
+ return (ENXIO);
- if (VTOFUD(vp)->flag & FN_DIRECTIO) {
+ if ((VTOFUD(vp)->flag & FN_DIRECTIO) != 0)
ioflag |= IO_DIRECT;
- }
- return fuse_io_dispatch(vp, uio, ioflag, cred);
+ err = 0;
+ freecred = 0;
+ if ((vnode_mount(vp)->mnt_flag & MNT_EXPORTED) != 0 &&
+ fuse_filehandle_validrw(vp, FUFH_RDONLY) == FUFH_INVALID) {
+ FS_DEBUG("doing open() before read");
+ /*
+ * This should only happen when VOP_READ() is done by an
+ * nfsd thread. Since the nfsd thread has already done
+ * permission checks, I believe it is safe to open the
+ * file as root.
+ */
+ cred = crget();
+ cred->cr_uid = cred->cr_ruid = cred->cr_svuid = 0;
+ gid = 0;
+ crsetgroups(cred, 1, &gid);
+ cred->cr_rgid = cred->cr_svgid = cred->cr_groups[0];
+ cred->cr_prison = &prison0;
+ prison_hold(cred->cr_prison);
+ freecred = 1;
+ err = fuse_filehandle_open(vp, FUFH_RDONLY, NULL, curthread,
+ cred);
+ } else
+ cred = ap->a_cred;
+
+ if (err == 0)
+ err = fuse_io_dispatch(vp, uio, ioflag, cred);
+
+#ifdef notdef
+ if (freefufh != 0)
+ fuse_filehandle_close(vp, FUFH_RDONLY, NULL, cred);
+#endif
+ if (freecred != 0)
+ crfree(cred);
+ return (err);
}
/*
@@ -1187,7 +1256,7 @@ fuse_vnop_read(struct vop_read_args *ap)
struct uio *a_uio;
struct ucred *a_cred;
int *a_eofflag;
- int *ncookies;
+ int *a_ncookies;
u_long **a_cookies;
};
*/
@@ -1203,10 +1272,14 @@ fuse_vnop_readdir(struct vop_readdir_arg
struct fuse_iov cookediov;
int err = 0;
- int freefufh = 0;
+ int ncookies;
+ u_long *cookies;
+ off_t startoff;
+ ssize_t tresid;
FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));
+ *ap->a_eofflag = 0;
if (fuse_isdeadfs(vp)) {
return ENXIO;
}
@@ -1216,25 +1289,61 @@ fuse_vnop_readdir(struct vop_readdir_arg
}
fvdat = VTOFUD(vp);
+ tresid = uio->uio_resid;
+ startoff = uio->uio_offset;
if (!fuse_filehandle_valid(vp, FUFH_RDONLY)) {
FS_DEBUG("calling readdir() before open()");
err = fuse_filehandle_open(vp, FUFH_RDONLY, &fufh, NULL, cred);
- freefufh = 1;
+ if (err == 0) {
+ /*
+ * When a directory is opened, it must be read from
+ * the beginning. Hopefully, the "startoff" still
+ * exists as an offset cookie for the directory.
+ * If not, it will read the entire directory without
+ * returning any entries and just return eof.
+ */
+ uio->uio_offset = 0;
+ }
} else {
err = fuse_filehandle_get(vp, FUFH_RDONLY, &fufh);
}
if (err) {
return (err);
}
+
+ if (ap->a_ncookies != NULL) {
+ ncookies = uio->uio_resid;
+ ncookies = ncookies / (offsetof(struct dirent, d_name) + 4) + 1;
+ cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
+ *ap->a_ncookies = ncookies;
+ *ap->a_cookies = cookies;
+ } else {
+ ncookies = 0;
+ cookies = NULL;
+ }
#define DIRCOOKEDSIZE FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + MAXNAMLEN + 1)
fiov_init(&cookediov, DIRCOOKEDSIZE);
- err = fuse_internal_readdir(vp, uio, fufh, &cookediov);
+ err = fuse_internal_readdir(vp, uio, startoff, fufh, &cookediov,
+ &ncookies, cookies);
fiov_teardown(&cookediov);
+#ifdef notdef
if (freefufh) {
fuse_filehandle_close(vp, FUFH_RDONLY, NULL, cred);
}
+#endif
+ if (ap->a_ncookies != NULL) {
+ if (err == 0) {
+ *ap->a_ncookies -= ncookies;
+ } else {
+ free(*ap->a_cookies, M_TEMP);
+ *ap->a_ncookies = 0;
+ *ap->a_cookies = NULL;
+ }
+ }
+ if (err == 0 && tresid == uio->uio_resid)
+ *ap->a_eofflag = 1;
return err;
}
@@ -1307,7 +1416,7 @@ fuse_vnop_reclaim(struct vop_reclaim_arg
for (type = 0; type < FUFH_MAXTYPE; type++) {
fufh = &(fvdat->fufh[type]);
if (FUFH_IS_VALID(fufh)) {
- printf("FUSE: vnode being reclaimed but fufh (type=%d) is valid",
+ FS_DEBUG("FUSE: vnode being reclaimed but fufh (type=%d) is valid",
type);
fuse_filehandle_close(vp, type, td, NULL);
}
@@ -1708,20 +1817,56 @@ fuse_vnop_write(struct vop_write_args *a
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
int ioflag = ap->a_ioflag;
- struct ucred *cred = ap->a_cred;
+ struct ucred *cred = NULL;
+ int err, freecred;
+ gid_t gid;
fuse_trace_printf_vnop();
- if (fuse_isdeadfs(vp)) {
- return ENXIO;
- }
- fuse_vnode_refreshsize(vp, cred);
+ if (fuse_isdeadfs(vp))
+ return (ENXIO);
+ fuse_vnode_refreshsize(vp, ap->a_cred);
- if (VTOFUD(vp)->flag & FN_DIRECTIO) {
+ if ((VTOFUD(vp)->flag & FN_DIRECTIO) != 0)
ioflag |= IO_DIRECT;
- }
- return fuse_io_dispatch(vp, uio, ioflag, cred);
+ err = 0;
+ freecred = 0;
+ if ((vnode_mount(vp)->mnt_flag & MNT_EXPORTED) != 0 &&
+ !fuse_filehandle_valid(vp, FUFH_RDWR)) {
+ FS_DEBUG("doing open() before write");
+ /*
+ * This should only happen when VOP_WRITE() is done by an
+ * nfsd thread. Since the nfsd thread has already done
+ * permission checks, I believe it is safe to open the
+ * file as root.
+ * The open is done FUFH_RDWR and not FUFH_WRONLY because
+ * a write of a partial buffer cache block will require
+ * that the block be read in first.
+ */
+ cred = crget();
+ cred->cr_uid = cred->cr_ruid = cred->cr_svuid = 0;
+ gid = 0;
+ crsetgroups(cred, 1, &gid);
+ cred->cr_rgid = cred->cr_svgid = cred->cr_groups[0];
+ cred->cr_prison = &prison0;
+ prison_hold(cred->cr_prison);
+ freecred = 1;
+ err = fuse_filehandle_open(vp, FUFH_RDWR, NULL, curthread,
+ cred);
+ } else
+ cred = ap->a_cred;
+
+ if (err == 0)
+ err = fuse_io_dispatch(vp, uio, ioflag, cred);
+
+#ifdef notdef
+ if (freefufh != 0)
+ fuse_filehandle_close(vp, FUFH_RDWR, NULL, cred);
+#endif
+ if (freecred != 0)
+ crfree(cred);
+ return (err);
}
/*
@@ -1752,24 +1897,35 @@ fuse_vnop_getpages(struct vop_getpages_a
td = curthread; /* XXX */
cred = curthread->td_ucred; /* XXX */
pages = ap->a_m;
- npages = ap->a_count;
+ count = ap->a_count;
if (!fsess_opt_mmap(vnode_mount(vp))) {
FS_DEBUG("called on non-cacheable vnode??\n");
return (VM_PAGER_ERROR);
}
+ npages = btoc(count);
/*
- * 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?
+ * If the requested 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.
*/
+
VM_OBJECT_WLOCK(vp->v_object);
- if (pages[npages - 1]->valid != 0 && --npages == 0)
- goto out;
+ fuse_vm_page_lock_queues();
+ if (pages[ap->a_reqpage]->valid != 0) {
+ for (i = 0; i < npages; ++i) {
+ if (i != ap->a_reqpage) {
+ fuse_vm_page_lock(pages[i]);
+ vm_page_free(pages[i]);
+ fuse_vm_page_unlock(pages[i]);
+ }
+ }
+ fuse_vm_page_unlock_queues();
+ VM_OBJECT_WUNLOCK(vp->v_object);
+ return 0;
+ }
+ fuse_vm_page_unlock_queues();
VM_OBJECT_WUNLOCK(vp->v_object);
/*
@@ -1783,7 +1939,6 @@ fuse_vnop_getpages(struct vop_getpages_a
PCPU_INC(cnt.v_vnodein);
PCPU_ADD(cnt.v_vnodepgsin, npages);
- count = npages << PAGE_SHIFT;
iov.iov_base = (caddr_t)kva;
iov.iov_len = count;
uio.uio_iov = &iov;
@@ -1801,6 +1956,17 @@ fuse_vnop_getpages(struct vop_getpages_a
if (error && (uio.uio_resid == count)) {
FS_DEBUG("error %d\n", error);
+ VM_OBJECT_WLOCK(vp->v_object);
+ fuse_vm_page_lock_queues();
+ for (i = 0; i < npages; ++i) {
+ if (i != ap->a_reqpage) {
+ fuse_vm_page_lock(pages[i]);
+ vm_page_free(pages[i]);
+ fuse_vm_page_unlock(pages[i]);
+ }
+ }
+ fuse_vm_page_unlock_queues();
+ VM_OBJECT_WUNLOCK(vp->v_object);
return VM_PAGER_ERROR;
}
/*
@@ -1841,15 +2007,12 @@ fuse_vnop_getpages(struct vop_getpages_a
*/
;
}
+ if (i != ap->a_reqpage)
+ vm_page_readahead_finish(m);
}
fuse_vm_page_unlock_queues();
-out:
VM_OBJECT_WUNLOCK(vp->v_object);
- if (ap->a_rbehind)
- *ap->a_rbehind = 0;
- if (ap->a_rahead)
- *ap->a_rahead = 0;
- return (VM_PAGER_OK);
+ return 0;
}
/*
@@ -1962,3 +2125,411 @@ fuse_vnop_print(struct vop_print_args *a
return 0;
}
+
+/*
+ struct vop_getextattr_args {
+ 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;
+ };
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list