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 <sys/ioccom.h>
60 #include <netproto/smb/smb.h>
61 #include <netproto/smb/smb_conn.h>
62 #include <netproto/smb/smb_subr.h>
65 #include "smbfs_node.h"
66 #include "smbfs_subr.h"
70 #include <sys/thread2.h>
72 /*#define SMBFS_RWGENERIC*/
74 extern int smbfs_pbuf_freecnt
;
76 static int smbfs_fastlookup
= 1;
78 SYSCTL_DECL(_vfs_smbfs
);
79 SYSCTL_INT(_vfs_smbfs
, OID_AUTO
, fastlookup
, CTLFLAG_RW
, &smbfs_fastlookup
, 0, "");
82 smbfs_readvdir(struct vnode
*vp
, struct uio
*uio
, struct ucred
*cred
)
84 struct smb_cred scred
;
85 struct smbfs_fctx
*ctx
;
87 struct smbnode
*np
= VTOSMB(vp
);
88 int error
, offset
, retval
/*, *eofflag = ap->a_eofflag*/;
91 SMBVDEBUG("dirname='%s'\n", np
->n_name
);
92 smb_makescred(&scred
, uio
->uio_td
, cred
);
94 if (uio
->uio_resid
< 0 || uio
->uio_offset
< 0 ||
95 uio
->uio_offset
> INT_MAX
)
99 offset
= uio
->uio_offset
;
101 if (uio
->uio_resid
> 0 && offset
< 1) {
102 if (vop_write_dirent(&error
, uio
, np
->n_ino
, DT_DIR
, 1, "."))
109 if (uio
->uio_resid
> 0 && offset
< 2) {
110 if (vop_write_dirent(&error
, uio
,
111 np
->n_parent
? VTOSMB(np
->n_parent
)->n_ino
: 2,
119 if (uio
->uio_resid
== 0)
122 if (offset
!= np
->n_dirofs
|| np
->n_dirseq
== NULL
) {
123 SMBVDEBUG("Reopening search %ld:%ld\n", offset
, np
->n_dirofs
);
125 smbfs_findclose(np
->n_dirseq
, &scred
);
129 error
= smbfs_findopen(np
, "*", 1,
130 SMB_FA_SYSTEM
| SMB_FA_HIDDEN
| SMB_FA_DIR
,
133 SMBVDEBUG("can not open search, error = %d", error
);
140 while (np
->n_dirofs
< offset
) {
141 error
= smbfs_findnext(ctx
, offset
- np
->n_dirofs
, &scred
);
144 smbfs_findclose(np
->n_dirseq
, &scred
);
146 return error
== ENOENT
? 0 : error
;
150 while (uio
->uio_resid
> 0 && !error
) {
152 * Overestimate the size of a record a bit, doesn't really
153 * hurt to be wrong here.
155 error
= smbfs_findnext(ctx
, uio
->uio_resid
/ _DIRENT_RECLEN(255) + 1, &scred
);
161 retval
= vop_write_dirent(&error
, uio
, ctx
->f_attr
.fa_ino
,
162 (ctx
->f_attr
.fa_attr
& SMB_FA_DIR
) ? DT_DIR
: DT_REG
,
163 ctx
->f_nmlen
, ctx
->f_name
);
166 if (smbfs_fastlookup
&& !error
) {
167 error
= smbfs_nget(vp
->v_mount
, vp
, ctx
->f_name
,
168 ctx
->f_nmlen
, &ctx
->f_attr
, &newvp
);
176 uio
->uio_offset
= offset
;
181 smbfs_readvnode(struct vnode
*vp
, struct uio
*uiop
, struct ucred
*cred
)
184 struct smbmount
*smp
= VFSTOSMBFS(vp
->v_mount
);
185 struct smbnode
*np
= VTOSMB(vp
);
187 struct smb_cred scred
;
191 * Protect against method which is not supported for now
193 if (uiop
->uio_segflg
== UIO_NOCOPY
)
196 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
) {
197 SMBFSERR("vn types other than VREG or VDIR are unsupported !\n");
200 if (uiop
->uio_resid
== 0)
202 if (uiop
->uio_offset
< 0)
204 /* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
207 if (vp
->v_type
== VDIR
) {
208 lks
= LK_EXCLUSIVE
;/*lockstatus(&vp->v_lock, td);*/
209 if (lks
== LK_SHARED
)
210 vn_lock(vp
, LK_UPGRADE
| LK_RETRY
);
211 error
= smbfs_readvdir(vp
, uiop
, cred
);
212 if (lks
== LK_SHARED
)
213 vn_lock(vp
, LK_DOWNGRADE
| LK_RETRY
);
217 /* biosize = SSTOCN(smp->sm_share)->sc_txmax;*/
218 if (np
->n_flag
& NMODIFIED
) {
219 smbfs_attr_cacheremove(vp
);
220 error
= VOP_GETATTR(vp
, &vattr
);
223 np
->n_mtime
.tv_sec
= vattr
.va_mtime
.tv_sec
;
225 error
= VOP_GETATTR(vp
, &vattr
);
228 if (np
->n_mtime
.tv_sec
!= vattr
.va_mtime
.tv_sec
) {
229 error
= smbfs_vinvalbuf(vp
, V_SAVE
, 1);
232 np
->n_mtime
.tv_sec
= vattr
.va_mtime
.tv_sec
;
235 smb_makescred(&scred
, td
, cred
);
236 return smb_read(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
240 smbfs_writevnode(struct vnode
*vp
, struct uio
*uiop
,
241 struct ucred
*cred
, int ioflag
)
244 struct smbmount
*smp
= VTOSMBFS(vp
);
245 struct smbnode
*np
= VTOSMB(vp
);
246 struct smb_cred scred
;
249 if (vp
->v_type
!= VREG
) {
250 SMBERROR("vn types other than VREG unsupported !\n");
253 SMBVDEBUG("ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
254 if (uiop
->uio_offset
< 0)
256 /* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize)
259 if (ioflag
& (IO_APPEND
| IO_SYNC
)) {
260 if (np
->n_flag
& NMODIFIED
) {
261 smbfs_attr_cacheremove(vp
);
262 error
= smbfs_vinvalbuf(vp
, V_SAVE
, 1);
266 if (ioflag
& IO_APPEND
) {
269 * File size can be changed by another client
271 smbfs_attr_cacheremove(vp
);
272 error
= VOP_GETATTR(vp
, &vattr
);
273 if (error
) return (error
);
275 uiop
->uio_offset
= np
->n_size
;
278 if (uiop
->uio_resid
== 0)
281 uiop
->uio_offset
+ uiop
->uio_resid
>
282 td
->td_proc
->p_rlimit
[RLIMIT_FSIZE
].rlim_cur
) {
283 lwpsignal(td
->td_proc
, td
->td_lwp
, SIGXFSZ
);
286 smb_makescred(&scred
, td
, cred
);
287 error
= smb_write(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
288 SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop
->uio_offset
, uiop
->uio_resid
);
290 if (uiop
->uio_offset
> np
->n_size
) {
291 np
->n_size
= uiop
->uio_offset
;
292 vnode_pager_setsize(vp
, np
->n_size
);
299 * Do an I/O operation to/from a cache block.
302 smbfs_doio(struct vnode
*vp
, struct bio
*bio
, struct ucred
*cr
, struct thread
*td
)
304 struct buf
*bp
= bio
->bio_buf
;
305 struct smbmount
*smp
= VFSTOSMBFS(vp
->v_mount
);
306 struct smbnode
*np
= VTOSMB(vp
);
307 struct uio uio
, *uiop
= &uio
;
309 struct smb_cred scred
;
313 uiop
->uio_iovcnt
= 1;
314 uiop
->uio_segflg
= UIO_SYSSPACE
;
317 smb_makescred(&scred
, td
, cr
);
319 if (bp
->b_cmd
== BUF_CMD_READ
) {
320 io
.iov_len
= uiop
->uio_resid
= bp
->b_bcount
;
321 io
.iov_base
= bp
->b_data
;
322 uiop
->uio_rw
= UIO_READ
;
323 switch (vp
->v_type
) {
325 uiop
->uio_offset
= bio
->bio_offset
;
326 error
= smb_read(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
329 if (uiop
->uio_resid
) {
330 int left
= uiop
->uio_resid
;
331 int nread
= bp
->b_bcount
- left
;
333 bzero((char *)bp
->b_data
+ nread
, left
);
337 kprintf("smbfs_doio: type %x unexpected\n",vp
->v_type
);
342 bp
->b_flags
|= B_ERROR
;
345 KKASSERT(bp
->b_cmd
== BUF_CMD_WRITE
);
346 if (bio
->bio_offset
+ bp
->b_dirtyend
> np
->n_size
)
347 bp
->b_dirtyend
= np
->n_size
- bio
->bio_offset
;
349 if (bp
->b_dirtyend
> bp
->b_dirtyoff
) {
350 io
.iov_len
= uiop
->uio_resid
= bp
->b_dirtyend
- bp
->b_dirtyoff
;
351 uiop
->uio_offset
= bio
->bio_offset
+ bp
->b_dirtyoff
;
352 io
.iov_base
= (char *)bp
->b_data
+ bp
->b_dirtyoff
;
353 uiop
->uio_rw
= UIO_WRITE
;
354 error
= smb_write(smp
->sm_share
, np
->n_fid
, uiop
, &scred
);
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.
370 || (!error
&& (bp
->b_flags
& B_NEEDCOMMIT
))) {
373 bp
->b_flags
&= ~(B_INVAL
|B_NOCACHE
);
374 if ((bp
->b_flags
& B_ASYNC
) == 0)
375 bp
->b_flags
|= B_EINTR
;
376 if ((bp
->b_flags
& B_PAGING
) == 0)
378 if ((bp
->b_flags
& B_ASYNC
) == 0)
379 bp
->b_flags
|= B_EINTR
;
383 bp
->b_flags
|= B_ERROR
;
386 bp
->b_dirtyoff
= bp
->b_dirtyend
= 0;
394 bp
->b_resid
= uiop
->uio_resid
;
400 * Vnode op for VM getpages.
401 * Wish wish .... get rid from multiple IO routines
403 * smbfs_getpages(struct vnode *a_vp, vm_page_t *a_m, int a_count,
404 * int a_reqpage, vm_ooffset_t a_offset)
407 smbfs_getpages(struct vop_getpages_args
*ap
)
409 #ifdef SMBFS_RWGENERIC
410 return vop_stdgetpages(ap
);
412 int i
, error
, nextoff
, size
, toff
, npages
, count
;
419 struct thread
*td
= curthread
; /* XXX */
421 struct smbmount
*smp
;
423 struct smb_cred scred
;
426 KKASSERT(td
->td_proc
);
429 cred
= td
->td_proc
->p_ucred
;
431 smp
= VFSTOSMBFS(vp
->v_mount
);
435 if (vp
->v_object
== NULL
) {
436 kprintf("smbfs_getpages: called with non-merged cache vnode??\n");
437 return VM_PAGER_ERROR
;
439 smb_makescred(&scred
, td
, cred
);
441 bp
= getpbuf(&smbfs_pbuf_freecnt
);
442 npages
= btoc(count
);
443 kva
= (vm_offset_t
) bp
->b_data
;
444 pmap_qenter(kva
, pages
, npages
);
446 iov
.iov_base
= (caddr_t
) kva
;
450 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
451 uio
.uio_resid
= count
;
452 uio
.uio_segflg
= UIO_SYSSPACE
;
453 uio
.uio_rw
= UIO_READ
;
457 * This is kinda nasty. Since smbfs is physically closing the
458 * fid on close(), we have to reopen it if necessary. There are
459 * other races here too, such as if another process opens the same
460 * file while we are blocked in read. XXX
464 if (np
->n_opencount
== 0) {
465 error
= smbfs_smb_open(np
, SMB_AM_OPENREAD
, &scred
);
470 error
= smb_read(smp
->sm_share
, np
->n_fid
, &uio
, &scred
);
472 smbfs_smb_close(smp
->sm_share
, np
->n_fid
, NULL
, &scred
);
473 pmap_qremove(kva
, npages
);
475 relpbuf(bp
, &smbfs_pbuf_freecnt
);
477 if (error
&& (uio
.uio_resid
== count
)) {
478 kprintf("smbfs_getpages: error %d\n",error
);
479 for (i
= 0; i
< npages
; i
++) {
480 if (ap
->a_reqpage
!= i
)
481 vnode_pager_freepage(pages
[i
]);
483 return VM_PAGER_ERROR
;
486 size
= count
- uio
.uio_resid
;
488 for (i
= 0, toff
= 0; i
< npages
; i
++, toff
= nextoff
) {
490 nextoff
= toff
+ PAGE_SIZE
;
493 m
->flags
&= ~PG_ZERO
;
495 if (nextoff
<= size
) {
496 m
->valid
= VM_PAGE_BITS_ALL
;
499 int nvalid
= ((size
+ DEV_BSIZE
- 1) - toff
) & ~(DEV_BSIZE
- 1);
500 vm_page_set_validclean(m
, 0, nvalid
);
503 if (i
!= ap
->a_reqpage
) {
505 * Whether or not to leave the page activated is up in
506 * the air, but we should put the page on a page queue
507 * somewhere (it already is in the object). Result:
508 * It appears that emperical results show that
509 * deactivating pages is best.
513 * Just in case someone was asking for this page we
514 * now tell them that it is ok to use.
517 if (m
->flags
& PG_WANTED
)
520 vm_page_deactivate(m
);
523 vnode_pager_freepage(m
);
528 #endif /* SMBFS_RWGENERIC */
532 * Vnode op for VM putpages.
533 * possible bug: all IO done in sync mode
534 * Note that vop_close always invalidate pages before close, so it's
535 * not necessary to open vnode.
537 * smbfs_putpages(struct vnode *a_vp, vm_page_t *a_m, int a_count, int a_sync,
538 * int *a_rtvals, vm_ooffset_t a_offset)
541 smbfs_putpages(struct vop_putpages_args
*ap
)
544 struct vnode
*vp
= ap
->a_vp
;
545 struct thread
*td
= curthread
; /* XXX */
548 #ifdef SMBFS_RWGENERIC
549 KKASSERT(td
->td_proc
);
550 cred
= td
->td_proc
->p_ucred
;
551 VOP_OPEN(vp
, FWRITE
, cred
, NULL
);
552 error
= vop_stdputpages(ap
);
553 VOP_CLOSE(vp
, FWRITE
, cred
);
560 int i
, npages
, count
;
563 struct smbmount
*smp
;
565 struct smb_cred scred
;
568 KKASSERT(td
->td_proc
);
569 cred
= td
->td_proc
->p_ucred
;
570 /* VOP_OPEN(vp, FWRITE, cred, NULL);*/
572 smp
= VFSTOSMBFS(vp
->v_mount
);
575 rtvals
= ap
->a_rtvals
;
576 npages
= btoc(count
);
578 for (i
= 0; i
< npages
; i
++) {
579 rtvals
[i
] = VM_PAGER_AGAIN
;
582 bp
= getpbuf(&smbfs_pbuf_freecnt
);
583 kva
= (vm_offset_t
) bp
->b_data
;
584 pmap_qenter(kva
, pages
, npages
);
586 iov
.iov_base
= (caddr_t
) kva
;
590 uio
.uio_offset
= IDX_TO_OFF(pages
[0]->pindex
);
591 uio
.uio_resid
= count
;
592 uio
.uio_segflg
= UIO_SYSSPACE
;
593 uio
.uio_rw
= UIO_WRITE
;
595 SMBVDEBUG("ofs=%d,resid=%d\n",(int)uio
.uio_offset
, uio
.uio_resid
);
597 smb_makescred(&scred
, td
, cred
);
600 * This is kinda nasty. Since smbfs is physically closing the
601 * fid on close(), we have to reopen it if necessary. There are
602 * other races here too, such as if another process opens the same
603 * file while we are blocked in read, or the file is open read-only
608 if (np
->n_opencount
== 0) {
609 error
= smbfs_smb_open(np
, SMB_AM_OPENRW
, &scred
);
614 error
= smb_write(smp
->sm_share
, np
->n_fid
, &uio
, &scred
);
616 smbfs_smb_close(smp
->sm_share
, np
->n_fid
, NULL
, &scred
);
617 /* VOP_CLOSE(vp, FWRITE, cred);*/
618 SMBVDEBUG("paged write done: %d\n", error
);
620 pmap_qremove(kva
, npages
);
621 relpbuf(bp
, &smbfs_pbuf_freecnt
);
624 int nwritten
= round_page(count
- uio
.uio_resid
) / PAGE_SIZE
;
625 for (i
= 0; i
< nwritten
; i
++) {
626 rtvals
[i
] = VM_PAGER_OK
;
631 #endif /* SMBFS_RWGENERIC */
635 * Flush and invalidate all dirty buffers. If another process is already
636 * doing the flush, just wait for completion.
639 smbfs_vinvalbuf(struct vnode
*vp
, int flags
, int intrflg
)
641 struct smbnode
*np
= VTOSMB(vp
);
642 int error
= 0, slpflag
, slptimeo
;
644 if (vp
->v_flag
& VRECLAIMED
)
653 while (np
->n_flag
& NFLUSHINPROG
) {
654 np
->n_flag
|= NFLUSHWANT
;
655 error
= tsleep((caddr_t
)&np
->n_flag
, 0, "smfsvinv", slptimeo
);
656 error
= smb_proc_intr(curthread
);
657 if (error
== EINTR
&& intrflg
)
660 np
->n_flag
|= NFLUSHINPROG
;
661 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
663 if (intrflg
&& (error
== ERESTART
|| error
== EINTR
)) {
664 np
->n_flag
&= ~NFLUSHINPROG
;
665 if (np
->n_flag
& NFLUSHWANT
) {
666 np
->n_flag
&= ~NFLUSHWANT
;
667 wakeup((caddr_t
)&np
->n_flag
);
671 error
= vinvalbuf(vp
, flags
, slpflag
, 0);
673 np
->n_flag
&= ~(NMODIFIED
| NFLUSHINPROG
);
674 if (np
->n_flag
& NFLUSHWANT
) {
675 np
->n_flag
&= ~NFLUSHWANT
;
676 wakeup((caddr_t
)&np
->n_flag
);