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