1 /*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * $Id: nfs_bio.c,v 1.1 94/10/20 10:57:29 root Exp Locker: bill $
37 */
38
39 #include "sys/param.h"
40 #include "sys/file.h"
41 #include "sys/mount.h"
42 #include "uio.h"
43 #include "ucred.h"
44 #include "sys/time.h"
45 #include "sys/errno.h"
46 #include "proc.h" /* for curproc only */
47 #include "buf.h"
48
49 #include "namei.h"
50 #include "vnode.h"
51 #include "nfs_node.h"
52 #include "nfs_v2.h"
53 #include "nfs.h"
54 #include "nfs_iom.h"
55 #include "nfs_mount.h"
56 #include "prototypes.h"
57
58 /* True and false, how exciting */
59 #define TRUE 1
60 #define FALSE 0
61
62 /*
63 * Vnode op for read using bio
64 * Any similarity to readip() is purely coincidental
65 */
nfs_bioread(vp,uio,ioflag,cred)66 nfs_bioread(vp, uio, ioflag, cred)
67 register struct vnode *vp;
68 register struct uio *uio;
69 int ioflag;
70 struct ucred *cred;
71 {
72 register struct nfsnode *np = VTONFS(vp);
73 register int biosize;
74 struct buf *bp;
75 struct vattr vattr;
76 daddr_t lbn, bn, rablock;
77 int diff, error = 0;
78 long n, on;
79
80 #ifdef lint
81 ioflag = ioflag;
82 #endif /* lint */
83 /*#ifdef DIAGNOSTIC*/
84 #ifdef DEBUG
85 if (uio->uio_rw != UIO_READ)
86 panic("nfs_read mode");
87 #endif
88 if (uio->uio_resid == 0)
89 return (0);
90 if (uio->uio_offset < 0 && vp->v_type != VDIR)
91 return (EINVAL);
92 biosize = VFSTONFS(vp->v_mount)->nm_rsize;
93 /*
94 * If the file's modify time on the server has changed since the
95 * last read rpc or you have written to the file,
96 * you may have lost data cache consistency with the
97 * server, so flush all of the file's data out of the cache.
98 * Then force a getattr rpc to ensure that you have up to date
99 * attributes.
100 * NB: This implies that cache data can be read when up to
101 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
102 * attributes this could be forced by setting n_attrstamp to 0 before
103 * the nfs_dogetattr() call.
104 */
105 if (vp->v_type != VLNK) {
106 if (np->n_flag & NMODIFIED) {
107 np->n_flag &= ~NMODIFIED;
108 vinvalbuf(vp, TRUE);
109 np->n_attrstamp = 0;
110 np->n_direofoffset = 0;
111 if (error = nfs_dogetattr(vp, &vattr, cred, 1,
112 uio->uio_procp))
113 return (error);
114 np->n_mtime = vattr.va_mtime.tv_sec;
115 } else {
116 if (error = nfs_dogetattr(vp, &vattr, cred, 1,
117 uio->uio_procp))
118 return (error);
119 if (np->n_mtime != vattr.va_mtime.tv_sec) {
120 np->n_direofoffset = 0;
121 vinvalbuf(vp, TRUE);
122 np->n_mtime = vattr.va_mtime.tv_sec;
123 }
124 }
125 }
126 do {
127 switch (vp->v_type) {
128 case VREG:
129 nfsstats.biocache_reads++;
130 lbn = uio->uio_offset / biosize;
131 on = uio->uio_offset & (biosize-1);
132 n = min((unsigned)(biosize - on), uio->uio_resid);
133 diff = np->n_size - uio->uio_offset;
134 if (diff <= 0)
135 return (error);
136 if (diff < n)
137 n = diff;
138 bn = lbn*(biosize/DEV_BSIZE);
139 rablock = (lbn+1)*(biosize/DEV_BSIZE);
140 if (vp->v_lastr + 1 == lbn &&
141 np->n_size > (rablock * DEV_BSIZE))
142 error = breada(vp, bn, biosize, rablock, biosize,
143 cred, &bp);
144 else
145 error = bread(vp, bn, biosize, cred, &bp);
146 vp->v_lastr = lbn;
147 if (bp->b_resid) {
148 diff = (on >= (biosize-bp->b_resid)) ? 0 :
149 (biosize-bp->b_resid-on);
150 n = min(n, diff);
151 }
152 break;
153 case VLNK:
154 nfsstats.biocache_readlinks++;
155 on = 0;
156 error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
157 n = min(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
158 break;
159 case VDIR:
160 nfsstats.biocache_readdirs++;
161 on = 0;
162 error = bread(vp, uio->uio_offset, NFS_DIRBLKSIZ, cred, &bp);
163 n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid);
164 break;
165 };
166 if (error) {
167 brelse(bp);
168 return (error);
169 }
170 if (n > 0)
171 error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
172 switch (vp->v_type) {
173 case VREG:
174 if (n+on == biosize || uio->uio_offset == np->n_size)
175 bp->b_flags |= B_AGE;
176 break;
177 case VLNK:
178 n = 0;
179 break;
180 case VDIR:
181 uio->uio_offset = bp->b_blkno;
182 break;
183 };
184 brelse(bp);
185 } while (error == 0 && uio->uio_resid > 0 && n != 0);
186 return (error);
187 }
188
189 /*
190 * Vnode op for write using bio
191 */
nfs_write(vp,uio,ioflag,cred)192 nfs_write(vp, uio, ioflag, cred)
193 register struct vnode *vp;
194 register struct uio *uio;
195 int ioflag;
196 struct ucred *cred;
197 {
198 struct proc *p = uio->uio_procp;
199 register int biosize;
200 struct buf *bp;
201 struct nfsnode *np = VTONFS(vp);
202 struct vattr vattr;
203 daddr_t lbn, bn;
204 int n, on, error = 0;
205
206 /* #ifdef DIAGNOSTIC */
207 #ifdef DEBUG
208 if (uio->uio_rw != UIO_WRITE)
209 panic("nfs_write mode");
210 if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
211 panic("nfs_write proc");
212 #endif
213 if (vp->v_type != VREG)
214 return (EIO);
215 /* Should we try and do this ?? */
216 if (ioflag & (IO_APPEND | IO_SYNC)) {
217 if (np->n_flag & NMODIFIED) {
218 np->n_flag &= ~NMODIFIED;
219 vinvalbuf(vp, TRUE);
220 }
221 if (ioflag & IO_APPEND) {
222 np->n_attrstamp = 0;
223 if (error = nfs_dogetattr(vp, &vattr, cred, 1, p))
224 return (error);
225 uio->uio_offset = np->n_size;
226 }
227 return (nfs_writerpc(vp, uio, cred));
228 }
229 #ifdef notdef
230 cnt = uio->uio_resid;
231 osize = np->n_size;
232 #endif
233 if (uio->uio_offset < 0)
234 return (EINVAL);
235 if (uio->uio_resid == 0)
236 return (0);
237
238 /*
239 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
240 * will be the same size within a filesystem. nfs_writerpc will
241 * still use nm_wsize when sizing the rpc's.
242 */
243 biosize = VFSTONFS(vp->v_mount)->nm_rsize;
244 np->n_flag |= NMODIFIED;
245 do {
246 nfsstats.biocache_writes++;
247 lbn = uio->uio_offset / biosize;
248 on = uio->uio_offset & (biosize-1);
249 n = min((unsigned)(biosize - on), uio->uio_resid);
250 if (uio->uio_offset+n > np->n_size) {
251 np->n_size = uio->uio_offset+n;
252 vnode_pager_setsize(vp, np->n_size);
253 }
254 bn = lbn*(biosize/DEV_BSIZE);
255 again:
256 bp = getblk(vp, bn, biosize);
257 if (bp->b_wcred == NOCRED) {
258 crhold(cred);
259 bp->b_wcred = cred;
260 }
261 if (bp->b_dirtyend > 0) {
262 /*
263 * If the new write will leave a contiguous dirty
264 * area, just update the b_dirtyoff and b_dirtyend,
265 * otherwise force a write rpc of the old dirty area.
266 */
267 if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
268 bp->b_dirtyoff = min(on, bp->b_dirtyoff);
269 bp->b_dirtyend = max((on+n), bp->b_dirtyend);
270 } else {
271 bp->b_proc = p;
272 if (error = bwrite(bp))
273 return (error);
274 goto again;
275 }
276 } else {
277 bp->b_dirtyoff = on;
278 bp->b_dirtyend = on+n;
279 }
280 if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
281 brelse(bp);
282 return (error);
283 }
284 if ((n+on) == biosize) {
285 bp->b_flags |= B_AGE;
286 bp->b_proc = (struct proc *)0;
287 bawrite(bp);
288 } else {
289 bp->b_proc = (struct proc *)0;
290 bdwrite(bp, vp);
291 }
292 } while (error == 0 && uio->uio_resid > 0 && n != 0);
293 #ifdef notdef
294 /* Should we try and do this for nfs ?? */
295 if (error && (ioflag & IO_UNIT)) {
296 np->n_size = osize;
297 uio->uio_offset -= cnt - uio->uio_resid;
298 uio->uio_resid = cnt;
299 }
300 #endif
301 return (error);
302 }
303