Date: Thu, 22 Apr 2004 12:02:26 -0700 (PDT) From: <jonathan@decru.com> To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/65901: smbfs fails fsx write/truncate-down/truncate-up/reread old write +FIX Message-ID: <200404221902.i3MJ2Q775166@fbsd2.hq.decru.com> Resent-Message-ID: <200404222330.i3MNUJ8Y099217@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>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@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.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200404221902.i3MJ2Q775166>