Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 23 May 2019 23:06:26 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r348209 - projects/fuse2/sys/fs/fuse
Message-ID:  <201905232306.x4NN6QeN019302@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: asomers
Date: Thu May 23 23:06:26 2019
New Revision: 348209
URL: https://svnweb.freebsd.org/changeset/base/348209

Log:
  fusefs: fix exporting fuse filesystems with nfsd
  
  A previous commit made fuse exportable via userland NFS servers.
  Compatibility with the in-kernel nfsd required two more changes:
  
  * During read and write operations, implicitly do a FUSE_OPEN if there isn't
    already a valid file handle.  That's because nfsd never calls VOP_OPEN.
  * During VOP_READDIR, if an implicit open was necessary, directory offsets
    from a previous VOP_READDIR may not be valid, so VOP_READDIR may have to
    start from the beginning and read until it encounters the requested
    offset.
  
  I've done only limited testing over NFS, so there are probably still some
  more bugs.  Thanks to rmacklem for all of the readdir changes, which he had
  made for his pnfs work.
  
  Sponsored by:	The FreeBSD Foundation

Modified:
  projects/fuse2/sys/fs/fuse/fuse_file.c
  projects/fuse2/sys/fs/fuse/fuse_internal.c
  projects/fuse2/sys/fs/fuse/fuse_internal.h
  projects/fuse2/sys/fs/fuse/fuse_io.c
  projects/fuse2/sys/fs/fuse/fuse_vnops.c

Modified: projects/fuse2/sys/fs/fuse/fuse_file.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_file.c	Thu May 23 22:57:57 2019	(r348208)
+++ projects/fuse2/sys/fs/fuse/fuse_file.c	Thu May 23 23:06:26 2019	(r348209)
@@ -286,6 +286,7 @@ fuse_filehandle_get(struct vnode *vp, int fflag,
 	fufh_type_t fufh_type;
 
 	fufh_type = fflags_2_fufh_type(fflag);
+	/* cred can be NULL for in-kernel clients */
 	if (cred == NULL)
 		goto fallback;
 

Modified: projects/fuse2/sys/fs/fuse/fuse_internal.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_internal.c	Thu May 23 22:57:57 2019	(r348208)
+++ projects/fuse2/sys/fs/fuse/fuse_internal.c	Thu May 23 23:06:26 2019	(r348209)
@@ -344,16 +344,19 @@ fuse_internal_mknod(struct vnode *dvp, struct vnode **
 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 = NULL;
+	int fnd_start;
 
-	if (uio_resid(uio) == 0) {
+	if (uio_resid(uio) == 0)
 		return 0;
-	}
 	fdisp_init(&fdi, 0);
 
 	/*
@@ -361,6 +364,17 @@ 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);
 		if (fri == NULL)
@@ -374,40 +388,46 @@ fuse_internal_readdir(struct vnode *vp,
 		fri->size = MIN(uio->uio_resid,
 		    fuse_get_mpdata(vp->v_mount)->max_read);
 
-		    if ((err = fdisp_wait_answ(&fdi))) {
+		if ((err = fdisp_wait_answ(&fdi)))
 			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)))
 			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;
 
-	if (bufsize < FUSE_NAME_OFFSET) {
+	cookies = *cookiesp;
+	if (bufsize < FUSE_NAME_OFFSET)
 		return -1;
-	}
 	for (;;) {
-
 		if (bufsize < FUSE_NAME_OFFSET) {
 			err = -1;
 			break;
@@ -415,10 +435,12 @@ fuse_internal_readdir_processdata(struct uio *uio,
 		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
@@ -436,30 +458,48 @@ fuse_internal_readdir_processdata(struct uio *uio,
 					    &fudge->namelen);
 
 		if (bytesavail > uio_resid(uio)) {
+			/* Out of space for the dir so we are done. */
 			err = -1;
 			break;
 		}
-		fiov_adjust(cookediov, bytesavail);
-		bzero(cookediov->base, bytesavail);
+		/*
+		 * Don't start to copy the directory entries out until
+		 * the requested offset in the directory is found.
+		 */
+		if (*fnd_start != 0) {
+			readany = true;
+			fiov_adjust(cookediov, bytesavail);
+			bzero(cookediov->base, bytesavail);
 
-		de = (struct dirent *)cookediov->base;
-		de->d_fileno = fudge->ino;
-		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);
-		dirent_terminate(de);
+			de = (struct dirent *)cookediov->base;
+			de->d_fileno = fudge->ino;
+			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);
+			dirent_terminate(de);
 
-		err = uiomove(cookediov->base, cookediov->len, uio);
-		if (err) {
-			break;
-		}
+			err = uiomove(cookediov->base, cookediov->len, uio);
+			if (err)
+				break;
+			if (cookies != NULL) {
+				if (*ncookies == 0) {
+					err = -1;
+					break;
+				}
+				*cookies = fudge->off;
+				cookies++;
+				(*ncookies)--;
+			}
+		} else if (startoff == fudge->off)
+			*fnd_start = 1;
 		buf = (char *)buf + freclen;
 		bufsize -= freclen;
 		uio_setoffset(uio, fudge->off);
 	}
+	*cookiesp = cookies;
 
 	return err;
 }

