struct vnode a_vp;
struct vnode *a_vp;
nfs_write(v)	void *v;{	struct vop_write_args /* {		struct vnode a_vp;		struct uio *a_uio;		int  a_ioflag;		struct ucred *a_cred;	} */ *ap = v;	register int biosize;	register struct uio *uio = ap->a_uio;	struct proc *p = uio->uio_procp;	register struct vnode *vp = ap->a_vp;	struct nfsnode *np = VTONFS(vp);	register struct ucred *cred = ap->a_cred;	int ioflag = ap->a_ioflag;	struct buf *bp;	struct vattr vattr;	struct nfsmount *nmp;	daddr_t lbn, bn;	int n, on, error = 0;#ifdef DIAGNOSTIC	if (uio->uio_rw != UIO_WRITE)		panic("nfs_write mode");	if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)		panic("nfs_write proc");#endif	if (vp->v_type != VREG)		return (EIO);	if (np->n_flag & NWRITEERR) {		np->n_flag &= ~NWRITEERR;		return (np->n_error);	}	if (ioflag & (IO_APPEND | IO_SYNC)) {		if (np->n_flag & NMODIFIED) {			np->n_attrstamp = 0;			error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);			if (error)				return (error);		}		if (ioflag & IO_APPEND) {			np->n_attrstamp = 0;			error = VOP_GETATTR(vp, &vattr, cred, p);			if (error)				return (error);			uio->uio_offset = np->n_size;		}	}	nmp = VFSTONFS(vp->v_mount);	if (uio->uio_offset < 0)		return (EINVAL);	if (uio->uio_resid == 0)		return (0);	/*	 * Maybe this should be above the vnode op call, but so long as	 * file servers have no limits, i don't think it matters	 */	if (p && uio->uio_offset + uio->uio_resid >	      p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {		psignal(p, SIGXFSZ);		return (EFBIG);	}	/*	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks	 * will be the same size within a filesystem. nfs_writerpc will	 * still use nm_wsize when sizing the rpc's.	 */	biosize = nmp->nm_rsize;	do {		/*		 * XXX make sure we aren't cached in the VM page cache		 */		(void)vnode_pager_uncache(vp);		/*		 * Check for a valid write lease.		 * If non-cachable, just do the rpc		 */		if ((nmp->nm_flag & NFSMNT_NQNFS) &&		    NQNFS_CKINVALID(vp, np, NQL_WRITE)) {			do {				error = nqnfs_getlease(vp, NQL_WRITE, cred, p);			} while (error == NQNFS_EXPIRED);			if (error)				return (error);			if (np->n_lrev != np->n_brev ||			    (np->n_flag & NQNFSNONCACHE)) {				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);				if (error)					return (error);				np->n_brev = np->n_lrev;			}		}		if (np->n_flag & NQNFSNONCACHE)			return (nfs_writerpc(vp, uio, cred, ioflag));		nfsstats.biocache_writes++;		lbn = uio->uio_offset / biosize;		on = uio->uio_offset & (biosize-1);		n = min((unsigned)(biosize - on), uio->uio_resid);		bn = lbn * (biosize / DEV_BSIZE);again:		bp = nfs_getcacheblk(vp, bn, biosize, p);		if (!bp)			return (EINTR);		if (bp->b_wcred == NOCRED) {			crhold(cred);			bp->b_wcred = cred;		}		np->n_flag |= NMODIFIED;		if (uio->uio_offset + n > np->n_size) {			np->n_size = uio->uio_offset + n;			vnode_pager_setsize(vp, (u_long)np->n_size);		}		/*		 * If the new write will leave a contiguous dirty		 * area, just update the b_dirtyoff and b_dirtyend,		 * otherwise force a write rpc of the old dirty area.		 */		if (bp->b_dirtyend > 0 &&		    (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {			bp->b_proc = p;			if (VOP_BWRITE(bp) == EINTR)				return (EINTR);			goto again;		}		/*		 * Check for valid write lease and get one as required.		 * In case getblk() and/or bwrite() delayed us.		 */		if ((nmp->nm_flag & NFSMNT_NQNFS) &&		    NQNFS_CKINVALID(vp, np, NQL_WRITE)) {			do {				error = nqnfs_getlease(vp, NQL_WRITE, cred, p);			} while (error == NQNFS_EXPIRED);			if (error) {				brelse(bp);				return (error);			}			if (np->n_lrev != np->n_brev ||			    (np->n_flag & NQNFSNONCACHE)) {				brelse(bp);				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);				if (error)					return (error);				np->n_brev = np->n_lrev;				goto again;			}		}		error = uiomove((char *)bp->b_data + on, n, uio);		if (error) {			bp->b_flags |= B_ERROR;			brelse(bp);			return (error);		}		if (bp->b_dirtyend > 0) {			bp->b_dirtyoff = min(on, bp->b_dirtyoff);			bp->b_dirtyend = max((on + n), bp->b_dirtyend);		} else {			bp->b_dirtyoff = on;			bp->b_dirtyend = on + n;		}#ifndef notdef		if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||		    bp->b_validoff > bp->b_dirtyend) {			bp->b_validoff = bp->b_dirtyoff;			bp->b_validend = bp->b_dirtyend;		} else {			bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);			bp->b_validend = max(bp->b_validend, bp->b_dirtyend);		}#else		bp->b_validoff = bp->b_dirtyoff;		bp->b_validend = bp->b_dirtyend;#endif		if (ioflag & IO_APPEND)			bp->b_flags |= B_APPENDWRITE;		/*		 * If the lease is non-cachable or IO_SYNC do bwrite().		 */		if ((np->n_flag & NQNFSNONCACHE) || (ioflag & IO_SYNC)) {			bp->b_proc = p;			if ((error = VOP_BWRITE(bp)) != 0)				return (error);		} else if ((n + on) == biosize &&			(nmp->nm_flag & NFSMNT_NQNFS) == 0) {			bp->b_proc = (struct proc *)0;			bawrite(bp);		} else			bdwrite(bp);	} while (uio->uio_resid > 0 && n > 0);	return (0);}