xref: /freebsd/sys/fs/smbfs/smbfs_io.c (revision b068bb09a1a82d9fef0e939ad6135443a959e290)
1d167cf6fSWarner Losh /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3d63027b6SPedro F. Giffuni  *
4d122d784SJoel Dahl  * Copyright (c) 2000-2001 Boris Popov
5681a5bbeSBoris Popov  * All rights reserved.
6681a5bbeSBoris Popov  *
7681a5bbeSBoris Popov  * Redistribution and use in source and binary forms, with or without
8681a5bbeSBoris Popov  * modification, are permitted provided that the following conditions
9681a5bbeSBoris Popov  * are met:
10681a5bbeSBoris Popov  * 1. Redistributions of source code must retain the above copyright
11681a5bbeSBoris Popov  *    notice, this list of conditions and the following disclaimer.
12681a5bbeSBoris Popov  * 2. Redistributions in binary form must reproduce the above copyright
13681a5bbeSBoris Popov  *    notice, this list of conditions and the following disclaimer in the
14681a5bbeSBoris Popov  *    documentation and/or other materials provided with the distribution.
15681a5bbeSBoris Popov  *
16681a5bbeSBoris Popov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17681a5bbeSBoris Popov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18681a5bbeSBoris Popov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19681a5bbeSBoris Popov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20681a5bbeSBoris Popov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21681a5bbeSBoris Popov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22681a5bbeSBoris Popov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23681a5bbeSBoris Popov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24681a5bbeSBoris Popov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25681a5bbeSBoris Popov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26681a5bbeSBoris Popov  * SUCH DAMAGE.
27681a5bbeSBoris Popov  *
28681a5bbeSBoris Popov  */
29681a5bbeSBoris Popov #include <sys/param.h>
30681a5bbeSBoris Popov #include <sys/systm.h>
31681a5bbeSBoris Popov #include <sys/kernel.h>
32681a5bbeSBoris Popov #include <sys/fcntl.h>
33681a5bbeSBoris Popov #include <sys/bio.h>
34681a5bbeSBoris Popov #include <sys/buf.h>
35681a5bbeSBoris Popov #include <sys/mount.h>
36681a5bbeSBoris Popov #include <sys/namei.h>
37681a5bbeSBoris Popov #include <sys/vnode.h>
38681a5bbeSBoris Popov #include <sys/dirent.h>
39caa8e38fSDavide Italiano #include <sys/rwlock.h>
40681a5bbeSBoris Popov #include <sys/signalvar.h>
41681a5bbeSBoris Popov #include <sys/sysctl.h>
424a370459SJohn Baldwin #include <sys/vmmeter.h>
43681a5bbeSBoris Popov 
44681a5bbeSBoris Popov #include <vm/vm.h>
451c771f92SKonstantin Belousov #include <vm/vm_param.h>
46681a5bbeSBoris Popov #include <vm/vm_page.h>
47681a5bbeSBoris Popov #include <vm/vm_extern.h>
48681a5bbeSBoris Popov #include <vm/vm_object.h>
49681a5bbeSBoris Popov #include <vm/vm_pager.h>
50681a5bbeSBoris Popov #include <vm/vnode_pager.h>
51681a5bbeSBoris Popov /*
52681a5bbeSBoris Popov #include <sys/ioccom.h>
53681a5bbeSBoris Popov */
54681a5bbeSBoris Popov #include <netsmb/smb.h>
55681a5bbeSBoris Popov #include <netsmb/smb_conn.h>
56681a5bbeSBoris Popov #include <netsmb/smb_subr.h>
57681a5bbeSBoris Popov 
58681a5bbeSBoris Popov #include <fs/smbfs/smbfs.h>
59681a5bbeSBoris Popov #include <fs/smbfs/smbfs_node.h>
60681a5bbeSBoris Popov #include <fs/smbfs/smbfs_subr.h>
61681a5bbeSBoris Popov 
62681a5bbeSBoris Popov /*#define SMBFS_RWGENERIC*/
63681a5bbeSBoris Popov 
64756a5412SGleb Smirnoff extern uma_zone_t smbfs_pbuf_zone;
65681a5bbeSBoris Popov 
66681a5bbeSBoris Popov static int smbfs_fastlookup = 1;
67681a5bbeSBoris Popov 
68681a5bbeSBoris Popov SYSCTL_DECL(_vfs_smbfs);
69681a5bbeSBoris Popov SYSCTL_INT(_vfs_smbfs, OID_AUTO, fastlookup, CTLFLAG_RW, &smbfs_fastlookup, 0, "");
70681a5bbeSBoris Popov 
71681a5bbeSBoris Popov #define DE_SIZE	(sizeof(struct dirent))
72681a5bbeSBoris Popov 
73681a5bbeSBoris Popov static int
smbfs_readvdir(struct vnode * vp,struct uio * uio,struct ucred * cred)74681a5bbeSBoris Popov smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred)
75681a5bbeSBoris Popov {
76681a5bbeSBoris Popov 	struct dirent de;
77681a5bbeSBoris Popov 	struct componentname cn;
78afe09751SDavide Italiano 	struct smb_cred *scred;
79681a5bbeSBoris Popov 	struct smbfs_fctx *ctx;
80681a5bbeSBoris Popov 	struct vnode *newvp;
81681a5bbeSBoris Popov 	struct smbnode *np = VTOSMB(vp);
82681a5bbeSBoris Popov 	int error/*, *eofflag = ap->a_eofflag*/;
83681a5bbeSBoris Popov 	long offset, limit;
84681a5bbeSBoris Popov 
85681a5bbeSBoris Popov 	np = VTOSMB(vp);
86681a5bbeSBoris Popov 	SMBVDEBUG("dirname='%s'\n", np->n_name);
87afe09751SDavide Italiano 	scred = smbfs_malloc_scred();
88afe09751SDavide Italiano 	smb_makescred(scred, uio->uio_td, cred);
89681a5bbeSBoris Popov 	offset = uio->uio_offset / DE_SIZE;	/* offset in the directory */
90681a5bbeSBoris Popov 	limit = uio->uio_resid / DE_SIZE;
91afe09751SDavide Italiano 	if (uio->uio_resid < DE_SIZE || uio->uio_offset < 0) {
92afe09751SDavide Italiano 		error = EINVAL;
93afe09751SDavide Italiano 		goto out;
94afe09751SDavide Italiano 	}
95681a5bbeSBoris Popov 	while (limit && offset < 2) {
96681a5bbeSBoris Popov 		limit--;
97681a5bbeSBoris Popov 		bzero((caddr_t)&de, DE_SIZE);
98681a5bbeSBoris Popov 		de.d_reclen = DE_SIZE;
99681a5bbeSBoris Popov 		de.d_fileno = (offset == 0) ? np->n_ino :
10080704a47SDavide Italiano 		    (np->n_parent ? np->n_parentino : 2);
101681a5bbeSBoris Popov 		if (de.d_fileno == 0)
102681a5bbeSBoris Popov 			de.d_fileno = 0x7ffffffd + offset;
10390f580b9SMark Johnston 		de.d_off = offset + 1;
104681a5bbeSBoris Popov 		de.d_namlen = offset + 1;
105681a5bbeSBoris Popov 		de.d_name[0] = '.';
106681a5bbeSBoris Popov 		de.d_name[1] = '.';
107681a5bbeSBoris Popov 		de.d_type = DT_DIR;
1086d2e2df7SMark Johnston 		dirent_terminate(&de);
109c9524588SDag-Erling Smørgrav 		error = uiomove(&de, DE_SIZE, uio);
110681a5bbeSBoris Popov 		if (error)
111afe09751SDavide Italiano 			goto out;
112681a5bbeSBoris Popov 		offset++;
113681a5bbeSBoris Popov 		uio->uio_offset += DE_SIZE;
114681a5bbeSBoris Popov 	}
115afe09751SDavide Italiano 	if (limit == 0) {
116afe09751SDavide Italiano 		error = 0;
117afe09751SDavide Italiano 		goto out;
118afe09751SDavide Italiano 	}
119681a5bbeSBoris Popov 	if (offset != np->n_dirofs || np->n_dirseq == NULL) {
120681a5bbeSBoris Popov 		SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs);
121681a5bbeSBoris Popov 		if (np->n_dirseq) {
122afe09751SDavide Italiano 			smbfs_findclose(np->n_dirseq, scred);
123681a5bbeSBoris Popov 			np->n_dirseq = NULL;
124681a5bbeSBoris Popov 		}
125681a5bbeSBoris Popov 		np->n_dirofs = 2;
126681a5bbeSBoris Popov 		error = smbfs_findopen(np, "*", 1,
127681a5bbeSBoris Popov 		    SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR,
128afe09751SDavide Italiano 		    scred, &ctx);
129681a5bbeSBoris Popov 		if (error) {
130681a5bbeSBoris Popov 			SMBVDEBUG("can not open search, error = %d", error);
131afe09751SDavide Italiano 			goto out;
132681a5bbeSBoris Popov 		}
133681a5bbeSBoris Popov 		np->n_dirseq = ctx;
134681a5bbeSBoris Popov 	} else
135681a5bbeSBoris Popov 		ctx = np->n_dirseq;
136681a5bbeSBoris Popov 	while (np->n_dirofs < offset) {
137afe09751SDavide Italiano 		error = smbfs_findnext(ctx, offset - np->n_dirofs++, scred);
138681a5bbeSBoris Popov 		if (error) {
139afe09751SDavide Italiano 			smbfs_findclose(np->n_dirseq, scred);
140681a5bbeSBoris Popov 			np->n_dirseq = NULL;
141afe09751SDavide Italiano 			error = ENOENT ? 0 : error;
142afe09751SDavide Italiano 			goto out;
143681a5bbeSBoris Popov 		}
144681a5bbeSBoris Popov 	}
145681a5bbeSBoris Popov 	error = 0;
146681a5bbeSBoris Popov 	for (; limit; limit--, offset++) {
147afe09751SDavide Italiano 		error = smbfs_findnext(ctx, limit, scred);
148681a5bbeSBoris Popov 		if (error)
149681a5bbeSBoris Popov 			break;
150681a5bbeSBoris Popov 		np->n_dirofs++;
151681a5bbeSBoris Popov 		bzero((caddr_t)&de, DE_SIZE);
152681a5bbeSBoris Popov 		de.d_reclen = DE_SIZE;
153681a5bbeSBoris Popov 		de.d_fileno = ctx->f_attr.fa_ino;
15490f580b9SMark Johnston 		de.d_off = offset + 1;
155681a5bbeSBoris Popov 		de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG;
156681a5bbeSBoris Popov 		de.d_namlen = ctx->f_nmlen;
157681a5bbeSBoris Popov 		bcopy(ctx->f_name, de.d_name, de.d_namlen);
1586d2e2df7SMark Johnston 		dirent_terminate(&de);
159681a5bbeSBoris Popov 		if (smbfs_fastlookup) {
160681a5bbeSBoris Popov 			error = smbfs_nget(vp->v_mount, vp, ctx->f_name,
161681a5bbeSBoris Popov 			    ctx->f_nmlen, &ctx->f_attr, &newvp);
162681a5bbeSBoris Popov 			if (!error) {
163681a5bbeSBoris Popov 				cn.cn_nameptr = de.d_name;
164681a5bbeSBoris Popov 				cn.cn_namelen = de.d_namlen;
165681a5bbeSBoris Popov 				cache_enter(vp, newvp, &cn);
166681a5bbeSBoris Popov 				vput(newvp);
167681a5bbeSBoris Popov 			}
168681a5bbeSBoris Popov 		}
169c9524588SDag-Erling Smørgrav 		error = uiomove(&de, DE_SIZE, uio);
170681a5bbeSBoris Popov 		if (error)
171681a5bbeSBoris Popov 			break;
172681a5bbeSBoris Popov 	}
173681a5bbeSBoris Popov 	if (error == ENOENT)
174681a5bbeSBoris Popov 		error = 0;
175681a5bbeSBoris Popov 	uio->uio_offset = offset * DE_SIZE;
176afe09751SDavide Italiano out:
177afe09751SDavide Italiano 	smbfs_free_scred(scred);
178681a5bbeSBoris Popov 	return error;
179681a5bbeSBoris Popov }
180681a5bbeSBoris Popov 
181681a5bbeSBoris Popov int
smbfs_readvnode(struct vnode * vp,struct uio * uiop,struct ucred * cred)182681a5bbeSBoris Popov smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred)
183681a5bbeSBoris Popov {
184681a5bbeSBoris Popov 	struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
185681a5bbeSBoris Popov 	struct smbnode *np = VTOSMB(vp);
186b1c996c4SBoris Popov 	struct thread *td;
187681a5bbeSBoris Popov 	struct vattr vattr;
188afe09751SDavide Italiano 	struct smb_cred *scred;
189681a5bbeSBoris Popov 	int error, lks;
190681a5bbeSBoris Popov 
191b8815755SBoris Popov 	/*
192b8815755SBoris Popov 	 * Protect against method which is not supported for now
193b8815755SBoris Popov 	 */
194b8815755SBoris Popov 	if (uiop->uio_segflg == UIO_NOCOPY)
195b8815755SBoris Popov 		return EOPNOTSUPP;
196b8815755SBoris Popov 
197681a5bbeSBoris Popov 	if (vp->v_type != VREG && vp->v_type != VDIR) {
198681a5bbeSBoris Popov 		SMBFSERR("vn types other than VREG or VDIR are unsupported !\n");
199681a5bbeSBoris Popov 		return EIO;
200681a5bbeSBoris Popov 	}
201681a5bbeSBoris Popov 	if (uiop->uio_resid == 0)
202681a5bbeSBoris Popov 		return 0;
203681a5bbeSBoris Popov 	if (uiop->uio_offset < 0)
204681a5bbeSBoris Popov 		return EINVAL;
205681a5bbeSBoris Popov /*	if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
206681a5bbeSBoris Popov 		return EFBIG;*/
207b1c996c4SBoris Popov 	td = uiop->uio_td;
208681a5bbeSBoris Popov 	if (vp->v_type == VDIR) {
20981c794f9SAttilio Rao 		lks = LK_EXCLUSIVE;	/* lockstatus(vp->v_vnlock); */
210681a5bbeSBoris Popov 		if (lks == LK_SHARED)
211cb05b60aSAttilio Rao 			vn_lock(vp, LK_UPGRADE | LK_RETRY);
212681a5bbeSBoris Popov 		error = smbfs_readvdir(vp, uiop, cred);
213681a5bbeSBoris Popov 		if (lks == LK_SHARED)
214cb05b60aSAttilio Rao 			vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
215681a5bbeSBoris Popov 		return error;
216681a5bbeSBoris Popov 	}
217681a5bbeSBoris Popov 
218681a5bbeSBoris Popov /*	biosize = SSTOCN(smp->sm_share)->sc_txmax;*/
219681a5bbeSBoris Popov 	if (np->n_flag & NMODIFIED) {
220681a5bbeSBoris Popov 		smbfs_attr_cacheremove(vp);
2210359a12eSAttilio Rao 		error = VOP_GETATTR(vp, &vattr, cred);
222681a5bbeSBoris Popov 		if (error)
223681a5bbeSBoris Popov 			return error;
224681a5bbeSBoris Popov 		np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
225681a5bbeSBoris Popov 	} else {
2260359a12eSAttilio Rao 		error = VOP_GETATTR(vp, &vattr, cred);
227681a5bbeSBoris Popov 		if (error)
228681a5bbeSBoris Popov 			return error;
229681a5bbeSBoris Popov 		if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
230e50508dfSPoul-Henning Kamp 			error = smbfs_vinvalbuf(vp, td);
231681a5bbeSBoris Popov 			if (error)
232681a5bbeSBoris Popov 				return error;
233681a5bbeSBoris Popov 			np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
234681a5bbeSBoris Popov 		}
235681a5bbeSBoris Popov 	}
236afe09751SDavide Italiano 	scred = smbfs_malloc_scred();
237afe09751SDavide Italiano 	smb_makescred(scred, td, cred);
238afe09751SDavide Italiano 	error = smb_read(smp->sm_share, np->n_fid, uiop, scred);
239afe09751SDavide Italiano 	smbfs_free_scred(scred);
240afe09751SDavide Italiano 	return (error);
241681a5bbeSBoris Popov }
242681a5bbeSBoris Popov 
243681a5bbeSBoris Popov int
smbfs_writevnode(struct vnode * vp,struct uio * uiop,struct ucred * cred,int ioflag)244681a5bbeSBoris Popov smbfs_writevnode(struct vnode *vp, struct uio *uiop,
245681a5bbeSBoris Popov 	struct ucred *cred, int ioflag)
246681a5bbeSBoris Popov {
247681a5bbeSBoris Popov 	struct smbmount *smp = VTOSMBFS(vp);
248681a5bbeSBoris Popov 	struct smbnode *np = VTOSMB(vp);
249afe09751SDavide Italiano 	struct smb_cred *scred;
250b1c996c4SBoris Popov 	struct thread *td;
251681a5bbeSBoris Popov 	int error = 0;
252681a5bbeSBoris Popov 
253681a5bbeSBoris Popov 	if (vp->v_type != VREG) {
254681a5bbeSBoris Popov 		SMBERROR("vn types other than VREG unsupported !\n");
255681a5bbeSBoris Popov 		return EIO;
256681a5bbeSBoris Popov 	}
257994f027fSDavide Italiano 	SMBVDEBUG("ofs=%jd,resid=%zd\n", (intmax_t)uiop->uio_offset,
258994f027fSDavide Italiano 	    uiop->uio_resid);
259681a5bbeSBoris Popov 	if (uiop->uio_offset < 0)
260681a5bbeSBoris Popov 		return EINVAL;
261681a5bbeSBoris Popov /*	if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
262681a5bbeSBoris Popov 		return (EFBIG);*/
263b1c996c4SBoris Popov 	td = uiop->uio_td;
264681a5bbeSBoris Popov 	if (ioflag & (IO_APPEND | IO_SYNC)) {
265681a5bbeSBoris Popov 		if (np->n_flag & NMODIFIED) {
266681a5bbeSBoris Popov 			smbfs_attr_cacheremove(vp);
267e50508dfSPoul-Henning Kamp 			error = smbfs_vinvalbuf(vp, td);
268681a5bbeSBoris Popov 			if (error)
269681a5bbeSBoris Popov 				return error;
270681a5bbeSBoris Popov 		}
271681a5bbeSBoris Popov 		if (ioflag & IO_APPEND) {
2723238c6bdSRuslan Ermilov #ifdef notyet
273681a5bbeSBoris Popov 			/*
274681a5bbeSBoris Popov 			 * File size can be changed by another client
275681a5bbeSBoris Popov 			 */
276681a5bbeSBoris Popov 			smbfs_attr_cacheremove(vp);
2770359a12eSAttilio Rao 			error = VOP_GETATTR(vp, &vattr, cred);
278681a5bbeSBoris Popov 			if (error) return (error);
279681a5bbeSBoris Popov #endif
280681a5bbeSBoris Popov 			uiop->uio_offset = np->n_size;
281681a5bbeSBoris Popov 		}
282681a5bbeSBoris Popov 	}
283681a5bbeSBoris Popov 	if (uiop->uio_resid == 0)
284681a5bbeSBoris Popov 		return 0;
285b5f770bdSEdward Tomasz Napierala 
286cc65a412SKonstantin Belousov 	error = vn_rlimit_fsize(vp, uiop, td);
287cc65a412SKonstantin Belousov 	if (error != 0)
288cc65a412SKonstantin Belousov 		return (error);
289b5f770bdSEdward Tomasz Napierala 
290afe09751SDavide Italiano 	scred = smbfs_malloc_scred();
291afe09751SDavide Italiano 	smb_makescred(scred, td, cred);
292afe09751SDavide Italiano 	error = smb_write(smp->sm_share, np->n_fid, uiop, scred);
293afe09751SDavide Italiano 	smbfs_free_scred(scred);
294994f027fSDavide Italiano 	SMBVDEBUG("after: ofs=%jd,resid=%zd\n", (intmax_t)uiop->uio_offset,
295994f027fSDavide Italiano 	    uiop->uio_resid);
296681a5bbeSBoris Popov 	if (!error) {
297681a5bbeSBoris Popov 		if (uiop->uio_offset > np->n_size) {
298681a5bbeSBoris Popov 			np->n_size = uiop->uio_offset;
299681a5bbeSBoris Popov 			vnode_pager_setsize(vp, np->n_size);
300681a5bbeSBoris Popov 		}
301681a5bbeSBoris Popov 	}
302681a5bbeSBoris Popov 	return error;
303681a5bbeSBoris Popov }
304681a5bbeSBoris Popov 
305681a5bbeSBoris Popov /*
306681a5bbeSBoris Popov  * Do an I/O operation to/from a cache block.
307681a5bbeSBoris Popov  */
308681a5bbeSBoris Popov int
smbfs_doio(struct vnode * vp,struct buf * bp,struct ucred * cr,struct thread * td)309066a8feaSPoul-Henning Kamp smbfs_doio(struct vnode *vp, struct buf *bp, struct ucred *cr, struct thread *td)
310681a5bbeSBoris Popov {
311681a5bbeSBoris Popov 	struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
312681a5bbeSBoris Popov 	struct smbnode *np = VTOSMB(vp);
313afe09751SDavide Italiano 	struct uio *uiop;
314681a5bbeSBoris Popov 	struct iovec io;
315afe09751SDavide Italiano 	struct smb_cred *scred;
316681a5bbeSBoris Popov 	int error = 0;
317681a5bbeSBoris Popov 
318afe09751SDavide Italiano 	uiop = malloc(sizeof(struct uio), M_SMBFSDATA, M_WAITOK);
319681a5bbeSBoris Popov 	uiop->uio_iov = &io;
320681a5bbeSBoris Popov 	uiop->uio_iovcnt = 1;
321681a5bbeSBoris Popov 	uiop->uio_segflg = UIO_SYSSPACE;
322b1c996c4SBoris Popov 	uiop->uio_td = td;
323681a5bbeSBoris Popov 
324afe09751SDavide Italiano 	scred = smbfs_malloc_scred();
325afe09751SDavide Italiano 	smb_makescred(scred, td, cr);
326681a5bbeSBoris Popov 
327681a5bbeSBoris Popov 	if (bp->b_iocmd == BIO_READ) {
328681a5bbeSBoris Popov 	    io.iov_len = uiop->uio_resid = bp->b_bcount;
329681a5bbeSBoris Popov 	    io.iov_base = bp->b_data;
330681a5bbeSBoris Popov 	    uiop->uio_rw = UIO_READ;
331681a5bbeSBoris Popov 	    switch (vp->v_type) {
332681a5bbeSBoris Popov 	      case VREG:
333681a5bbeSBoris Popov 		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
334afe09751SDavide Italiano 		error = smb_read(smp->sm_share, np->n_fid, uiop, scred);
335681a5bbeSBoris Popov 		if (error)
336681a5bbeSBoris Popov 			break;
337681a5bbeSBoris Popov 		if (uiop->uio_resid) {
338681a5bbeSBoris Popov 			int left = uiop->uio_resid;
339681a5bbeSBoris Popov 			int nread = bp->b_bcount - left;
340681a5bbeSBoris Popov 			if (left > 0)
341681a5bbeSBoris Popov 			    bzero((char *)bp->b_data + nread, left);
342681a5bbeSBoris Popov 		}
343681a5bbeSBoris Popov 		break;
344681a5bbeSBoris Popov 	    default:
345681a5bbeSBoris Popov 		printf("smbfs_doio:  type %x unexpected\n",vp->v_type);
346681a5bbeSBoris Popov 		break;
34774b8d63dSPedro F. Giffuni 	    }
348681a5bbeSBoris Popov 	    if (error) {
349681a5bbeSBoris Popov 		bp->b_error = error;
350681a5bbeSBoris Popov 		bp->b_ioflags |= BIO_ERROR;
351681a5bbeSBoris Popov 	    }
352681a5bbeSBoris Popov 	} else { /* write */
353681a5bbeSBoris Popov 	    if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size)
354681a5bbeSBoris Popov 		bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
355681a5bbeSBoris Popov 
356681a5bbeSBoris Popov 	    if (bp->b_dirtyend > bp->b_dirtyoff) {
357681a5bbeSBoris Popov 		io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff;
358681a5bbeSBoris Popov 		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff;
359681a5bbeSBoris Popov 		io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
360681a5bbeSBoris Popov 		uiop->uio_rw = UIO_WRITE;
361afe09751SDavide Italiano 		error = smb_write(smp->sm_share, np->n_fid, uiop, scred);
362681a5bbeSBoris Popov 
363681a5bbeSBoris Popov 		/*
364681a5bbeSBoris Popov 		 * For an interrupted write, the buffer is still valid
365681a5bbeSBoris Popov 		 * and the write hasn't been pushed to the server yet,
366681a5bbeSBoris Popov 		 * so we can't set BIO_ERROR and report the interruption
367681a5bbeSBoris Popov 		 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
368681a5bbeSBoris Popov 		 * is not relevant, so the rpc attempt is essentially
369681a5bbeSBoris Popov 		 * a noop.  For the case of a V3 write rpc not being
370681a5bbeSBoris Popov 		 * committed to stable storage, the block is still
371681a5bbeSBoris Popov 		 * dirty and requires either a commit rpc or another
372681a5bbeSBoris Popov 		 * write rpc with iomode == NFSV3WRITE_FILESYNC before
373681a5bbeSBoris Popov 		 * the block is reused. This is indicated by setting
374681a5bbeSBoris Popov 		 * the B_DELWRI and B_NEEDCOMMIT flags.
375681a5bbeSBoris Popov 		 */
376681a5bbeSBoris Popov 		if (error == EINTR
377681a5bbeSBoris Popov 		    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
378681a5bbeSBoris Popov 			bp->b_flags &= ~(B_INVAL|B_NOCACHE);
379681a5bbeSBoris Popov 			if ((bp->b_flags & B_ASYNC) == 0)
380681a5bbeSBoris Popov 			    bp->b_flags |= B_EINTR;
381681a5bbeSBoris Popov 			if ((bp->b_flags & B_PAGING) == 0) {
382681a5bbeSBoris Popov 			    bdirty(bp);
383681a5bbeSBoris Popov 			    bp->b_flags &= ~B_DONE;
384681a5bbeSBoris Popov 			}
385681a5bbeSBoris Popov 			if ((bp->b_flags & B_ASYNC) == 0)
386681a5bbeSBoris Popov 			    bp->b_flags |= B_EINTR;
387681a5bbeSBoris Popov 		} else {
388681a5bbeSBoris Popov 			if (error) {
389681a5bbeSBoris Popov 				bp->b_ioflags |= BIO_ERROR;
390681a5bbeSBoris Popov 				bp->b_error = error;
391681a5bbeSBoris Popov 			}
392681a5bbeSBoris Popov 			bp->b_dirtyoff = bp->b_dirtyend = 0;
393681a5bbeSBoris Popov 		}
394681a5bbeSBoris Popov 	    } else {
395681a5bbeSBoris Popov 		bp->b_resid = 0;
396681a5bbeSBoris Popov 		bufdone(bp);
397afe09751SDavide Italiano 		free(uiop, M_SMBFSDATA);
398afe09751SDavide Italiano 		smbfs_free_scred(scred);
399681a5bbeSBoris Popov 		return 0;
400681a5bbeSBoris Popov 	    }
401681a5bbeSBoris Popov 	}
402681a5bbeSBoris Popov 	bp->b_resid = uiop->uio_resid;
403681a5bbeSBoris Popov 	bufdone(bp);
404afe09751SDavide Italiano 	free(uiop, M_SMBFSDATA);
405afe09751SDavide Italiano 	smbfs_free_scred(scred);
406681a5bbeSBoris Popov 	return error;
407681a5bbeSBoris Popov }
408681a5bbeSBoris Popov 
409681a5bbeSBoris Popov /*
410681a5bbeSBoris Popov  * Vnode op for VM getpages.
411681a5bbeSBoris Popov  * Wish wish .... get rid from multiple IO routines
412681a5bbeSBoris Popov  */
413681a5bbeSBoris Popov int
smbfs_getpages(struct vop_getpages_args * ap)414b09b03a1SMateusz Guzik smbfs_getpages(struct vop_getpages_args *ap)
415681a5bbeSBoris Popov {
416681a5bbeSBoris Popov #ifdef SMBFS_RWGENERIC
417a62615e5SPoul-Henning Kamp 	return vop_stdgetpages(ap);
418681a5bbeSBoris Popov #else
419b0cd2017SGleb Smirnoff 	int i, error, nextoff, size, toff, npages, count;
420681a5bbeSBoris Popov 	struct uio uio;
421681a5bbeSBoris Popov 	struct iovec iov;
422681a5bbeSBoris Popov 	vm_offset_t kva;
423681a5bbeSBoris Popov 	struct buf *bp;
424681a5bbeSBoris Popov 	struct vnode *vp;
425b1c996c4SBoris Popov 	struct thread *td;
426681a5bbeSBoris Popov 	struct ucred *cred;
427681a5bbeSBoris Popov 	struct smbmount *smp;
428681a5bbeSBoris Popov 	struct smbnode *np;
429afe09751SDavide Italiano 	struct smb_cred *scred;
43082b8b189SAlan Cox 	vm_object_t object;
431b0cd2017SGleb Smirnoff 	vm_page_t *pages;
432681a5bbeSBoris Popov 
433681a5bbeSBoris Popov 	vp = ap->a_vp;
43482b8b189SAlan Cox 	if ((object = vp->v_object) == NULL) {
435959b83b9SBoris Popov 		printf("smbfs_getpages: called with non-merged cache vnode??\n");
436959b83b9SBoris Popov 		return VM_PAGER_ERROR;
437959b83b9SBoris Popov 	}
438959b83b9SBoris Popov 
439b1c996c4SBoris Popov 	td = curthread;				/* XXX */
440a854ed98SJohn Baldwin 	cred = td->td_ucred;		/* XXX */
441681a5bbeSBoris Popov 	np = VTOSMB(vp);
442681a5bbeSBoris Popov 	smp = VFSTOSMBFS(vp->v_mount);
443681a5bbeSBoris Popov 	pages = ap->a_m;
444f17f88d3SGleb Smirnoff 	npages = ap->a_count;
445681a5bbeSBoris Popov 
446959b83b9SBoris Popov 	/*
447959b83b9SBoris Popov 	 * If the requested page is partially valid, just return it and
448959b83b9SBoris Popov 	 * allow the pager to zero-out the blanks.  Partially valid pages
449959b83b9SBoris Popov 	 * can only occur at the file EOF.
450b0cd2017SGleb Smirnoff 	 *
451b0cd2017SGleb Smirnoff 	 * XXXGL: is that true for SMB filesystem?
452959b83b9SBoris Popov 	 */
453caa8e38fSDavide Italiano 	VM_OBJECT_WLOCK(object);
4540012f373SJeff Roberson 	if (!vm_page_none_valid(pages[npages - 1]) && --npages == 0)
455f17f88d3SGleb Smirnoff 		goto out;
456caa8e38fSDavide Italiano 	VM_OBJECT_WUNLOCK(object);
457959b83b9SBoris Popov 
458afe09751SDavide Italiano 	scred = smbfs_malloc_scred();
459afe09751SDavide Italiano 	smb_makescred(scred, td, cred);
460681a5bbeSBoris Popov 
461756a5412SGleb Smirnoff 	bp = uma_zalloc(smbfs_pbuf_zone, M_WAITOK);
4623f36e6f2SBoris Popov 
463681a5bbeSBoris Popov 	kva = (vm_offset_t) bp->b_data;
464681a5bbeSBoris Popov 	pmap_qenter(kva, pages, npages);
46583c9dea1SGleb Smirnoff 	VM_CNT_INC(v_vnodein);
46683c9dea1SGleb Smirnoff 	VM_CNT_ADD(v_vnodepgsin, npages);
467681a5bbeSBoris Popov 
468f17f88d3SGleb Smirnoff 	count = npages << PAGE_SHIFT;
469681a5bbeSBoris Popov 	iov.iov_base = (caddr_t) kva;
470681a5bbeSBoris Popov 	iov.iov_len = count;
471681a5bbeSBoris Popov 	uio.uio_iov = &iov;
472681a5bbeSBoris Popov 	uio.uio_iovcnt = 1;
473681a5bbeSBoris Popov 	uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
474681a5bbeSBoris Popov 	uio.uio_resid = count;
475681a5bbeSBoris Popov 	uio.uio_segflg = UIO_SYSSPACE;
476681a5bbeSBoris Popov 	uio.uio_rw = UIO_READ;
477b1c996c4SBoris Popov 	uio.uio_td = td;
478681a5bbeSBoris Popov 
479afe09751SDavide Italiano 	error = smb_read(smp->sm_share, np->n_fid, &uio, scred);
480afe09751SDavide Italiano 	smbfs_free_scred(scred);
481681a5bbeSBoris Popov 	pmap_qremove(kva, npages);
482681a5bbeSBoris Popov 
483756a5412SGleb Smirnoff 	uma_zfree(smbfs_pbuf_zone, bp);
484681a5bbeSBoris Popov 
485681a5bbeSBoris Popov 	if (error && (uio.uio_resid == count)) {
486681a5bbeSBoris Popov 		printf("smbfs_getpages: error %d\n",error);
487681a5bbeSBoris Popov 		return VM_PAGER_ERROR;
488681a5bbeSBoris Popov 	}
489681a5bbeSBoris Popov 
490681a5bbeSBoris Popov 	size = count - uio.uio_resid;
491681a5bbeSBoris Popov 
492b0cd2017SGleb Smirnoff 	VM_OBJECT_WLOCK(object);
493681a5bbeSBoris Popov 	for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
494681a5bbeSBoris Popov 		vm_page_t m;
495681a5bbeSBoris Popov 		nextoff = toff + PAGE_SIZE;
496681a5bbeSBoris Popov 		m = pages[i];
497681a5bbeSBoris Popov 
498681a5bbeSBoris Popov 		if (nextoff <= size) {
499959b83b9SBoris Popov 			/*
500959b83b9SBoris Popov 			 * Read operation filled an entire page
501959b83b9SBoris Popov 			 */
5020012f373SJeff Roberson 			vm_page_valid(m);
50312aa4fdcSAlan Cox 			KASSERT(m->dirty == 0,
50412aa4fdcSAlan Cox 			    ("smbfs_getpages: page %p is dirty", m));
505959b83b9SBoris Popov 		} else if (size > toff) {
506959b83b9SBoris Popov 			/*
507959b83b9SBoris Popov 			 * Read operation filled a partial page.
508959b83b9SBoris Popov 			 */
5090012f373SJeff Roberson 			vm_page_invalid(m);
510dc874f98SKonstantin Belousov 			vm_page_set_valid_range(m, 0, size - toff);
5113933ec4dSAlan Cox 			KASSERT(m->dirty == 0,
51242eb4108SAlan Cox 			    ("smbfs_getpages: page %p is dirty", m));
513681a5bbeSBoris Popov 		} else {
514959b83b9SBoris Popov 			/*
515b3a15dddSPedro F. Giffuni 			 * Read operation was short.  If no error occurred
516959b83b9SBoris Popov 			 * we may have hit a zero-fill section.   We simply
517959b83b9SBoris Popov 			 * leave valid set to 0.
518959b83b9SBoris Popov 			 */
519959b83b9SBoris Popov 			;
520681a5bbeSBoris Popov 		}
52103679e23SAlan Cox 	}
522f17f88d3SGleb Smirnoff out:
523caa8e38fSDavide Italiano 	VM_OBJECT_WUNLOCK(object);
524f17f88d3SGleb Smirnoff 	if (ap->a_rbehind)
525f17f88d3SGleb Smirnoff 		*ap->a_rbehind = 0;
526f17f88d3SGleb Smirnoff 	if (ap->a_rahead)
527f17f88d3SGleb Smirnoff 		*ap->a_rahead = 0;
528f17f88d3SGleb Smirnoff 	return (VM_PAGER_OK);
529681a5bbeSBoris Popov #endif /* SMBFS_RWGENERIC */
530681a5bbeSBoris Popov }
531681a5bbeSBoris Popov 
532681a5bbeSBoris Popov /*
533681a5bbeSBoris Popov  * Vnode op for VM putpages.
534681a5bbeSBoris Popov  * possible bug: all IO done in sync mode
535681a5bbeSBoris Popov  * Note that vop_close always invalidate pages before close, so it's
536681a5bbeSBoris Popov  * not necessary to open vnode.
537681a5bbeSBoris Popov  */
538681a5bbeSBoris Popov int
smbfs_putpages(struct vop_putpages_args * ap)539b09b03a1SMateusz Guzik smbfs_putpages(struct vop_putpages_args *ap)
540681a5bbeSBoris Popov {
541681a5bbeSBoris Popov 	int error;
542681a5bbeSBoris Popov 	struct vnode *vp = ap->a_vp;
543b1c996c4SBoris Popov 	struct thread *td;
544681a5bbeSBoris Popov 	struct ucred *cred;
545681a5bbeSBoris Popov 
546681a5bbeSBoris Popov #ifdef SMBFS_RWGENERIC
547b1c996c4SBoris Popov 	td = curthread;			/* XXX */
548a854ed98SJohn Baldwin 	cred = td->td_ucred;		/* XXX */
5497a31868eSKonstantin Belousov 	VOP_OPEN(vp, FWRITE, cred, td, NULL);
550a62615e5SPoul-Henning Kamp 	error = vop_stdputpages(ap);
551b1c996c4SBoris Popov 	VOP_CLOSE(vp, FWRITE, cred, td);
552681a5bbeSBoris Popov 	return error;
553681a5bbeSBoris Popov #else
554681a5bbeSBoris Popov 	struct uio uio;
555681a5bbeSBoris Popov 	struct iovec iov;
556681a5bbeSBoris Popov 	vm_offset_t kva;
557681a5bbeSBoris Popov 	struct buf *bp;
558681a5bbeSBoris Popov 	int i, npages, count;
559681a5bbeSBoris Popov 	int *rtvals;
560681a5bbeSBoris Popov 	struct smbmount *smp;
561681a5bbeSBoris Popov 	struct smbnode *np;
562afe09751SDavide Italiano 	struct smb_cred *scred;
563681a5bbeSBoris Popov 	vm_page_t *pages;
564681a5bbeSBoris Popov 
565b1c996c4SBoris Popov 	td = curthread;			/* XXX */
566a854ed98SJohn Baldwin 	cred = td->td_ucred;		/* XXX */
5677a31868eSKonstantin Belousov /*	VOP_OPEN(vp, FWRITE, cred, td, NULL);*/
568681a5bbeSBoris Popov 	np = VTOSMB(vp);
569681a5bbeSBoris Popov 	smp = VFSTOSMBFS(vp->v_mount);
570681a5bbeSBoris Popov 	pages = ap->a_m;
571681a5bbeSBoris Popov 	count = ap->a_count;
572681a5bbeSBoris Popov 	rtvals = ap->a_rtvals;
573681a5bbeSBoris Popov 	npages = btoc(count);
574681a5bbeSBoris Popov 
575681a5bbeSBoris Popov 	for (i = 0; i < npages; i++) {
576031ec8c1SKonstantin Belousov 		rtvals[i] = VM_PAGER_ERROR;
577681a5bbeSBoris Popov 	}
578681a5bbeSBoris Popov 
579756a5412SGleb Smirnoff 	bp = uma_zalloc(smbfs_pbuf_zone, M_WAITOK);
5803f36e6f2SBoris Popov 
581681a5bbeSBoris Popov 	kva = (vm_offset_t) bp->b_data;
582681a5bbeSBoris Popov 	pmap_qenter(kva, pages, npages);
58383c9dea1SGleb Smirnoff 	VM_CNT_INC(v_vnodeout);
58483c9dea1SGleb Smirnoff 	VM_CNT_ADD(v_vnodepgsout, count);
585681a5bbeSBoris Popov 
586681a5bbeSBoris Popov 	iov.iov_base = (caddr_t) kva;
587681a5bbeSBoris Popov 	iov.iov_len = count;
588681a5bbeSBoris Popov 	uio.uio_iov = &iov;
589681a5bbeSBoris Popov 	uio.uio_iovcnt = 1;
590681a5bbeSBoris Popov 	uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
591681a5bbeSBoris Popov 	uio.uio_resid = count;
592681a5bbeSBoris Popov 	uio.uio_segflg = UIO_SYSSPACE;
593681a5bbeSBoris Popov 	uio.uio_rw = UIO_WRITE;
594b1c996c4SBoris Popov 	uio.uio_td = td;
595994f027fSDavide Italiano 	SMBVDEBUG("ofs=%jd,resid=%zd\n", (intmax_t)uio.uio_offset,
596994f027fSDavide Italiano 	    uio.uio_resid);
597681a5bbeSBoris Popov 
598afe09751SDavide Italiano 	scred = smbfs_malloc_scred();
599afe09751SDavide Italiano 	smb_makescred(scred, td, cred);
600afe09751SDavide Italiano 	error = smb_write(smp->sm_share, np->n_fid, &uio, scred);
601afe09751SDavide Italiano 	smbfs_free_scred(scred);
602b1c996c4SBoris Popov /*	VOP_CLOSE(vp, FWRITE, cred, td);*/
603681a5bbeSBoris Popov 	SMBVDEBUG("paged write done: %d\n", error);
604681a5bbeSBoris Popov 
605681a5bbeSBoris Popov 	pmap_qremove(kva, npages);
6063f36e6f2SBoris Popov 
607756a5412SGleb Smirnoff 	uma_zfree(smbfs_pbuf_zone, bp);
608681a5bbeSBoris Popov 
609555b7bb4SKonstantin Belousov 	if (error == 0) {
610555b7bb4SKonstantin Belousov 		vnode_pager_undirty_pages(pages, rtvals, count - uio.uio_resid,
611555b7bb4SKonstantin Belousov 		    npages * PAGE_SIZE, npages * PAGE_SIZE);
612555b7bb4SKonstantin Belousov 	}
613555b7bb4SKonstantin Belousov 	return (rtvals[0]);
614681a5bbeSBoris Popov #endif /* SMBFS_RWGENERIC */
615681a5bbeSBoris Popov }
616681a5bbeSBoris Popov 
617681a5bbeSBoris Popov /*
618681a5bbeSBoris Popov  * Flush and invalidate all dirty buffers. If another process is already
619681a5bbeSBoris Popov  * doing the flush, just wait for completion.
620681a5bbeSBoris Popov  */
621681a5bbeSBoris Popov int
smbfs_vinvalbuf(struct vnode * vp,struct thread * td)622e50508dfSPoul-Henning Kamp smbfs_vinvalbuf(struct vnode *vp, struct thread *td)
623681a5bbeSBoris Popov {
624681a5bbeSBoris Popov 	struct smbnode *np = VTOSMB(vp);
625e50508dfSPoul-Henning Kamp 	int error = 0;
626681a5bbeSBoris Popov 
627abd80ddbSMateusz Guzik 	if (VN_IS_DOOMED(vp))
628681a5bbeSBoris Popov 		return 0;
629e6e370a7SJeff Roberson 
630681a5bbeSBoris Popov 	while (np->n_flag & NFLUSHINPROG) {
631681a5bbeSBoris Popov 		np->n_flag |= NFLUSHWANT;
632e50508dfSPoul-Henning Kamp 		error = tsleep(&np->n_flag, PRIBIO + 2, "smfsvinv", 2 * hz);
633120d1b9eSJeff Roberson 		error = smb_td_intr(td);
634e50508dfSPoul-Henning Kamp 		if (error == EINTR)
635681a5bbeSBoris Popov 			return EINTR;
636681a5bbeSBoris Popov 	}
637681a5bbeSBoris Popov 	np->n_flag |= NFLUSHINPROG;
638dcf67e65SStephan Uphoff 
639*b068bb09SKonstantin Belousov 	vnode_pager_clean_sync(vp);
6400d7935fdSAttilio Rao 	error = vinvalbuf(vp, V_SAVE, PCATCH, 0);
641681a5bbeSBoris Popov 	while (error) {
642e50508dfSPoul-Henning Kamp 		if (error == ERESTART || error == EINTR) {
643681a5bbeSBoris Popov 			np->n_flag &= ~NFLUSHINPROG;
644681a5bbeSBoris Popov 			if (np->n_flag & NFLUSHWANT) {
645681a5bbeSBoris Popov 				np->n_flag &= ~NFLUSHWANT;
646521f364bSDag-Erling Smørgrav 				wakeup(&np->n_flag);
647681a5bbeSBoris Popov 			}
648681a5bbeSBoris Popov 			return EINTR;
649681a5bbeSBoris Popov 		}
6500d7935fdSAttilio Rao 		error = vinvalbuf(vp, V_SAVE, PCATCH, 0);
651681a5bbeSBoris Popov 	}
652681a5bbeSBoris Popov 	np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
653681a5bbeSBoris Popov 	if (np->n_flag & NFLUSHWANT) {
654681a5bbeSBoris Popov 		np->n_flag &= ~NFLUSHWANT;
655521f364bSDag-Erling Smørgrav 		wakeup(&np->n_flag);
656681a5bbeSBoris Popov 	}
657681a5bbeSBoris Popov 	return (error);
658681a5bbeSBoris Popov }
659