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 $
33 * $DragonFly: src/sys/vfs/smbfs/smbfs_io.c,v 1.29 2007/08/28 01:04:33 dillon Exp $
36 #include <sys/param.h>
37 #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/namei.h>
44 #include <sys/vnode.h>
45 #include <sys/dirent.h>
46 #include <sys/signalvar.h>
47 #include <sys/sysctl.h>
49 #include <machine/limits.h>
52 #include <vm/vm_page.h>
53 #include <vm/vm_extern.h>
54 #include <vm/vm_object.h>
55 #include <vm/vm_pager.h>
56 #include <vm/vnode_pager.h>
58 #include <netproto/smb/smb.h>
59 #include <netproto/smb/smb_conn.h>
60 #include <netproto/smb/smb_subr.h>
63 #include "smbfs_node.h"
64 #include "smbfs_subr.h"
68 #include <sys/thread2.h>
70 /*#define SMBFS_RWGENERIC*/
72 extern int smbfs_pbuf_freecnt
;
74 static int smbfs_fastlookup
= 1;
76 SYSCTL_DECL(_vfs_smbfs
);
77 SYSCTL_INT(_vfs_smbfs
, OID_AUTO
, fastlookup
, CTLFLAG_RW
, &smbfs_fastlookup
, 0, "");
80 smbfs_readvdir(struct vnode
*vp
, struct uio
*uio
, struct ucred
*cred
)
82 struct smb_cred scred
;
83 struct smbfs_fctx
*ctx
;
86 int error
, offset
, retval
;
89 SMBVDEBUG("dirname='%s'\n", np
->n_name
);
90 smb_makescred(&scred
, uio
->uio_td
, cred
);
92 if (uio
->uio_offset
< 0 || uio
->uio_offset
> INT_MAX
)
96 offset
= uio
->uio_offset
;
98 if (uio
->uio_resid
> 0 && offset
< 1) {
99 if (vop_write_dirent(&error
, uio
, np
->n_ino
, DT_DIR
, 1, "."))
106 if (uio
->uio_resid
> 0 && offset
< 2) {
107 if (vop_write_dirent(&error
, uio
,
108 np
->n_parent
? VTOSMB(np
->n_parent
)->n_ino
: 2,
116 if (uio
->uio_resid
== 0)
119 if (offset
!= np
->n_dirofs
|| np
->n_dirseq
== NULL
) {
120 SMBVDEBUG("Reopening search %ld:%ld\n", offset
, np
->n_dirofs
);
122 smbfs_findclose(np
->n_dirseq
, &scred
);
126 error
= smbfs_findopen(np
, "*", 1,
127 SMB_FA_SYSTEM
| SMB_FA_HIDDEN
| SMB_FA_DIR
,
130 SMBVDEBUG("can not open search, error = %d", error
);
137 while (np
->n_dirofs
< offset
) {
138 error
= smbfs_findnext(ctx
, offset
- np
->n_dirofs
, &scred
);
141 smbfs_findclose(np
->n_dirseq
, &scred
);
143 return error
== ENOENT
? 0 : error
;
147 while (uio
->uio_resid
> 0 && !error
) {
149 * Overestimate the size of a record a bit, doesn't really
150 * hurt to be wrong here.
152 error
= smbfs_findnext(ctx
, uio
->uio_resid
/ _DIRENT_RECLEN(255) + 1, &scred
);
158 retval
= vop_write_dirent(&error
, uio
, ctx
->f_attr
.fa_ino
,
159 (ctx
->f_attr
.fa_attr
& SMB_FA_DIR
) ? DT_DIR
: DT_REG
,
160 ctx
->f_nmlen
, ctx
->f_name
);
163 if (smbfs_fastlookup
&& !error
) {
164 error
= smbfs_nget(vp
->v_mount
, vp
, ctx
->f_name
,
165 ctx
->f_nmlen
, &ctx
->f_attr
, &newvp
);
173 uio
->uio_offset
= offset
;
178 smbfs_readvnode(struct vnode
*vp
, struct uio
*uiop
, struct ucred
*cred
)
181 struct smbmount
*smp
= VFSTOSMBFS(vp
->v_mount
);
182 struct smbnode
*np
= VTOSMB(vp
);
184 struct smb_cred scred
;
188 * Protect against method which is not supported for now
190 if (uiop
->uio_segflg
== UIO_NOCOPY
)
193 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
) {
194 SMBFSERR("vn types other than VREG or VDIR are unsupported !\n");
197 if (uiop
->uio_resid
== 0)
199 if (uiop
->uio_offset
< 0)
202 if (vp
->v_type
== VDIR
) {
203 lks
= LK_EXCLUSIVE
;/*lockstatus(&vp->v_lock, td);*/
204 if (lks
== LK_SHARED
)
205 vn_lock(vp
, LK_UPGRADE
| LK_RETRY
);
206 error
= smbfs_readvdir(vp
, uiop
, cred
);
207 if (lks
== LK_SHARED
)
208 vn_lock(vp
, LK_DOWNGRADE
| LK_RETRY
);
212 /* biosize = SSTOCN(smp->sm_share)->sc_txmax;*/
213 if (np
->n_flag
& NMODIFIED
) {
214 smbfs_attr_cacheremove(vp
);
215 error
= VOP_GETATTR(vp
, &vattr
);
218 np
->n_mtime
.tv_sec
= vattr
.va_mtime
.tv_sec
;
220 error
= VOP_GETATTR(vp
, &vattr
);
223 if (np
->n_mtime
.tv_sec
!= vattr
.va_mtime
.tv_sec
) {
224 error
= smbfs_vinvalbuf(vp
, V_SAVE
, 1);
227 np
->n_mtime
.tv_sec
= vattr
.va_mtime
.tv_sec
;
230 smb_makescred(&scred
, td
, cred
);
231 return smb_read(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
235 smbfs_writevnode(struct vnode
*vp
, struct uio
*uiop
,
236 struct ucred
*cred
, int ioflag
)
239 struct smbmount
*smp
= VTOSMBFS(vp
);
240 struct smbnode
*np
= VTOSMB(vp
);
241 struct smb_cred scred
;
244 if (vp
->v_type
!= VREG
) {
245 SMBERROR("vn types other than VREG unsupported !\n");
248 SMBVDEBUG("ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
249 if (uiop
->uio_offset
< 0)
252 if (ioflag
& (IO_APPEND
| IO_SYNC
)) {
253 if (np
->n_flag
& NMODIFIED
) {
254 smbfs_attr_cacheremove(vp
);
255 error
= smbfs_vinvalbuf(vp
, V_SAVE
, 1);
259 if (ioflag
& IO_APPEND
) {
262 * File size can be changed by another client
264 smbfs_attr_cacheremove(vp
);
265 error
= VOP_GETATTR(vp
, &vattr
);
266 if (error
) return (error
);
268 uiop
->uio_offset
= np
->n_size
;
271 if (uiop
->uio_resid
== 0)
274 uiop
->uio_offset
+ uiop
->uio_resid
>
275 td
->td_proc
->p_rlimit
[RLIMIT_FSIZE
].rlim_cur
) {
276 lwpsignal(td
->td_proc
, td
->td_lwp
, SIGXFSZ
);
279 smb_makescred(&scred
, td
, cred
);
280 error
= smb_write(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
281 SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
283 if (uiop
->uio_offset
> np
->n_size
) {
284 np
->n_size
= uiop
->uio_offset
;
285 vnode_pager_setsize(vp
, np
->n_size
);
292 * Do an I/O operation to/from a cache block.
295 smbfs_doio(struct vnode
*vp
, struct bio
*bio
, struct ucred
*cr
, struct thread
*td
)
297 struct buf
*bp
= bio
->bio_buf
;
298 struct smbmount
*smp
= VFSTOSMBFS(vp
->v_mount
);
299 struct smbnode
*np
= VTOSMB(vp
);
300 struct uio uio
, *uiop
= &uio
;
302 struct smb_cred scred
;
306 uiop
->uio_iovcnt
= 1;
307 uiop
->uio_segflg
= UIO_SYSSPACE
;
310 smb_makescred(&scred
, td
, cr
);
312 if (bp
->b_cmd
== BUF_CMD_READ
) {
313 io
.iov_len
= uiop
->uio_resid
= (size_t)bp
->b_bcount
;
314 io
.iov_base
= bp
->b_data
;
315 uiop
->uio_rw
= UIO_READ
;
316 switch (vp
->v_type
) {
318 uiop
->uio_offset
= bio
->bio_offset
;
319 error
= smb_read(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
322 if (uiop
->uio_resid
) {
323 size_t left
= uiop
->uio_resid
;
324 size_t nread
= (size_t)bp
->b_bcount
- left
;
326 bzero((char *)bp
->b_data
+ nread
, left
);
330 kprintf("smbfs_doio: type %x unexpected\n",vp
->v_type
);
335 bp
->b_flags
|= B_ERROR
;
338 KKASSERT(bp
->b_cmd
== BUF_CMD_WRITE
);
339 if (bio
->bio_offset
+ bp
->b_dirtyend
> np
->n_size
)
340 bp
->b_dirtyend
= np
->n_size
- bio
->bio_offset
;
342 if (bp
->b_dirtyend
> bp
->b_dirtyoff
) {
343 io
.iov_len
= uiop
->uio_resid
=
344 (size_t)(bp
->b_dirtyend
- bp
->b_dirtyoff
);
345 uiop
->uio_offset
= bio
->bio_offset
+ bp
->b_dirtyoff
;
346 io
.iov_base
= (char *)bp
->b_data
+ bp
->b_dirtyoff
;
347 uiop
->uio_rw
= UIO_WRITE
;
348 error
= smb_write(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
351 * For an interrupted write, the buffer is still valid
352 * and the write hasn't been pushed to the server yet,
353 * so we can't set BIO_ERROR and report the interruption
354 * by setting B_EINTR. For the async case, B_EINTR
355 * is not relevant, so the rpc attempt is essentially
356 * a noop. For the case of a V3 write rpc not being
357 * committed to stable storage, the block is still
358 * dirty and requires either a commit rpc or another
359 * write rpc with iomode == NFSV3WRITE_FILESYNC before
360 * the block is reused. This is indicated by setting
361 * the B_DELWRI and B_NEEDCOMMIT flags.
364 || (!error
&& (bp
->b_flags
& B_NEEDCOMMIT
))) {
367 bp
->b_flags
&= ~(B_INVAL
|B_NOCACHE
);
368 if ((bp
->b_flags
& B_PAGING
) == 0)
370 bp
->b_flags
|= B_EINTR
;
374 bp
->b_flags
|= B_ERROR
;
377 bp
->b_dirtyoff
= bp
->b_dirtyend
= 0;
385 bp
->b_resid
= uiop
->uio_resid
;
391 * Vnode op for VM getpages.
392 * Wish wish .... get rid from multiple IO routines
394 * smbfs_getpages(struct vnode *a_vp, vm_page_t *a_m, int a_count,
395 * int a_reqpage, vm_ooffset_t a_offset)
398 smbfs_getpages(struct vop_getpages_args
*ap
)
400 #ifdef SMBFS_RWGENERIC
401 return vop_stdgetpages(ap
);
403 int i
, error
, npages
;
405 size_t size
, toff
, nextoff
, count
;
411 struct thread
*td
= curthread
; /* XXX */
413 struct smbmount
*smp
;
415 struct smb_cred scred
;
418 KKASSERT(td
->td_proc
);
421 cred
= td
->td_proc
->p_ucred
;
423 smp
= VFSTOSMBFS(vp
->v_mount
);
425 count
= (size_t)ap
->a_count
;
427 if (vp
->v_object
== NULL
) {
428 kprintf("smbfs_getpages: called with non-merged cache vnode??\n");
429 return VM_PAGER_ERROR
;
431 smb_makescred(&scred
, td
, cred
);
433 bp
= getpbuf(&smbfs_pbuf_freecnt
);
434 npages
= btoc(count
);
435 kva
= (vm_offset_t
) bp
->b_data
;
436 pmap_qenter(kva
, pages
, npages
);
438 iov
.iov_base
= (caddr_t
) kva
;
442 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
443 uio
.uio_resid
= count
;
444 uio
.uio_segflg
= UIO_SYSSPACE
;
445 uio
.uio_rw
= UIO_READ
;
449 * This is kinda nasty. Since smbfs is physically closing the
450 * fid on close(), we have to reopen it if necessary. There are
451 * other races here too, such as if another process opens the same
452 * file while we are blocked in read. XXX
456 if (np
->n_opencount
== 0) {
457 error
= smbfs_smb_open(np
, SMB_AM_OPENREAD
, &scred
);
462 error
= smb_read(smp
->sm_share
, np
->n_fid
, &uio
, &scred
);
464 smbfs_smb_close(smp
->sm_share
, np
->n_fid
, NULL
, &scred
);
465 pmap_qremove(kva
, npages
);
467 relpbuf(bp
, &smbfs_pbuf_freecnt
);
469 if (error
&& (uio
.uio_resid
== count
)) {
470 kprintf("smbfs_getpages: error %d\n",error
);
471 for (i
= 0; i
< npages
; i
++) {
472 if (ap
->a_reqpage
!= i
)
473 vnode_pager_freepage(pages
[i
]);
475 return VM_PAGER_ERROR
;
478 size
= count
- uio
.uio_resid
;
480 for (i
= 0, toff
= 0; i
< npages
; i
++, toff
= nextoff
) {
482 nextoff
= toff
+ PAGE_SIZE
;
485 m
->flags
&= ~PG_ZERO
;
488 * NOTE: pmap dirty bit should have already been cleared.
489 * We do not clear it here.
491 if (nextoff
<= size
) {
492 m
->valid
= VM_PAGE_BITS_ALL
;
495 int nvalid
= ((size
+ DEV_BSIZE
- 1) - toff
) &
497 vm_page_set_validclean(m
, 0, nvalid
);
500 if (i
!= ap
->a_reqpage
) {
502 * Whether or not to leave the page activated is up in
503 * the air, but we should put the page on a page queue
504 * somewhere (it already is in the object). Result:
505 * It appears that emperical results show that
506 * deactivating pages is best.
510 * Just in case someone was asking for this page we
511 * now tell them that it is ok to use.
514 if (m
->flags
& PG_WANTED
)
517 vm_page_deactivate(m
);
520 vnode_pager_freepage(m
);
525 #endif /* SMBFS_RWGENERIC */
529 * Vnode op for VM putpages.
530 * possible bug: all IO done in sync mode
531 * Note that vop_close always invalidate pages before close, so it's
532 * not necessary to open vnode.
534 * smbfs_putpages(struct vnode *a_vp, vm_page_t *a_m, int a_count, int a_sync,
535 * int *a_rtvals, vm_ooffset_t a_offset)
538 smbfs_putpages(struct vop_putpages_args
*ap
)
541 struct vnode
*vp
= ap
->a_vp
;
542 struct thread
*td
= curthread
; /* XXX */
545 #ifdef SMBFS_RWGENERIC
546 KKASSERT(td
->td_proc
);
547 cred
= td
->td_proc
->p_ucred
;
548 VOP_OPEN(vp
, FWRITE
, cred
, NULL
);
549 error
= vop_stdputpages(ap
);
550 VOP_CLOSE(vp
, FWRITE
, cred
);
557 int i
, npages
, count
;
560 struct smbmount
*smp
;
562 struct smb_cred scred
;
565 KKASSERT(td
->td_proc
);
566 cred
= td
->td_proc
->p_ucred
;
567 /* VOP_OPEN(vp, FWRITE, cred, NULL);*/
569 smp
= VFSTOSMBFS(vp
->v_mount
);
572 rtvals
= ap
->a_rtvals
;
573 npages
= btoc(count
);
575 for (i
= 0; i
< npages
; i
++) {
576 rtvals
[i
] = VM_PAGER_AGAIN
;
579 bp
= getpbuf(&smbfs_pbuf_freecnt
);
580 kva
= (vm_offset_t
) bp
->b_data
;
581 pmap_qenter(kva
, pages
, npages
);
583 iov
.iov_base
= (caddr_t
) kva
;
587 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
588 uio
.uio_resid
= count
;
589 uio
.uio_segflg
= UIO_SYSSPACE
;
590 uio
.uio_rw
= UIO_WRITE
;
592 SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio
.uio_offset
, uio
.uio_resid
);
594 smb_makescred(&scred
, td
, cred
);
597 * This is kinda nasty. Since smbfs is physically closing the
598 * fid on close(), we have to reopen it if necessary. There are
599 * other races here too, such as if another process opens the same
600 * file while we are blocked in read, or the file is open read-only
605 if (np
->n_opencount
== 0) {
606 error
= smbfs_smb_open(np
, SMB_AM_OPENRW
, &scred
);
611 error
= smb_write(smp
->sm_share
, np
->n_fid
, &uio
, &scred
);
613 smbfs_smb_close(smp
->sm_share
, np
->n_fid
, NULL
, &scred
);
614 /* VOP_CLOSE(vp, FWRITE, cred);*/
615 SMBVDEBUG("paged write done: %d\n", error
);
617 pmap_qremove(kva
, npages
);
618 relpbuf(bp
, &smbfs_pbuf_freecnt
);
621 int nwritten
= round_page(count
- uio
.uio_resid
) / PAGE_SIZE
;
622 for (i
= 0; i
< nwritten
; i
++) {
623 rtvals
[i
] = VM_PAGER_OK
;
624 vm_page_undirty(pages
[i
]);
628 #endif /* SMBFS_RWGENERIC */
632 * Flush and invalidate all dirty buffers. If another process is already
633 * doing the flush, just wait for completion.
636 smbfs_vinvalbuf(struct vnode
*vp
, int flags
, int intrflg
)
638 struct smbnode
*np
= VTOSMB(vp
);
639 int error
= 0, slpflag
, slptimeo
;
641 if (vp
->v_flag
& VRECLAIMED
)
650 while (np
->n_flag
& NFLUSHINPROG
) {
651 np
->n_flag
|= NFLUSHWANT
;
652 error
= tsleep((caddr_t
)&np
->n_flag
, 0, "smfsvinv", slptimeo
);
653 error
= smb_proc_intr(curthread
);
654 if (error
== EINTR
&& intrflg
)
657 np
->n_flag
|= NFLUSHINPROG
;
658 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
660 if (intrflg
&& (error
== ERESTART
|| error
== EINTR
)) {
661 np
->n_flag
&= ~NFLUSHINPROG
;
662 if (np
->n_flag
& NFLUSHWANT
) {
663 np
->n_flag
&= ~NFLUSHWANT
;
664 wakeup((caddr_t
)&np
->n_flag
);
668 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
670 np
->n_flag
&= ~(NMODIFIED
| NFLUSHINPROG
);
671 if (np
->n_flag
& NFLUSHWANT
) {
672 np
->n_flag
&= ~NFLUSHWANT
;
673 wakeup((caddr_t
)&np
->n_flag
);