Modified: projects/fuse2/sys/fs/fuse/fuse_internal.h
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_internal.h	Thu May 23 22:57:57 2019	(r348208)
+++ projects/fuse2/sys/fs/fuse/fuse_internal.h	Thu May 23 23:06:26 2019	(r348209)
@@ -236,10 +236,12 @@ int fuse_internal_mknod(struct vnode *dvp, struct vnod
 	struct componentname *cnp, struct vattr *vap);
 
 /* readdir */
-int fuse_internal_readdir(struct vnode *vp, struct uio *uio,
-    struct fuse_filehandle *fufh, struct fuse_iov *cookediov);
-int fuse_internal_readdir_processdata(struct uio *uio, size_t reqsize,
-    void *buf, size_t bufsize, void *param);
+int fuse_internal_readdir(struct vnode *vp, struct uio *uio, off_t startoff,
+    struct fuse_filehandle *fufh, struct fuse_iov *cookediov, int *ncookies,
+    u_long *cookies);
+int fuse_internal_readdir_processdata(struct uio *uio, off_t startoff,
+    int *fnd_start, size_t reqsize, void *buf, size_t bufsize,
+    struct fuse_iov *cookediov, int *ncookies, u_long **cookiesp);
 
 /* remove */
 

Modified: projects/fuse2/sys/fs/fuse/fuse_io.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_io.c	Thu May 23 22:57:57 2019	(r348208)
+++ projects/fuse2/sys/fs/fuse/fuse_io.c	Thu May 23 23:06:26 2019	(r348209)
@@ -170,15 +170,26 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, in
 	struct fuse_filehandle *fufh;
 	int err, directio;
 	int fflag;
+	bool closefufh = false;
 
 	MPASS(vp->v_type == VREG || vp->v_type == VDIR);
 
 	fflag = (uio->uio_rw == UIO_READ) ? FREAD : FWRITE;
 	err = fuse_filehandle_getrw(vp, fflag, &fufh, cred, pid);
-	if (err) {
+	if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
+		/* 
+		 * nfsd will do I/O without first doing VOP_OPEN.  We
+		 * must implicitly open the file here
+		 */
+		err = fuse_filehandle_open(vp, fflag, &fufh, curthread, cred);
+		closefufh = true;
+	}
+	else if (err) {
 		printf("FUSE: io dispatch: filehandles are closed\n");
 		return err;
 	}
+	if (err)
+		goto out;
 	SDT_PROBE5(fusefs, , io, io_dispatch, vp, uio, ioflag, cred, fufh);
 
 	/*
@@ -222,7 +233,7 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, in
 
 			err = fuse_vnode_size(vp, &filesize, cred, curthread);
 			if (err)
-				return err;
+				goto out;
 
 			start = uio->uio_offset;
 			end = start + uio->uio_resid;
@@ -246,6 +257,10 @@ fuse_io_dispatch(struct vnode *vp, struct uio *uio, in
 	default:
 		panic("uninterpreted mode passed to fuse_io_dispatch");
 	}
+
+out:
+	if (closefufh)
+		fuse_filehandle_close(vp, fufh, curthread, cred);
 
 	return (err);
 }

Modified: projects/fuse2/sys/fs/fuse/fuse_vnops.c
==============================================================================
--- projects/fuse2/sys/fs/fuse/fuse_vnops.c	Thu May 23 22:57:57 2019	(r348208)
+++ projects/fuse2/sys/fs/fuse/fuse_vnops.c	Thu May 23 23:06:26 2019	(r348209)
@@ -1176,7 +1176,7 @@ fuse_vnop_mknod(struct vop_mknod_args *ap)
 }
 
 /*
-    struct vnop_open_args {
+    struct vop_open_args {
 	struct vnode *a_vp;
 	int  a_mode;
 	struct ucred *a_cred;
@@ -1270,7 +1270,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;
     };
 */
@@ -1283,8 +1283,15 @@ fuse_vnop_readdir(struct vop_readdir_args *ap)
 	struct fuse_filehandle *fufh = NULL;
 	struct fuse_iov cookediov;
 	int err = 0;
+	u_long *cookies;
+	off_t startoff;
+	ssize_t tresid;
+	int ncookies;
+	bool closefufh = false;
 	pid_t pid = curthread->td_proc->p_pid;
 
+	if (ap->a_eofflag)
+		*ap->a_eofflag = 0;
 	if (fuse_isdeadfs(vp)) {
 		return ENXIO;
 	}
@@ -1293,15 +1300,60 @@ fuse_vnop_readdir(struct vop_readdir_args *ap)
 		return EINVAL;
 	}
 
+	tresid = uio->uio_resid;
+	startoff = uio->uio_offset;
 	err = fuse_filehandle_get_dir(vp, &fufh, cred, pid);
+	if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) {
+		/* 
+		 * nfsd will do VOP_READDIR without first doing VOP_OPEN.  We
+		 * must implicitly open the directory here
+		 */
+		err = fuse_filehandle_open(vp, FREAD, &fufh, curthread, cred);
+		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;
+		}
+		closefufh = true;
+	}
 	if (err)
 		return (err);
+	if (ap->a_ncookies != NULL) {
+		ncookies = uio->uio_resid /
+			(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);
+	if (closefufh)
+		fuse_filehandle_close(vp, fufh, curthread, cred);
+
+	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;
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201905232306.x4NN6QeN019302>