1 /* $NetBSD: smbfs_io.c,v 1.16 2003/10/25 08:39:05 christos Exp $ */
2
3 /*
4 * Copyright (c) 2000-2001, Boris Popov
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * FreeBSD: src/sys/fs/smbfs/smbfs_io.c,v 1.7 2001/12/02 08:56:58 bp Exp
35 *
36 */
37
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: smbfs_io.c,v 1.16 2003/10/25 08:39:05 christos Exp $");
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/resourcevar.h> /* defines plimit structure in proc struct */
44 #include <sys/kernel.h>
45 #include <sys/proc.h>
46 #include <sys/fcntl.h>
47 #include <sys/buf.h>
48 #include <sys/mount.h>
49 #include <sys/namei.h>
50 #include <sys/vnode.h>
51 #include <sys/dirent.h>
52 #include <sys/signalvar.h>
53 #include <sys/sysctl.h>
54 #include <sys/vmmeter.h>
55
56 #ifndef __NetBSD__
57 #include <vm/vm.h>
58 #if __FreeBSD_version < 400000
59 #include <vm/vm_prot.h>
60 #endif
61 #include <vm/vm_page.h>
62 #include <vm/vm_extern.h>
63 #include <vm/vm_object.h>
64 #include <vm/vm_pager.h>
65 #include <vm/vnode_pager.h>
66 #else /* __NetBSD__ */
67 #include <uvm/uvm.h>
68 #include <uvm/uvm_extern.h>
69 #endif /* __NetBSD__ */
70
71 /*
72 #include <sys/ioccom.h>
73 */
74 #include <netsmb/smb.h>
75 #include <netsmb/smb_conn.h>
76 #include <netsmb/smb_subr.h>
77
78 #include <fs/smbfs/smbfs.h>
79 #include <fs/smbfs/smbfs_node.h>
80 #include <fs/smbfs/smbfs_subr.h>
81
82 /*#define SMBFS_RWGENERIC*/
83
84 #define DE_SIZE (sizeof(struct dirent))
85
86 static int
87 smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred)
88 {
89 struct dirent de;
90 struct smb_cred scred;
91 struct smbfs_fctx *ctx;
92 struct smbnode *np = VTOSMB(vp);
93 int error = 0/*, *eofflag = ap->a_eofflag*/;
94 long offset, limit;
95
96 KASSERT(vp->v_type == VDIR);
97
98 if (uio->uio_resid < DE_SIZE || uio->uio_offset < 0)
99 return EINVAL;
100
101 SMBVDEBUG("dirname='%.*s'\n", (int) np->n_nmlen, np->n_name);
102 smb_makescred(&scred, uio->uio_procp, cred);
103 offset = uio->uio_offset / DE_SIZE; /* offset in the directory */
104 limit = uio->uio_resid / DE_SIZE;
105
106 /* Simulate . */
107 if (offset < 1) {
108 memset(&de, 0, sizeof(de));
109 de.d_fileno = np->n_ino;
110 de.d_reclen = DE_SIZE;
111 de.d_type = DT_DIR;
112 de.d_namlen = 1;
113 strncpy(de.d_name, ".", 2);
114 error = uiomove((caddr_t)&de, sizeof(struct dirent), uio);
115 if (error)
116 return error;
117 limit--;
118 offset++;
119 }
120 /* Simulate .. */
121 if (limit > 0 && offset < 2) {
122 memset(&de, 0, sizeof(de));
123 de.d_fileno = (np->n_parent ? np->n_parent->n_ino : 2);
124 de.d_reclen = DE_SIZE;
125 de.d_type = DT_DIR;
126 de.d_namlen = 2;
127 strncpy(de.d_name, "..", 3);
128 error = uiomove((caddr_t)&de, sizeof(struct dirent), uio);
129 if (error)
130 return error;
131 limit--;
132 offset++;
133 }
134
135 if (limit == 0)
136 return (0);
137
138 if (offset != np->n_dirofs || np->n_dirseq == NULL) {
139 SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs);
140 if (np->n_dirseq) {
141 smbfs_findclose(np->n_dirseq, &scred);
142 np->n_dirseq = NULL;
143 }
144 np->n_dirofs = 2;
145 error = smbfs_findopen(np, "*", 1,
146 SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR,
147 &scred, &ctx);
148 if (error) {
149 SMBVDEBUG("can not open search, error = %d", error);
150 return error;
151 }
152 np->n_dirseq = ctx;
153 } else
154 ctx = np->n_dirseq;
155
156 /* skip entries before offset */
157 while (np->n_dirofs < offset) {
158 error = smbfs_findnext(ctx, offset - np->n_dirofs++, &scred);
159 if (error) {
160 smbfs_findclose(np->n_dirseq, &scred);
161 np->n_dirseq = NULL;
162 return (error == ENOENT) ? 0 : error;
163 }
164 }
165
166 for (; limit; limit--, offset++) {
167 error = smbfs_findnext(ctx, limit, &scred);
168 if (error) {
169 if (error == ENOENT)
170 error = 0;
171 break;
172 }
173 np->n_dirofs++;
174 memset(&de, 0, DE_SIZE);
175 de.d_reclen = DE_SIZE;
176 de.d_fileno = ctx->f_attr.fa_ino;
177 de.d_type = (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG;
178 de.d_namlen = ctx->f_nmlen;
179 memcpy(de.d_name, ctx->f_name, de.d_namlen);
180 de.d_name[de.d_namlen] = '\0';
181 error = uiomove((caddr_t)&de, DE_SIZE, uio);
182 if (error)
183 break;
184 }
185
186 return (error);
187 }
188
189 int
190 smbfs_readvnode(struct vnode *vp, struct uio *uiop, struct ucred *cred)
191 {
192 struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
193 struct smbnode *np = VTOSMB(vp);
194 struct proc *p;
195 struct vattr vattr;
196 struct smb_cred scred;
197 int error;
198
199 KASSERT(vp->v_type == VREG || vp->v_type == VDIR);
200
201 if (uiop->uio_resid == 0)
202 return 0;
203 if (uiop->uio_offset < 0)
204 return EINVAL;
205 /* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
206 return EFBIG;*/
207 if (vp->v_type == VDIR) {
208 error = smbfs_readvdir(vp, uiop, cred);
209 return error;
210 }
211
212 p = uiop->uio_procp;
213 if (np->n_flag & NMODIFIED) {
214 smbfs_attr_cacheremove(vp);
215 error = VOP_GETATTR(vp, &vattr, cred, p);
216 if (error)
217 return error;
218 np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
219 } else {
220 error = VOP_GETATTR(vp, &vattr, cred, p);
221 if (error)
222 return error;
223 if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
224 error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
225 if (error)
226 return error;
227 np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
228 }
229 }
230 smb_makescred(&scred, p, cred);
231 return smb_read(smp->sm_share, np->n_fid, uiop, &scred);
232 }
233
234 int
235 smbfs_writevnode(struct vnode *vp, struct uio *uiop,
236 struct ucred *cred, int ioflag)
237 {
238 struct smbmount *smp = VTOSMBFS(vp);
239 struct smbnode *np = VTOSMB(vp);
240 struct smb_cred scred;
241 struct proc *p;
242 int error = 0;
243 int extended = 0;
244 size_t resid = uiop->uio_resid;
245
246 /* vn types other than VREG unsupported */
247 KASSERT(vp->v_type == VREG);
248
249 SMBVDEBUG("ofs=%lld,resid=%d\n",
250 (long long int) uiop->uio_offset,
251 uiop->uio_resid);
252 if (uiop->uio_offset < 0)
253 return EINVAL;
254 /* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
255 return (EFBIG);*/
256 p = uiop->uio_procp;
257 if (ioflag & (IO_APPEND | IO_SYNC)) {
258 if (np->n_flag & NMODIFIED) {
259 smbfs_attr_cacheremove(vp);
260 error = smbfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
261 if (error)
262 return error;
263 }
264 if (ioflag & IO_APPEND) {
265 #if notyet
266 /*
267 * File size can be changed by another client
268 */
269 smbfs_attr_cacheremove(vp);
270 error = VOP_GETATTR(vp, &vattr, cred, td);
271 if (error)
272 return (error);
273 #endif
274 uiop->uio_offset = np->n_size;
275 }
276 }
277 if (uiop->uio_resid == 0)
278 return 0;
279 if (p && uiop->uio_offset + uiop->uio_resid > p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
280 psignal(p, SIGXFSZ);
281 return EFBIG;
282 }
283 smb_makescred(&scred, p, cred);
284 error = smb_write(smp->sm_share, np->n_fid, uiop, &scred);
285 SMBVDEBUG("after: ofs=%lld,resid=%d,err=%d\n",(long long int)uiop->uio_offset, uiop->uio_resid, error);
286 if (!error) {
287 if (uiop->uio_offset > np->n_size) {
288 np->n_size = uiop->uio_offset;
289 uvm_vnp_setsize(vp, np->n_size);
290 extended = 1;
291 }
292
293 }
294 if (resid > uiop->uio_resid)
295 VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0));
296 return error;
297 }
298
299 /*
300 * Do an I/O operation to/from a cache block.
301 */
302 int
303 smbfs_doio(struct buf *bp, struct ucred *cr, struct proc *p)
304 {
305 struct vnode *vp = bp->b_vp;
306 struct smbmount *smp = VFSTOSMBFS(vp->v_mount);
307 struct smbnode *np = VTOSMB(vp);
308 struct uio uio, *uiop = &uio;
309 struct iovec io;
310 struct smb_cred scred;
311 int error = 0;
312
313 uiop->uio_iov = &io;
314 uiop->uio_iovcnt = 1;
315 uiop->uio_segflg = UIO_SYSSPACE;
316 uiop->uio_procp = p;
317
318 smb_makescred(&scred, p, cr);
319
320 if (bp->b_flags == B_READ) {
321 io.iov_len = uiop->uio_resid = bp->b_bcount;
322 io.iov_base = bp->b_data;
323 uiop->uio_rw = UIO_READ;
324 switch (vp->v_type) {
325 case VREG:
326 uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
327 error = smb_read(smp->sm_share, np->n_fid, uiop, &scred);
328 if (error)
329 break;
330 if (uiop->uio_resid) {
331 int left = uiop->uio_resid;
332 int nread = bp->b_bcount - left;
333 if (left > 0)
334 bzero((char *)bp->b_data + nread, left);
335 }
336 break;
337 default:
338 printf("smbfs_doio: type %x unexpected\n",vp->v_type);
339 break;
340 };
341 if (error) {
342 bp->b_error = error;
343 bp->b_flags |= B_ERROR;
344 }
345 } else { /* write */
346 io.iov_len = uiop->uio_resid = bp->b_bcount;
347 uiop->uio_offset = ((off_t)bp->b_blkno) << DEV_BSHIFT;
348 io.iov_base = bp->b_data;
349 uiop->uio_rw = UIO_WRITE;
350 bp->b_flags |= B_BUSY;
351 error = smb_write(smp->sm_share, np->n_fid, uiop, &scred);
352 bp->b_flags &= ~B_BUSY;
353
354
355 #ifndef __NetBSD__ /* XXX */
356 /*
357 * For an interrupted write, the buffer is still valid
358 * and the write hasn't been pushed to the server yet,
359 * so we can't set BIO_ERROR and report the interruption
360 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
361 * is not relevant, so the rpc attempt is essentially
362 * a noop. For the case of a V3 write rpc not being
363 * committed to stable storage, the block is still
364 * dirty and requires either a commit rpc or another
365 * write rpc with iomode == NFSV3WRITE_FILESYNC before
366 * the block is reused. This is indicated by setting
367 * the B_DELWRI and B_NEEDCOMMIT flags.
368 */
369 if (error == EINTR
370 || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
371 int s;
372
373 s = splbio();
374 bp->b_flags &= ~(B_INVAL|B_NOCACHE);
375 if ((bp->b_flags & B_ASYNC) == 0)
376 bp->b_flags |= B_EINTR;
377 if ((bp->b_flags & B_PAGING) == 0) {
378 bdirty(bp);
379 bp->b_flags &= ~B_DONE;
380 }
381 if ((bp->b_flags & B_ASYNC) == 0)
382 bp->b_flags |= B_EINTR;
383 splx(s);
384 } else {
385 if (error) {
386 bp->b_ioflags |= BIO_ERROR;
387 bp->b_error = error;
388 }
389 bp->b_dirtyoff = bp->b_dirtyend = 0;
390 }
391 #endif /* !__NetBSD__ */
392 }
393 bp->b_resid = uiop->uio_resid;
394 biodone(bp);
395 return error;
396 }
397
398 /*
399 * Flush and invalidate all dirty buffers. If another process is already
400 * doing the flush, just wait for completion.
401 */
402 int
403 smbfs_vinvalbuf(vp, flags, cred, p, intrflg)
404 struct vnode *vp;
405 int flags;
406 struct ucred *cred;
407 struct proc *p;
408 int intrflg;
409 {
410 struct smbnode *np = VTOSMB(vp);
411 int error = 0, slpflag;
412
413 if (intrflg)
414 slpflag = PCATCH;
415 else
416 slpflag = 0;
417
418 while (np->n_flag & NFLUSHINPROG) {
419 np->n_flag |= NFLUSHWANT;
420 error = tsleep((caddr_t)&np->n_flag,
421 (PRIBIO + 2) | slpflag, "smfsvinv", 0);
422 if (error)
423 return (error);
424 }
425 np->n_flag |= NFLUSHINPROG;
426 for(;;) {
427 if ((error = vinvalbuf(vp, flags, cred, p, slpflag, 0)) == 0)
428 break;
429
430 if (intrflg && (error == ERESTART || error == EINTR)) {
431 np->n_flag &= ~NFLUSHINPROG;
432 if (np->n_flag & NFLUSHWANT) {
433 np->n_flag &= ~NFLUSHWANT;
434 wakeup((caddr_t)&np->n_flag);
435 }
436 return (error);
437 }
438 }
439 np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
440 if (np->n_flag & NFLUSHWANT) {
441 np->n_flag &= ~NFLUSHWANT;
442 wakeup((caddr_t)&np->n_flag);
443 }
444 return (error);
445 }
Cache object: 8f95c928c70c118976b5980019da2141
|