kern/65901: smbfs fails fsx write/truncate-down/truncate-up/reread old write +FIX

jonathan at decru.com jonathan at decru.com
Thu Apr 22 16:30:19 PDT 2004


>Number:         65901
>Category:       kern
>Synopsis:       smbfs fails fsx write/truncate-down/truncate-up/reread old write +FIX
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Apr 22 16:30:19 PDT 2004
>Closed-Date:
>Last-Modified:
>Originator:     
>Release:        FreeBSD 4.7-RELEASE; FreeBSD-4.8-RELEASE; also seen from
>Organization:
Representing self
>Environment:
System: FreeBSD jonathan80.decru.com 4.7-RELEASE FreeBSD 4.7-RELEASE #27: Tue Jul 22 15:09:28 PDT 2003     root at jonathan80.decru.com:/usr/src-4.7/sys/compile/GENERIC.BIG.SMP  i386

	i386, FreeBSD RELENG_4 with all versions of smbfs that I've tested.
	I don't run FreeBSD-5, so I cant test it there.

>Description:

	Run fsx with -RW. (smbfs does not implement consistency between
	read-write access on the one hand, and mmap access on the other;
	using fsx -R -W avoids testing for those (known) bugs.)

	When  fsx issues the  sequence of operations:
	    1.  write data, X.
	    2.  truncate file down below X.
	    3.  truncate file back above upper edge of X.
	    4.  read from offset of prior data X

	then smbfs passes back the old prior file content, X,
	when it should instead return zeroes.

>How-To-Repeat:

	repeat as above using fsx.

>Fix:

The patch below could be described as a "hot glue gun" approach; but
its also a thorough, suspenders-and-braces fix for this problem
(which, btw, does not occur in the NetBSD version of smbfs.)

I'm not really au-fait with the FreeBSD VM system, so I examined the
FreeBSD-4 NFS client code (taken from FreeBSD 4.7's sys/nfs as at
around April 2002) for the corresponding scenario under NFS;, and
patched similar handling into the smbfs code. it may well be that not
all the changes below are necessary; but they appear sufficient.

This patch also applies cleanly to FreeBSD 4-stable as recent up to as
recent as 4.9-STABLE January 2004; for me, it fixes the reported
problem on all those releases.  (If the problem is already fixed
elsewhere in 4.x, I didn't notice).


The timestamps on the diff below are genuine. Someone more familiar
with the 4-STABLE NFS code might wish to subsequent later fixes into
this patch; or reconstruct a similar fix starting from more recent
4-STABLE NFS code.   I can't speak to FreeBSD-5.



--- smbfs_vnops.c.DIST	Sun Apr 21 21:18:58 2002
+++ smbfs_vnops.c	Fri Dec 27 19:45:35 2002
@@ -340,6 +340,113 @@
 	return 0;
 }
 
+/*XXX code stloen from NFS */
+static struct buf *	smb_getcacheblk(struct vnode *vp, daddr_t bn,
+					int size, struct proc * p);
+int			smb_meta_setsize(struct vnode *vp, struct ucred *cred,
+					 struct proc *p, u_quad_t nsize);
+
+/*
+ * Get an smb [was nfs] cache block.
+ *
+ * Allocate a new one if the block isn't currently in the cache
+ * and return the block marked busy. If the calling process is
+ * interrupted by a signal for an interruptible mount point, return
+ * NULL.
+ *
+ * The caller must carefully deal with the possible B_INVAL state of
+ * the buffer.  nfs_doio() clears B_INVAL (and nfs_asyncio() clears it
+ * indirectly), so synchronous reads can be issued without worrying about
+ * the B_INVAL state.  We have to be a little more careful when dealing
+ * with writes (see comments in nfs_write()) when extending a file past
+ * its EOF.
+ */
+
+static struct buf *
+smb_getcacheblk(vp, bn, size, p)
+	struct vnode *vp;
+	daddr_t bn;
+	int size;
+	struct proc *p;
+{
+	register struct buf *bp;
+	struct mount *mp;
+	struct smbmount *nmp;
+
+	mp = vp->v_mount;
+#if 0
+	nmp = VTOSMB(mp);
+#else
+	nmp = NULL;
+#endif
+
+#if 0
+	if (nmp && nmp->nm_flag & NFSMNT_INT) {
+		bp = getblk(vp, bn, size, PCATCH, 0);
+		while (bp == (struct buf *)0) {
+			if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
+				return ((struct buf *)0);
+			bp = getblk(vp, bn, size, 0, 2 * hz);
+		}
+	} else 
+#endif
+	{
+		bp = getblk(vp, bn, size, 0, 0);
+	}
+
+	if (vp->v_type == VREG) {
+		int biosize;
+
+		biosize = mp->mnt_stat.f_iosize;
+		bp->b_blkno = bn * (biosize / DEV_BSIZE);
+	}
+	return (bp);
+}
+
+/*
+ * Used to aid in handling ftruncate() operations on the NFS client side.
+ * Truncation creates a number of special problems for NFS.  We have to
+ * throw away VM pages and buffer cache buffers that are beyond EOF, and
+ * we have to properly handle VM pages or (potentially dirty) buffers
+ * that straddle the truncation point.
+ */
+
+int
+smb_meta_setsize(struct vnode *vp, struct ucred *cred, struct proc *p, u_quad_t nsize)
+{
+	struct smbnode *np = VTOSMB(vp);
+	u_quad_t tsize = np->n_size;
+	int biosize = vp->v_mount->mnt_stat.f_iosize;
+	int error = 0;
+
+	np->n_size = nsize;
+
+	if (np->n_size < tsize) {
+		struct buf *bp;
+		daddr_t lbn;
+		int bufsize;
+
+		/*
+		 * vtruncbuf() doesn't get the buffer overlapping the 
+		 * truncation point.  We may have a B_DELWRI and/or B_CACHE
+		 * buffer that now needs to be truncated.
+		 */
+		error = vtruncbuf(vp, cred, p, nsize, biosize);
+		lbn = nsize / biosize;
+		bufsize = nsize & (biosize - 1);
+		bp = smb_getcacheblk(vp, lbn, bufsize, p);
+		if (bp->b_dirtyoff > bp->b_bcount)
+			bp->b_dirtyoff = bp->b_bcount;
+		if (bp->b_dirtyend > bp->b_bcount)
+			bp->b_dirtyend = bp->b_bcount;
+		bp->b_flags |= B_RELBUF;  /* don't leave garbage around */
+		brelse(bp);
+	} else {
+		vnode_pager_setsize(vp, nsize);
+	}
+	return(error);
+}
+
 static int
 smbfs_setattr(ap)
 	struct vop_setattr_args /* {
@@ -383,7 +490,10 @@
 		if (isreadonly)
 			return EROFS;
 		doclose = 0;
+
 		vnode_pager_setsize(vp, (u_long)vap->va_size);
+		smb_meta_setsize(vp, ap->a_cred, ap->a_p, vap->va_size);
+
  		tsize = np->n_size;
  		np->n_size = vap->va_size;
 		if (np->n_opencount == 0) {


>Release-Note:
>Audit-Trail:
>Unformatted:
 		approximately FreeBSD-4.6-RELEASE through 4.9-RC1.


More information about the freebsd-bugs mailing list