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