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
;
487 if (nextoff
<= size
) {
488 m
->valid
= VM_PAGE_BITS_ALL
;
491 int nvalid
= ((size
+ DEV_BSIZE
- 1) - toff
) & ~(DEV_BSIZE
- 1);
492 vm_page_set_validclean(m
, 0, nvalid
);
495 if (i
!= ap
->a_reqpage
) {
497 * Whether or not to leave the page activated is up in
498 * the air, but we should put the page on a page queue
499 * somewhere (it already is in the object). Result:
500 * It appears that emperical results show that
501 * deactivating pages is best.
505 * Just in case someone was asking for this page we
506 * now tell them that it is ok to use.
509 if (m
->flags
& PG_WANTED
)
512 vm_page_deactivate(m
);
515 vnode_pager_freepage(m
);
520 #endif /* SMBFS_RWGENERIC */
524 * Vnode op for VM putpages.
525 * possible bug: all IO done in sync mode
526 * Note that vop_close always invalidate pages before close, so it's
527 * not necessary to open vnode.
529 * smbfs_putpages(struct vnode *a_vp, vm_page_t *a_m, int a_count, int a_sync,
530 * int *a_rtvals, vm_ooffset_t a_offset)
533 smbfs_putpages(struct vop_putpages_args
*ap
)
536 struct vnode
*vp
= ap
->a_vp
;
537 struct thread
*td
= curthread
; /* XXX */
540 #ifdef SMBFS_RWGENERIC
541 KKASSERT(td
->td_proc
);
542 cred
= td
->td_proc
->p_ucred
;
543 VOP_OPEN(vp
, FWRITE
, cred
, NULL
);
544 error
= vop_stdputpages(ap
);
545 VOP_CLOSE(vp
, FWRITE
, cred
);
552 int i
, npages
, count
;
555 struct smbmount
*smp
;
557 struct smb_cred scred
;
560 KKASSERT(td
->td_proc
);
561 cred
= td
->td_proc
->p_ucred
;
562 /* VOP_OPEN(vp, FWRITE, cred, NULL);*/
564 smp
= VFSTOSMBFS(vp
->v_mount
);
567 rtvals
= ap
->a_rtvals
;
568 npages
= btoc(count
);
570 for (i
= 0; i
< npages
; i
++) {
571 rtvals
[i
] = VM_PAGER_AGAIN
;
574 bp
= getpbuf(&smbfs_pbuf_freecnt
);
575 kva
= (vm_offset_t
) bp
->b_data
;
576 pmap_qenter(kva
, pages
, npages
);
578 iov
.iov_base
= (caddr_t
) kva
;
582 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
583 uio
.uio_resid
= count
;
584 uio
.uio_segflg
= UIO_SYSSPACE
;
585 uio
.uio_rw
= UIO_WRITE
;
587 SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio
.uio_offset
, uio
.uio_resid
);
589 smb_makescred(&scred
, td
, cred
);
592 * This is kinda nasty. Since smbfs is physically closing the
593 * fid on close(), we have to reopen it if necessary. There are
594 * other races here too, such as if another process opens the same
595 * file while we are blocked in read, or the file is open read-only
600 if (np
->n_opencount
== 0) {
601 error
= smbfs_smb_open(np
, SMB_AM_OPENRW
, &scred
);
606 error
= smb_write(smp
->sm_share
, np
->n_fid
, &uio
, &scred
);
608 smbfs_smb_close(smp
->sm_share
, np
->n_fid
, NULL
, &scred
);
609 /* VOP_CLOSE(vp, FWRITE, cred);*/
610 SMBVDEBUG("paged write done: %d\n", error
);
612 pmap_qremove(kva
, npages
);
613 relpbuf(bp
, &smbfs_pbuf_freecnt
);
616 int nwritten
= round_page(count
- uio
.uio_resid
) / PAGE_SIZE
;
617 for (i
= 0; i
< nwritten
; i
++) {
618 rtvals
[i
] = VM_PAGER_OK
;
623 #endif /* SMBFS_RWGENERIC */
627 * Flush and invalidate all dirty buffers. If another process is already
628 * doing the flush, just wait for completion.
631 smbfs_vinvalbuf(struct vnode
*vp
, int flags
, int intrflg
)
633 struct smbnode
*np
= VTOSMB(vp
);
634 int error
= 0, slpflag
, slptimeo
;
636 if (vp
->v_flag
& VRECLAIMED
)
645 while (np
->n_flag
& NFLUSHINPROG
) {
646 np
->n_flag
|= NFLUSHWANT
;
647 error
= tsleep((caddr_t
)&np
->n_flag
, 0, "smfsvinv", slptimeo
);
648 error
= smb_proc_intr(curthread
);
649 if (error
== EINTR
&& intrflg
)
652 np
->n_flag
|= NFLUSHINPROG
;
653 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
655 if (intrflg
&& (error
== ERESTART
|| error
== EINTR
)) {
656 np
->n_flag
&= ~NFLUSHINPROG
;
657 if (np
->n_flag
& NFLUSHWANT
) {
658 np
->n_flag
&= ~NFLUSHWANT
;
659 wakeup((caddr_t
)&np
->n_flag
);
663 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
665 np
->n_flag
&= ~(NMODIFIED
| NFLUSHINPROG
);
666 if (np
->n_flag
& NFLUSHWANT
) {
667 np
->n_flag
&= ~NFLUSHWANT
;
668 wakeup((caddr_t
)&np
->n_flag
);