2 * Copyright (c) 2000-2001, Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * $FreeBSD: src/sys/fs/smbfs/smbfs_io.c,v 1.3.2.3 2003/01/17 08:20:26 tjr Exp $
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/resourcevar.h> /* defines plimit structure in proc struct */
39 #include <sys/kernel.h>
41 #include <sys/fcntl.h>
42 #include <sys/mount.h>
43 #include <sys/vnode.h>
44 #include <sys/dirent.h>
45 #include <sys/signalvar.h>
46 #include <sys/sysctl.h>
48 #include <machine/limits.h>
51 #include <vm/vm_page2.h>
52 #include <vm/vm_extern.h>
53 #include <vm/vm_object.h>
54 #include <vm/vm_pager.h>
55 #include <vm/vnode_pager.h>
57 #include <netproto/smb/smb.h>
58 #include <netproto/smb/smb_conn.h>
59 #include <netproto/smb/smb_subr.h>
62 #include "smbfs_node.h"
63 #include "smbfs_subr.h"
67 #include <sys/thread2.h>
69 /*#define SMBFS_RWGENERIC*/
71 extern int smbfs_pbuf_freecnt
;
73 static int smbfs_fastlookup
= 1;
75 SYSCTL_DECL(_vfs_smbfs
);
76 SYSCTL_INT(_vfs_smbfs
, OID_AUTO
, fastlookup
, CTLFLAG_RW
, &smbfs_fastlookup
, 0, "");
79 smbfs_readvdir(struct vnode
*vp
, struct uio
*uio
, struct ucred
*cred
)
81 struct smb_cred scred
;
82 struct smbfs_fctx
*ctx
;
85 int error
, offset
, retval
;
88 SMBVDEBUG("dirname='%s'\n", np
->n_name
);
89 smb_makescred(&scred
, uio
->uio_td
, cred
);
91 if (uio
->uio_offset
< 0 || uio
->uio_offset
> INT_MAX
)
95 offset
= uio
->uio_offset
;
97 if (uio
->uio_resid
> 0 && offset
< 1) {
98 if (vop_write_dirent(&error
, uio
, np
->n_ino
, DT_DIR
, 1, "."))
105 if (uio
->uio_resid
> 0 && offset
< 2) {
106 if (vop_write_dirent(&error
, uio
,
107 np
->n_parent
? VTOSMB(np
->n_parent
)->n_ino
: 2,
115 if (uio
->uio_resid
== 0)
118 if (offset
!= np
->n_dirofs
|| np
->n_dirseq
== NULL
) {
119 SMBVDEBUG("Reopening search %ld:%ld\n", offset
, np
->n_dirofs
);
121 smbfs_findclose(np
->n_dirseq
, &scred
);
125 error
= smbfs_findopen(np
, "*", 1,
126 SMB_FA_SYSTEM
| SMB_FA_HIDDEN
| SMB_FA_DIR
,
129 SMBVDEBUG("can not open search, error = %d", error
);
136 while (np
->n_dirofs
< offset
) {
137 error
= smbfs_findnext(ctx
, offset
- np
->n_dirofs
, &scred
);
140 smbfs_findclose(np
->n_dirseq
, &scred
);
142 return error
== ENOENT
? 0 : error
;
146 while (uio
->uio_resid
> 0 && !error
) {
148 * Overestimate the size of a record a bit, doesn't really
149 * hurt to be wrong here.
151 error
= smbfs_findnext(ctx
, uio
->uio_resid
/ _DIRENT_RECLEN(255) + 1, &scred
);
157 retval
= vop_write_dirent(&error
, uio
, ctx
->f_attr
.fa_ino
,
158 (ctx
->f_attr
.fa_attr
& SMB_FA_DIR
) ? DT_DIR
: DT_REG
,
159 ctx
->f_nmlen
, ctx
->f_name
);
162 if (smbfs_fastlookup
&& !error
) {
163 error
= smbfs_nget(vp
->v_mount
, vp
, ctx
->f_name
,
164 ctx
->f_nmlen
, &ctx
->f_attr
, &newvp
);
172 uio
->uio_offset
= offset
;
177 smbfs_readvnode(struct vnode
*vp
, struct uio
*uiop
, struct ucred
*cred
)
180 struct smbmount
*smp
= VFSTOSMBFS(vp
->v_mount
);
181 struct smbnode
*np
= VTOSMB(vp
);
183 struct smb_cred scred
;
187 * Protect against method which is not supported for now
189 if (uiop
->uio_segflg
== UIO_NOCOPY
)
192 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
) {
193 SMBFSERR("vn types other than VREG or VDIR are unsupported !\n");
196 if (uiop
->uio_resid
== 0)
198 if (uiop
->uio_offset
< 0)
201 if (vp
->v_type
== VDIR
) {
202 lks
= LK_EXCLUSIVE
;/*lockstatus(&vp->v_lock, td);*/
203 if (lks
== LK_SHARED
)
204 vn_lock(vp
, LK_UPGRADE
| LK_RETRY
);
205 error
= smbfs_readvdir(vp
, uiop
, cred
);
206 if (lks
== LK_SHARED
)
207 vn_lock(vp
, LK_DOWNGRADE
| LK_RETRY
);
211 /* biosize = SSTOCN(smp->sm_share)->sc_txmax;*/
212 if (np
->n_flag
& NMODIFIED
) {
213 smbfs_attr_cacheremove(vp
);
214 error
= VOP_GETATTR(vp
, &vattr
);
217 np
->n_mtime
.tv_sec
= vattr
.va_mtime
.tv_sec
;
219 error
= VOP_GETATTR(vp
, &vattr
);
222 if (np
->n_mtime
.tv_sec
!= vattr
.va_mtime
.tv_sec
) {
223 error
= smbfs_vinvalbuf(vp
, V_SAVE
, 1);
226 np
->n_mtime
.tv_sec
= vattr
.va_mtime
.tv_sec
;
229 smb_makescred(&scred
, td
, cred
);
230 return smb_read(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
234 smbfs_writevnode(struct vnode
*vp
, struct uio
*uiop
,
235 struct ucred
*cred
, int ioflag
)
238 struct smbmount
*smp
= VTOSMBFS(vp
);
239 struct smbnode
*np
= VTOSMB(vp
);
240 struct smb_cred scred
;
243 if (vp
->v_type
!= VREG
) {
244 SMBERROR("vn types other than VREG unsupported !\n");
247 SMBVDEBUG("ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
248 if (uiop
->uio_offset
< 0)
251 if (ioflag
& (IO_APPEND
| IO_SYNC
)) {
252 if (np
->n_flag
& NMODIFIED
) {
253 smbfs_attr_cacheremove(vp
);
254 error
= smbfs_vinvalbuf(vp
, V_SAVE
, 1);
258 if (ioflag
& IO_APPEND
) {
261 * File size can be changed by another client
263 smbfs_attr_cacheremove(vp
);
264 error
= VOP_GETATTR(vp
, &vattr
);
265 if (error
) return (error
);
267 uiop
->uio_offset
= np
->n_size
;
270 if (uiop
->uio_resid
== 0)
273 uiop
->uio_offset
+ uiop
->uio_resid
>
274 td
->td_proc
->p_rlimit
[RLIMIT_FSIZE
].rlim_cur
) {
275 lwpsignal(td
->td_proc
, td
->td_lwp
, SIGXFSZ
);
278 smb_makescred(&scred
, td
, cred
);
279 error
= smb_write(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
280 SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
282 if (uiop
->uio_offset
> np
->n_size
) {
283 np
->n_size
= uiop
->uio_offset
;
284 vnode_pager_setsize(vp
, np
->n_size
);
291 * Do an I/O operation to/from a cache block.
294 smbfs_doio(struct vnode
*vp
, struct bio
*bio
, struct ucred
*cr
, struct thread
*td
)
296 struct buf
*bp
= bio
->bio_buf
;
297 struct smbmount
*smp
= VFSTOSMBFS(vp
->v_mount
);
298 struct smbnode
*np
= VTOSMB(vp
);
299 struct uio uio
, *uiop
= &uio
;
301 struct smb_cred scred
;
305 uiop
->uio_iovcnt
= 1;
306 uiop
->uio_segflg
= UIO_SYSSPACE
;
309 smb_makescred(&scred
, td
, cr
);
311 if (bp
->b_cmd
== BUF_CMD_READ
) {
312 io
.iov_len
= uiop
->uio_resid
= (size_t)bp
->b_bcount
;
313 io
.iov_base
= bp
->b_data
;
314 uiop
->uio_rw
= UIO_READ
;
315 switch (vp
->v_type
) {
317 uiop
->uio_offset
= bio
->bio_offset
;
318 error
= smb_read(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
321 if (uiop
->uio_resid
) {
322 size_t left
= uiop
->uio_resid
;
323 size_t nread
= (size_t)bp
->b_bcount
- left
;
325 bzero((char *)bp
->b_data
+ nread
, left
);
329 kprintf("smbfs_doio: type %x unexpected\n",vp
->v_type
);
334 bp
->b_flags
|= B_ERROR
;
337 KKASSERT(bp
->b_cmd
== BUF_CMD_WRITE
);
338 if (bio
->bio_offset
+ bp
->b_dirtyend
> np
->n_size
)
339 bp
->b_dirtyend
= np
->n_size
- bio
->bio_offset
;
341 if (bp
->b_dirtyend
> bp
->b_dirtyoff
) {
342 io
.iov_len
= uiop
->uio_resid
=
343 (size_t)(bp
->b_dirtyend
- bp
->b_dirtyoff
);
344 uiop
->uio_offset
= bio
->bio_offset
+ bp
->b_dirtyoff
;
345 io
.iov_base
= (char *)bp
->b_data
+ bp
->b_dirtyoff
;
346 uiop
->uio_rw
= UIO_WRITE
;
347 error
= smb_write(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
350 * For an interrupted write, the buffer is still valid
351 * and the write hasn't been pushed to the server yet,
352 * so we can't set BIO_ERROR and report the interruption
353 * by setting B_EINTR. For the async case, B_EINTR
354 * is not relevant, so the rpc attempt is essentially
355 * a noop. For the case of a V3 write rpc not being
356 * committed to stable storage, the block is still
357 * dirty and requires either a commit rpc or another
358 * write rpc with iomode == NFSV3WRITE_FILESYNC before
359 * the block is reused. This is indicated by setting
360 * the B_DELWRI and B_NEEDCOMMIT flags.
363 || (!error
&& (bp
->b_flags
& B_NEEDCOMMIT
))) {
366 bp
->b_flags
&= ~(B_INVAL
|B_NOCACHE
);
367 if ((bp
->b_flags
& B_PAGING
) == 0)
369 bp
->b_flags
|= B_EINTR
;
373 bp
->b_flags
|= B_ERROR
;
376 bp
->b_dirtyoff
= bp
->b_dirtyend
= 0;
384 bp
->b_resid
= uiop
->uio_resid
;
390 * Vnode op for VM getpages.
391 * Wish wish .... get rid from multiple IO routines
393 * smbfs_getpages(struct vnode *a_vp, vm_page_t *a_m, int a_count,
394 * int a_reqpage, vm_ooffset_t a_offset)
397 smbfs_getpages(struct vop_getpages_args
*ap
)
399 #ifdef SMBFS_RWGENERIC
400 return vop_stdgetpages(ap
);
402 int i
, error
, npages
;
404 size_t size
, toff
, nextoff
, count
;
410 struct thread
*td
= curthread
; /* XXX */
412 struct smbmount
*smp
;
414 struct smb_cred scred
;
417 KKASSERT(td
->td_proc
);
420 cred
= td
->td_proc
->p_ucred
;
422 smp
= VFSTOSMBFS(vp
->v_mount
);
424 count
= (size_t)ap
->a_count
;
426 if (vp
->v_object
== NULL
) {
427 kprintf("smbfs_getpages: called with non-merged cache vnode??\n");
428 return VM_PAGER_ERROR
;
430 smb_makescred(&scred
, td
, cred
);
432 bp
= getpbuf_kva(&smbfs_pbuf_freecnt
);
433 npages
= btoc(count
);
434 kva
= (vm_offset_t
) bp
->b_data
;
435 pmap_qenter(kva
, pages
, npages
);
437 iov
.iov_base
= (caddr_t
) kva
;
441 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
442 uio
.uio_resid
= count
;
443 uio
.uio_segflg
= UIO_SYSSPACE
;
444 uio
.uio_rw
= UIO_READ
;
448 * This is kinda nasty. Since smbfs is physically closing the
449 * fid on close(), we have to reopen it if necessary. There are
450 * other races here too, such as if another process opens the same
451 * file while we are blocked in read. XXX
455 if (np
->n_opencount
== 0) {
456 error
= smbfs_smb_open(np
, SMB_AM_OPENREAD
, &scred
);
461 error
= smb_read(smp
->sm_share
, np
->n_fid
, &uio
, &scred
);
463 smbfs_smb_close(smp
->sm_share
, np
->n_fid
, NULL
, &scred
);
464 pmap_qremove(kva
, npages
);
466 relpbuf(bp
, &smbfs_pbuf_freecnt
);
468 if (error
&& (uio
.uio_resid
== count
)) {
469 kprintf("smbfs_getpages: error %d\n",error
);
470 for (i
= 0; i
< npages
; i
++) {
471 if (ap
->a_reqpage
!= i
)
472 vnode_pager_freepage(pages
[i
]);
474 return VM_PAGER_ERROR
;
477 size
= count
- uio
.uio_resid
;
479 for (i
= 0, toff
= 0; i
< npages
; i
++, toff
= nextoff
) {
481 nextoff
= toff
+ PAGE_SIZE
;
485 * NOTE: pmap dirty bit should have already been cleared.
486 * We do not clear it here.
488 if (nextoff
<= size
) {
489 m
->valid
= VM_PAGE_BITS_ALL
;
492 int nvalid
= rounddown2((size
+ DEV_BSIZE
- 1) - toff
,
494 vm_page_set_validclean(m
, 0, nvalid
);
497 if (i
!= ap
->a_reqpage
) {
499 * Whether or not to leave the page activated is up in
500 * the air, but we should put the page on a page queue
501 * somewhere (it already is in the object). Result:
502 * It appears that emperical results show that
503 * deactivating pages is best.
507 * Just in case someone was asking for this page we
508 * now tell them that it is ok to use.
511 if (m
->flags
& PG_REFERENCED
)
514 vm_page_deactivate(m
);
517 vnode_pager_freepage(m
);
522 #endif /* SMBFS_RWGENERIC */
526 * Vnode op for VM putpages.
527 * possible bug: all IO done in sync mode
528 * Note that vop_close always invalidate pages before close, so it's
529 * not necessary to open vnode.
531 * smbfs_putpages(struct vnode *a_vp, vm_page_t *a_m, int a_count, int a_flags,
532 * int *a_rtvals, vm_ooffset_t a_offset)
535 smbfs_putpages(struct vop_putpages_args
*ap
)
538 struct vnode
*vp
= ap
->a_vp
;
539 struct thread
*td
= curthread
; /* XXX */
542 #ifdef SMBFS_RWGENERIC
543 KKASSERT(td
->td_proc
);
544 cred
= td
->td_proc
->p_ucred
;
545 VOP_OPEN(vp
, FWRITE
, cred
, NULL
);
546 error
= vop_stdputpages(ap
);
547 VOP_CLOSE(vp
, FWRITE
, cred
, NULL
);
554 int i
, npages
, count
;
557 struct smbmount
*smp
;
559 struct smb_cred scred
;
562 KKASSERT(td
->td_proc
);
563 cred
= td
->td_proc
->p_ucred
;
564 /* VOP_OPEN(vp, FWRITE, cred, NULL);*/
566 smp
= VFSTOSMBFS(vp
->v_mount
);
569 rtvals
= ap
->a_rtvals
;
570 npages
= btoc(count
);
572 for (i
= 0; i
< npages
; i
++) {
573 rtvals
[i
] = VM_PAGER_AGAIN
;
576 bp
= getpbuf_kva(&smbfs_pbuf_freecnt
);
577 kva
= (vm_offset_t
) bp
->b_data
;
578 pmap_qenter(kva
, pages
, npages
);
580 iov
.iov_base
= (caddr_t
) kva
;
584 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
585 uio
.uio_resid
= count
;
586 uio
.uio_segflg
= UIO_SYSSPACE
;
587 uio
.uio_rw
= UIO_WRITE
;
589 SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio
.uio_offset
, uio
.uio_resid
);
591 smb_makescred(&scred
, td
, cred
);
594 * This is kinda nasty. Since smbfs is physically closing the
595 * fid on close(), we have to reopen it if necessary. There are
596 * other races here too, such as if another process opens the same
597 * file while we are blocked in read, or the file is open read-only
602 if (np
->n_opencount
== 0) {
603 error
= smbfs_smb_open(np
, SMB_AM_OPENRW
, &scred
);
608 error
= smb_write(smp
->sm_share
, np
->n_fid
, &uio
, &scred
);
610 smbfs_smb_close(smp
->sm_share
, np
->n_fid
, NULL
, &scred
);
611 /* VOP_CLOSE(vp, FWRITE, cred);*/
612 SMBVDEBUG("paged write done: %d\n", error
);
614 pmap_qremove(kva
, npages
);
615 relpbuf(bp
, &smbfs_pbuf_freecnt
);
618 int nwritten
= round_page(count
- uio
.uio_resid
) / PAGE_SIZE
;
619 for (i
= 0; i
< nwritten
; i
++) {
620 rtvals
[i
] = VM_PAGER_OK
;
621 vm_page_undirty(pages
[i
]);
625 #endif /* SMBFS_RWGENERIC */
629 * Flush and invalidate all dirty buffers. If another process is already
630 * doing the flush, just wait for completion.
633 smbfs_vinvalbuf(struct vnode
*vp
, int flags
, int intrflg
)
635 struct smbnode
*np
= VTOSMB(vp
);
636 int error
= 0, slpflag
, slptimeo
;
638 if (vp
->v_flag
& VRECLAIMED
)
647 while (np
->n_flag
& NFLUSHINPROG
) {
648 np
->n_flag
|= NFLUSHWANT
;
649 error
= tsleep((caddr_t
)&np
->n_flag
, 0, "smfsvinv", slptimeo
);
650 error
= smb_proc_intr(curthread
);
651 if (error
== EINTR
&& intrflg
)
654 np
->n_flag
|= NFLUSHINPROG
;
655 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
657 if (intrflg
&& (error
== ERESTART
|| error
== EINTR
)) {
658 np
->n_flag
&= ~NFLUSHINPROG
;
659 if (np
->n_flag
& NFLUSHWANT
) {
660 np
->n_flag
&= ~NFLUSHWANT
;
661 wakeup((caddr_t
)&np
->n_flag
);
665 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
667 np
->n_flag
&= ~(NMODIFIED
| NFLUSHINPROG
);
668 if (np
->n_flag
& NFLUSHWANT
) {
669 np
->n_flag
&= ~NFLUSHWANT
;
670 wakeup((caddr_t
)&np
->n_flag
);