smbfs_io.c revision 111742
151090Ssheldonh/*
251090Ssheldonh * Copyright (c) 2000-2001, Boris Popov
351090Ssheldonh * All rights reserved.
451090Ssheldonh *
551090Ssheldonh * Redistribution and use in source and binary forms, with or without
651090Ssheldonh * modification, are permitted provided that the following conditions
751090Ssheldonh * are met:
851090Ssheldonh * 1. Redistributions of source code must retain the above copyright
951090Ssheldonh *    notice, this list of conditions and the following disclaimer.
1051090Ssheldonh * 2. Redistributions in binary form must reproduce the above copyright
1151090Ssheldonh *    notice, this list of conditions and the following disclaimer in the
1251090Ssheldonh *    documentation and/or other materials provided with the distribution.
1351090Ssheldonh * 3. All advertising materials mentioning features or use of this software
1451090Ssheldonh *    must display the following acknowledgement:
1551090Ssheldonh *    This product includes software developed by Boris Popov.
1651090Ssheldonh * 4. Neither the name of the author nor the names of any co-contributors
1751090Ssheldonh *    may be used to endorse or promote products derived from this software
1851090Ssheldonh *    without specific prior written permission.
1951090Ssheldonh *
2051090Ssheldonh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2151090Ssheldonh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2251090Ssheldonh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2351090Ssheldonh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2451090Ssheldonh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2551090Ssheldonh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2651090Ssheldonh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27141580Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2851090Ssheldonh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29216629Sjilles * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3051090Ssheldonh * SUCH DAMAGE.
3151090Ssheldonh *
3251090Ssheldonh * $FreeBSD: head/sys/fs/smbfs/smbfs_io.c 111742 2003-03-02 15:56:49Z des $
3351090Ssheldonh *
34149912Sgarys */
35165559Sru#include <sys/param.h>
36149912Sgarys#include <sys/systm.h>
37149912Sgarys#include <sys/resourcevar.h>	/* defines plimit structure in proc struct */
38165559Sru#include <sys/kernel.h>
39207166Sjilles#include <sys/proc.h>
40165559Sru#include <sys/fcntl.h>
41165559Sru#include <sys/bio.h>
4251090Ssheldonh#include <sys/buf.h>
4351090Ssheldonh#include <sys/mount.h>
4451090Ssheldonh#include <sys/namei.h>
45100574Stjr#include <sys/vnode.h>
4660261Ssheldonh#include <sys/dirent.h>
4751090Ssheldonh#include <sys/signalvar.h>
4851090Ssheldonh#include <sys/sysctl.h>
4960261Ssheldonh#include <sys/vmmeter.h>
5051090Ssheldonh
5151090Ssheldonh#include <vm/vm.h>
5251090Ssheldonh#include <vm/vm_page.h>
5351090Ssheldonh#include <vm/vm_extern.h>
5460261Ssheldonh#include <vm/vm_object.h>
5551090Ssheldonh#include <vm/vm_pager.h>
5651090Ssheldonh#include <vm/vnode_pager.h>
5751090Ssheldonh/*
5851090Ssheldonh#include <sys/ioccom.h>
5951090Ssheldonh*/
6051090Ssheldonh#include <netsmb/smb.h>
6160261Ssheldonh#include <netsmb/smb_conn.h>
6251090Ssheldonh#include <netsmb/smb_subr.h>
6351090Ssheldonh
6451090Ssheldonh#include <fs/smbfs/smbfs.h>
6551090Ssheldonh#include <fs/smbfs/smbfs_node.h>
6651090Ssheldonh#include <fs/smbfs/smbfs_subr.h>
6751090Ssheldonh
6851090Ssheldonh/*#define SMBFS_RWGENERIC*/
6951090Ssheldonh
7051090Ssheldonhextern int smbfs_pbuf_freecnt;
7151090Ssheldonh
7286505Sknustatic int smbfs_fastlookup = 1;
7351090Ssheldonh
7451090SsheldonhSYSCTL_DECL(_vfs_smbfs);
7560261SsheldonhSYSCTL_INT(_vfs_smbfs, OID_AUTO, fastlookup, CTLFLAG_RW, &smbfs_fastlookup, 0, "");
7651090Ssheldonh
7751090Ssheldonh
7851090Ssheldonh#define DE_SIZE	(sizeof(struct dirent))
7951090Ssheldonh
8051090Ssheldonhstatic int
8151090Ssheldonhsmbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred)
8251090Ssheldonh{
8351090Ssheldonh	struct dirent de;
8451090Ssheldonh	struct componentname cn;
8560261Ssheldonh	struct smb_cred scred;
8651090Ssheldonh	struct smbfs_fctx *ctx;
8751090Ssheldonh	struct vnode *newvp;
8851090Ssheldonh	struct smbnode *np = VTOSMB(vp);
8951090Ssheldonh	int error/*, *eofflag = ap->a_eofflag*/;
9051090Ssheldonh	long offset, limit;
91149912Sgarys
9260261Ssheldonh	np = VTOSMB(vp);
9351090Ssheldonh	SMBVDEBUG("dirname='%s'\n", np->n_name);
9451090Ssheldonh	smb_makescred(&scred, uio->uio_td, cred);
9560261Ssheldonh	offset = uio->uio_offset / DE_SIZE;	/* offset in the directory */
9651090Ssheldonh	limit = uio->uio_resid / DE_SIZE;
9751090Ssheldonh	if (uio->uio_resid < DE_SIZE || uio->uio_offset < 0)
9851090Ssheldonh		return EINVAL;
9951090Ssheldonh	while (limit && offset < 2) {
10051090Ssheldonh		limit--;
10160261Ssheldonh		bzero((caddr_t)&de, DE_SIZE);
102215520Sjilles		de.d_reclen = DE_SIZE;
10351090Ssheldonh		de.d_fileno = (offset == 0) ? np->n_ino :
10451090Ssheldonh		    (np->n_parent ? VTOSMB(np->n_parent)->n_ino : 2);
10557674Ssheldonh		if (de.d_fileno == 0)
10651090Ssheldonh			de.d_fileno = 0x7ffffffd + offset;
10751090Ssheldonh		de.d_namlen = offset + 1;
10851090Ssheldonh		de.d_name[0] = '.';
109149912Sgarys		de.d_name[1] = '.';
11060261Ssheldonh		de.d_name[offset + 1] = '\0';
11151090Ssheldonh		de.d_type = DT_DIR;
11251090Ssheldonh		error = uiomove(&de, DE_SIZE, uio);
11360261Ssheldonh		if (error)
11460261Ssheldonh			return error;
11551090Ssheldonh		offset++;
11651090Ssheldonh		uio->uio_offset += DE_SIZE;
11751090Ssheldonh	}
11851090Ssheldonh	if (limit == 0)
11951090Ssheldonh		return 0;
12051090Ssheldonh	if (offset != np->n_dirofs || np->n_dirseq == NULL) {
12160261Ssheldonh		SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs);
12286505Sknu		if (np->n_dirseq) {
12351090Ssheldonh			smbfs_findclose(np->n_dirseq, &scred);
12451090Ssheldonh			np->n_dirseq = NULL;
125153092Sstefanf		}
12651090Ssheldonh		np->n_dirofs = 2;
12786505Sknu		error = smbfs_findopen(np, "*", 1,
12851090Ssheldonh		    SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR,
12951090Ssheldonh		    &scred, &ctx);
13051090Ssheldonh		if (error) {
13151090Ssheldonh			SMBVDEBUG("can not open search, error = %d", error);
13260261Ssheldonh			return error;
13351090Ssheldonh		}
13451090Ssheldonh		np->n_dirseq = ctx;
13551090Ssheldonh	} else
13651090Ssheldonh		ctx = np->n_dirseq;
13751090Ssheldonh	while (np->n_dirofs < offset) {
13851090Ssheldonh		error = smbfs_findnext(ctx, offset - np->n_dirofs++, &scred);
13960261Ssheldonh		if (error) {
14051090Ssheldonh			smbfs_findclose(np->n_dirseq, &scred);
14151090Ssheldonh			np->n_dirseq = NULL;
142149912Sgarys			return error == ENOENT ? 0 : error;
14351090Ssheldonh		}
144163326Sru	}
14551090Ssheldonh	error = 0;
14651090Ssheldonh	for (; limit; limit--, offset++) {
14751090Ssheldonh		error = smbfs_findnext(ctx, limit, &scred);
14851090Ssheldonh		if (error)
14951090Ssheldonh			break;
15051090Ssheldonh		np->n_dirofs++;
15151090Ssheldonh		bzero((caddr_t)&de, DE_SIZE);
15251090Ssheldonh		de.d_reclen = DE_SIZE;
15379727Sschweikh		de.d_fileno = ctx->f_attr.fa_ino;
154165559Sru		de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG;
15551090Ssheldonh		de.d_namlen = ctx->f_nmlen;
15651275Ssheldonh		bcopy(ctx->f_name, de.d_name, de.d_namlen);
15751275Ssheldonh		de.d_name[de.d_namlen] = '\0';
158165559Sru		if (smbfs_fastlookup) {
15951090Ssheldonh			error = smbfs_nget(vp->v_mount, vp, ctx->f_name,
160149912Sgarys			    ctx->f_nmlen, &ctx->f_attr, &newvp);
161149912Sgarys			if (!error) {
16251090Ssheldonh				cn.cn_nameptr = de.d_name;
163165559Sru				cn.cn_namelen = de.d_namlen;
16451090Ssheldonh				cache_enter(vp, newvp, &cn);
165165559Sru				vput(newvp);
16651090Ssheldonh			}
16751090Ssheldonh		}
16851090Ssheldonh		error = uiomove(&de, DE_SIZE, uio);
16951090Ssheldonh		if (error)
17051090Ssheldonh			break;
17151090Ssheldonh	}
17251090Ssheldonh	if (error == ENOENT)
17351090Ssheldonh		error = 0;
17451090Ssheldonh	uio->uio_offset = offset * DE_SIZE;
17551090Ssheldonh	return error;
17651090Ssheldonh}
17751275Ssheldonh
178149912Sgarysint
179149912Sgaryssmbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred)
180149912Sgarys{
181149912Sgarys	struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
182149912Sgarys	struct smbnode *np = VTOSMB(vp);
183149912Sgarys	struct thread *td;
184149912Sgarys	struct vattr vattr;
185149912Sgarys	struct smb_cred scred;
186149912Sgarys	int error, lks;
187149912Sgarys
188149912Sgarys	/*
189149912Sgarys	 * Protect against method which is not supported for now
190142331Strhodes	 */
191142331Strhodes	if (uiop->uio_segflg == UIO_NOCOPY)
192142331Strhodes		return EOPNOTSUPP;
193142331Strhodes
194142331Strhodes	if (vp->v_type != VREG && vp->v_type != VDIR) {
195142331Strhodes		SMBFSERR("vn types other than VREG or VDIR are unsupported !\n");
19681285Sru		return EIO;
197209033Suqs	}
198207168Sjilles	if (uiop->uio_resid == 0)
199149912Sgarys		return 0;
200207168Sjilles	if (uiop->uio_offset < 0)
201207168Sjilles		return EINVAL;
202207817Sjilles/*	if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
203207166Sjilles		return EFBIG;*/
204165559Sru	td = uiop->uio_td;
205165559Sru	if (vp->v_type == VDIR) {
206165559Sru		lks = LK_EXCLUSIVE;/*lockstatus(vp->v_vnlock, td);*/
20751090Ssheldonh		if (lks == LK_SHARED)
208142331Strhodes			vn_lock(vp, LK_UPGRADE | LK_RETRY, td);
209100574Stjr		error = smbfs_readvdir(vp, uiop, cred);
21060261Ssheldonh		if (lks == LK_SHARED)
211165559Sru			vn_lock(vp, LK_DOWNGRADE | LK_RETRY, td);
21251090Ssheldonh		return error;
213142331Strhodes	}
21460261Ssheldonh
21551090Ssheldonh/*	biosize = SSTOCN(smp->sm_share)->sc_txmax;*/
216165559Sru	if (np->n_flag & NMODIFIED) {
217165559Sru		smbfs_attr_cacheremove(vp);
218165559Sru		error = VOP_GETATTR(vp, &vattr, cred, td);
21960261Ssheldonh		if (error)
220165559Sru			return error;
22151090Ssheldonh		np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
22251090Ssheldonh	} else {
22351090Ssheldonh		error = VOP_GETATTR(vp, &vattr, cred, td);
22451090Ssheldonh		if (error)
22551090Ssheldonh			return error;
22660261Ssheldonh		if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
22751090Ssheldonh			error = smbfs_vinvalbuf(vp, V_SAVE, cred, td, 1);
228165559Sru			if (error)
22951090Ssheldonh				return error;
23051090Ssheldonh			np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
23151090Ssheldonh		}
23251090Ssheldonh	}
23351090Ssheldonh	smb_makescred(&scred, td, cred);
23451090Ssheldonh	return smb_read(smp->sm_share, np->n_fid, uiop, &scred);
23551090Ssheldonh}
23651090Ssheldonh
23786505Sknuint
238165559Srusmbfs_writevnode(struct vnode *vp, struct uio *uiop,
239165559Sru	struct ucred *cred, int ioflag)
24060261Ssheldonh{
24151090Ssheldonh	struct smbmount *smp = VTOSMBFS(vp);
24251090Ssheldonh	struct smbnode *np = VTOSMB(vp);
24351090Ssheldonh	struct smb_cred scred;
244165559Sru	struct proc *p;
24551090Ssheldonh	struct thread *td;
24651090Ssheldonh	int error = 0;
247207196Sjilles
24851090Ssheldonh	if (vp->v_type != VREG) {
24951090Ssheldonh		SMBERROR("vn types other than VREG unsupported !\n");
25060261Ssheldonh		return EIO;
251165559Sru	}
25251090Ssheldonh	SMBVDEBUG("ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid);
253165559Sru	if (uiop->uio_offset < 0)
254216629Sjilles		return EINVAL;
25551090Ssheldonh/*	if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
256149912Sgarys		return (EFBIG);*/
25760261Ssheldonh	td = uiop->uio_td;
25851090Ssheldonh	p = td->td_proc;
25951090Ssheldonh	if (ioflag & (IO_APPEND | IO_SYNC)) {
26060261Ssheldonh		if (np->n_flag & NMODIFIED) {
26151090Ssheldonh			smbfs_attr_cacheremove(vp);
26251090Ssheldonh			error = smbfs_vinvalbuf(vp, V_SAVE, cred, td, 1);
26351090Ssheldonh			if (error)
26451090Ssheldonh				return error;
26551090Ssheldonh		}
26660261Ssheldonh		if (ioflag & IO_APPEND) {
267215520Sjilles#if notyet
26851090Ssheldonh			/*
26951090Ssheldonh			 * File size can be changed by another client
270165559Sru			 */
27151090Ssheldonh			smbfs_attr_cacheremove(vp);
27251090Ssheldonh			error = VOP_GETATTR(vp, &vattr, cred, td);
27351090Ssheldonh			if (error) return (error);
274149912Sgarys#endif
27560261Ssheldonh			uiop->uio_offset = np->n_size;
276165559Sru		}
27751090Ssheldonh	}
27860261Ssheldonh	if (uiop->uio_resid == 0)
27960261Ssheldonh		return 0;
28051090Ssheldonh	if (p && uiop->uio_offset + uiop->uio_resid > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
28151090Ssheldonh		PROC_LOCK(td->td_proc);
28251090Ssheldonh		psignal(td->td_proc, SIGXFSZ);
28351090Ssheldonh		PROC_UNLOCK(td->td_proc);
28451090Ssheldonh		return EFBIG;
28551090Ssheldonh	}
28660261Ssheldonh	smb_makescred(&scred, td, cred);
28786505Sknu	error = smb_write(smp->sm_share, np->n_fid, uiop, &scred);
28851090Ssheldonh	SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid);
28951090Ssheldonh	if (!error) {
290153092Sstefanf		if (uiop->uio_offset > np->n_size) {
29151090Ssheldonh			np->n_size = uiop->uio_offset;
29286505Sknu			vnode_pager_setsize(vp, np->n_size);
293207196Sjilles		}
294207196Sjilles	}
295165559Sru	return error;
296165559Sru}
29760261Ssheldonh
29851090Ssheldonh/*
29951090Ssheldonh * Do an I/O operation to/from a cache block.
30051090Ssheldonh */
30151090Ssheldonhint
30251090Ssheldonhsmbfs_doio(struct buf *bp, struct ucred *cr, struct thread *td)
303165559Sru{
30460261Ssheldonh	struct vnode *vp = bp->b_vp;
30551090Ssheldonh	struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
306165559Sru	struct smbnode *np = VTOSMB(vp);
30751090Ssheldonh	struct uio uio, *uiop = &uio;
30851090Ssheldonh	struct iovec io;
30951090Ssheldonh	struct smb_cred scred;
31051090Ssheldonh	int error = 0;
31186654Sknu
312142331Strhodes	uiop->uio_iov = &io;
31351090Ssheldonh	uiop->uio_iovcnt = 1;
31451090Ssheldonh	uiop->uio_segflg = UIO_SYSSPACE;
31551090Ssheldonh	uiop->uio_td = td;
31651090Ssheldonh
31760261Ssheldonh	smb_makescred(&scred, td, cr);
318215520Sjilles
31951090Ssheldonh	if (bp->b_iocmd == BIO_READ) {
32051090Ssheldonh	    io.iov_len = uiop->uio_resid = bp->b_bcount;
32186654Sknu	    io.iov_base = bp->b_data;
32251090Ssheldonh	    uiop->uio_rw = UIO_READ;
32386654Sknu	    switch (vp->v_type) {
32451090Ssheldonh	      case VREG:
32551090Ssheldonh		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
32651090Ssheldonh		error = smb_read(smp->sm_share, np->n_fid, uiop, &scred);
32751090Ssheldonh		if (error)
32851090Ssheldonh			break;
32957714Ssheldonh		if (uiop->uio_resid) {
33051090Ssheldonh			int left = uiop->uio_resid;
33151090Ssheldonh			int nread = bp->b_bcount - left;
33251090Ssheldonh			if (left > 0)
333			    bzero((char *)bp->b_data + nread, left);
334		}
335		break;
336	    default:
337		printf("smbfs_doio:  type %x unexpected\n",vp->v_type);
338		break;
339	    };
340	    if (error) {
341		bp->b_error = error;
342		bp->b_ioflags |= BIO_ERROR;
343	    }
344	} else { /* write */
345	    if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size)
346		bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
347
348	    if (bp->b_dirtyend > bp->b_dirtyoff) {
349		io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff;
350		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff;
351		io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
352		uiop->uio_rw = UIO_WRITE;
353		bp->b_flags |= B_WRITEINPROG;
354		error = smb_write(smp->sm_share, np->n_fid, uiop, &scred);
355		bp->b_flags &= ~B_WRITEINPROG;
356
357		/*
358		 * For an interrupted write, the buffer is still valid
359		 * and the write hasn't been pushed to the server yet,
360		 * so we can't set BIO_ERROR and report the interruption
361		 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
362		 * is not relevant, so the rpc attempt is essentially
363		 * a noop.  For the case of a V3 write rpc not being
364		 * committed to stable storage, the block is still
365		 * dirty and requires either a commit rpc or another
366		 * write rpc with iomode == NFSV3WRITE_FILESYNC before
367		 * the block is reused. This is indicated by setting
368		 * the B_DELWRI and B_NEEDCOMMIT flags.
369		 */
370		if (error == EINTR
371		    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
372			int s;
373
374			s = splbio();
375			bp->b_flags &= ~(B_INVAL|B_NOCACHE);
376			if ((bp->b_flags & B_ASYNC) == 0)
377			    bp->b_flags |= B_EINTR;
378			if ((bp->b_flags & B_PAGING) == 0) {
379			    bdirty(bp);
380			    bp->b_flags &= ~B_DONE;
381			}
382			if ((bp->b_flags & B_ASYNC) == 0)
383			    bp->b_flags |= B_EINTR;
384			splx(s);
385		} else {
386			if (error) {
387				bp->b_ioflags |= BIO_ERROR;
388				bp->b_error = error;
389			}
390			bp->b_dirtyoff = bp->b_dirtyend = 0;
391		}
392	    } else {
393		bp->b_resid = 0;
394		bufdone(bp);
395		return 0;
396	    }
397	}
398	bp->b_resid = uiop->uio_resid;
399	bufdone(bp);
400	return error;
401}
402
403/*
404 * Vnode op for VM getpages.
405 * Wish wish .... get rid from multiple IO routines
406 */
407int
408smbfs_getpages(ap)
409	struct vop_getpages_args /* {
410		struct vnode *a_vp;
411		vm_page_t *a_m;
412		int a_count;
413		int a_reqpage;
414		vm_ooffset_t a_offset;
415	} */ *ap;
416{
417#ifdef SMBFS_RWGENERIC
418	return vop_stdgetpages(ap);
419#else
420	int i, error, nextoff, size, toff, npages, count, reqpage;
421	struct uio uio;
422	struct iovec iov;
423	vm_offset_t kva;
424	struct buf *bp;
425	struct vnode *vp;
426	struct thread *td;
427	struct ucred *cred;
428	struct smbmount *smp;
429	struct smbnode *np;
430	struct smb_cred scred;
431	vm_page_t *pages, m;
432
433	vp = ap->a_vp;
434	if (vp->v_object == NULL) {
435		printf("smbfs_getpages: called with non-merged cache vnode??\n");
436		return VM_PAGER_ERROR;
437	}
438
439	td = curthread;				/* XXX */
440	cred = td->td_ucred;		/* XXX */
441	np = VTOSMB(vp);
442	smp = VFSTOSMBFS(vp->v_mount);
443	pages = ap->a_m;
444	count = ap->a_count;
445	npages = btoc(count);
446	reqpage = ap->a_reqpage;
447
448	/*
449	 * If the requested page is partially valid, just return it and
450	 * allow the pager to zero-out the blanks.  Partially valid pages
451	 * can only occur at the file EOF.
452	 */
453	m = pages[reqpage];
454
455	if (m->valid != 0) {
456		/* handled by vm_fault now	  */
457		/* vm_page_zero_invalid(m, TRUE); */
458		vm_page_lock_queues();
459		for (i = 0; i < npages; ++i) {
460			if (i != reqpage)
461				vm_page_free(pages[i]);
462		}
463		vm_page_unlock_queues();
464		return 0;
465	}
466
467	smb_makescred(&scred, td, cred);
468
469	bp = getpbuf(&smbfs_pbuf_freecnt);
470
471	kva = (vm_offset_t) bp->b_data;
472	pmap_qenter(kva, pages, npages);
473	cnt.v_vnodein++;
474	cnt.v_vnodepgsin += npages;
475
476	iov.iov_base = (caddr_t) kva;
477	iov.iov_len = count;
478	uio.uio_iov = &iov;
479	uio.uio_iovcnt = 1;
480	uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
481	uio.uio_resid = count;
482	uio.uio_segflg = UIO_SYSSPACE;
483	uio.uio_rw = UIO_READ;
484	uio.uio_td = td;
485
486	error = smb_read(smp->sm_share, np->n_fid, &uio, &scred);
487	pmap_qremove(kva, npages);
488
489	relpbuf(bp, &smbfs_pbuf_freecnt);
490
491	if (error && (uio.uio_resid == count)) {
492		printf("smbfs_getpages: error %d\n",error);
493		vm_page_lock_queues();
494		for (i = 0; i < npages; i++) {
495			if (reqpage != i)
496				vm_page_free(pages[i]);
497		}
498		vm_page_unlock_queues();
499		return VM_PAGER_ERROR;
500	}
501
502	size = count - uio.uio_resid;
503
504	vm_page_lock_queues();
505	for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
506		vm_page_t m;
507		nextoff = toff + PAGE_SIZE;
508		m = pages[i];
509
510		m->flags &= ~PG_ZERO;
511
512		if (nextoff <= size) {
513			/*
514			 * Read operation filled an entire page
515			 */
516			m->valid = VM_PAGE_BITS_ALL;
517			vm_page_undirty(m);
518		} else if (size > toff) {
519			/*
520			 * Read operation filled a partial page.
521			 */
522			m->valid = 0;
523			vm_page_set_validclean(m, 0, size - toff);
524			/* handled by vm_fault now	  */
525			/* vm_page_zero_invalid(m, TRUE); */
526		} else {
527			/*
528			 * Read operation was short.  If no error occured
529			 * we may have hit a zero-fill section.   We simply
530			 * leave valid set to 0.
531			 */
532			;
533		}
534
535		if (i != reqpage) {
536			/*
537			 * Whether or not to leave the page activated is up in
538			 * the air, but we should put the page on a page queue
539			 * somewhere (it already is in the object).  Result:
540			 * It appears that emperical results show that
541			 * deactivating pages is best.
542			 */
543
544			/*
545			 * Just in case someone was asking for this page we
546			 * now tell them that it is ok to use.
547			 */
548			if (!error) {
549				if (m->flags & PG_WANTED)
550					vm_page_activate(m);
551				else
552					vm_page_deactivate(m);
553				vm_page_wakeup(m);
554			} else {
555				vm_page_free(m);
556			}
557		}
558	}
559	vm_page_unlock_queues();
560	return 0;
561#endif /* SMBFS_RWGENERIC */
562}
563
564/*
565 * Vnode op for VM putpages.
566 * possible bug: all IO done in sync mode
567 * Note that vop_close always invalidate pages before close, so it's
568 * not necessary to open vnode.
569 */
570int
571smbfs_putpages(ap)
572	struct vop_putpages_args /* {
573		struct vnode *a_vp;
574		vm_page_t *a_m;
575		int a_count;
576		int a_sync;
577		int *a_rtvals;
578		vm_ooffset_t a_offset;
579	} */ *ap;
580{
581	int error;
582	struct vnode *vp = ap->a_vp;
583	struct thread *td;
584	struct ucred *cred;
585
586#ifdef SMBFS_RWGENERIC
587	td = curthread;			/* XXX */
588	cred = td->td_ucred;		/* XXX */
589	VOP_OPEN(vp, FWRITE, cred, td);
590	error = vop_stdputpages(ap);
591	VOP_CLOSE(vp, FWRITE, cred, td);
592	return error;
593#else
594	struct uio uio;
595	struct iovec iov;
596	vm_offset_t kva;
597	struct buf *bp;
598	int i, npages, count;
599	int *rtvals;
600	struct smbmount *smp;
601	struct smbnode *np;
602	struct smb_cred scred;
603	vm_page_t *pages;
604
605	td = curthread;			/* XXX */
606	cred = td->td_ucred;		/* XXX */
607/*	VOP_OPEN(vp, FWRITE, cred, td);*/
608	np = VTOSMB(vp);
609	smp = VFSTOSMBFS(vp->v_mount);
610	pages = ap->a_m;
611	count = ap->a_count;
612	rtvals = ap->a_rtvals;
613	npages = btoc(count);
614
615	for (i = 0; i < npages; i++) {
616		rtvals[i] = VM_PAGER_AGAIN;
617	}
618
619	bp = getpbuf(&smbfs_pbuf_freecnt);
620
621	kva = (vm_offset_t) bp->b_data;
622	pmap_qenter(kva, pages, npages);
623	cnt.v_vnodeout++;
624	cnt.v_vnodepgsout += count;
625
626	iov.iov_base = (caddr_t) kva;
627	iov.iov_len = count;
628	uio.uio_iov = &iov;
629	uio.uio_iovcnt = 1;
630	uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
631	uio.uio_resid = count;
632	uio.uio_segflg = UIO_SYSSPACE;
633	uio.uio_rw = UIO_WRITE;
634	uio.uio_td = td;
635	SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio.uio_offset, uio.uio_resid);
636
637	smb_makescred(&scred, td, cred);
638	error = smb_write(smp->sm_share, np->n_fid, &uio, &scred);
639/*	VOP_CLOSE(vp, FWRITE, cred, td);*/
640	SMBVDEBUG("paged write done: %d\n", error);
641
642	pmap_qremove(kva, npages);
643
644	relpbuf(bp, &smbfs_pbuf_freecnt);
645
646	if (!error) {
647		int nwritten = round_page(count - uio.uio_resid) / PAGE_SIZE;
648		vm_page_lock_queues();
649		for (i = 0; i < nwritten; i++) {
650			rtvals[i] = VM_PAGER_OK;
651			vm_page_undirty(pages[i]);
652		}
653		vm_page_unlock_queues();
654	}
655	return rtvals[0];
656#endif /* SMBFS_RWGENERIC */
657}
658
659/*
660 * Flush and invalidate all dirty buffers. If another process is already
661 * doing the flush, just wait for completion.
662 */
663int
664smbfs_vinvalbuf(vp, flags, cred, td, intrflg)
665	struct vnode *vp;
666	int flags;
667	struct ucred *cred;
668	struct thread *td;
669	int intrflg;
670{
671	struct smbnode *np = VTOSMB(vp);
672	int error = 0, slpflag, slptimeo;
673
674	VI_LOCK(vp);
675	if (vp->v_iflag & VI_XLOCK) {
676		VI_UNLOCK(vp);
677		return 0;
678	}
679	VI_UNLOCK(vp);
680
681	if (intrflg) {
682		slpflag = PCATCH;
683		slptimeo = 2 * hz;
684	} else {
685		slpflag = 0;
686		slptimeo = 0;
687	}
688	while (np->n_flag & NFLUSHINPROG) {
689		np->n_flag |= NFLUSHWANT;
690		error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "smfsvinv", slptimeo);
691		error = smb_proc_intr(td->td_proc);
692		if (error == EINTR && intrflg)
693			return EINTR;
694	}
695	np->n_flag |= NFLUSHINPROG;
696	error = vinvalbuf(vp, flags, cred, td, slpflag, 0);
697	while (error) {
698		if (intrflg && (error == ERESTART || error == EINTR)) {
699			np->n_flag &= ~NFLUSHINPROG;
700			if (np->n_flag & NFLUSHWANT) {
701				np->n_flag &= ~NFLUSHWANT;
702				wakeup((caddr_t)&np->n_flag);
703			}
704			return EINTR;
705		}
706		error = vinvalbuf(vp, flags, cred, td, slpflag, 0);
707	}
708	np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
709	if (np->n_flag & NFLUSHWANT) {
710		np->n_flag &= ~NFLUSHWANT;
711		wakeup((caddr_t)&np->n_flag);
712	}
713	return (error);
714}
715