]>
git.saurik.com Git - apple/xnu.git/blob - bsd/ufs/ufs/ufs_readwrite.c
5afd4a8a0da5772b8eb81da10a862b8c93dbdd73
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
30 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
33 * The Regents of the University of California. All rights reserved.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95
66 #include <sys/buf_internal.h>
67 #include <sys/uio_internal.h>
70 #define BLKSIZE(a, b, c) blksize(a, b, c)
77 * Vnode op for reading.
81 struct vnop_read_args
/* {
85 vfs_context_t a_context;
88 return(ffs_read_internal(ap
->a_vp
, ap
->a_uio
, ap
->a_ioflag
));
93 ffs_read_internal(vnode_t vp
, struct uio
*uio
, int ioflag
)
97 buf_t bp
= (struct buf
*)0;
98 ufs_daddr_t lbn
, nextlbn
;
100 long size
, xfersize
, blkoffset
;
105 #endif /* REV_ENDIAN_FS */
111 rev_endian
=(vp
->v_mount
->mnt_flag
& MNT_REVEND
);
112 #endif /* REV_ENDIAN_FS */
115 if (uio
->uio_rw
!= UIO_READ
)
116 panic("ffs_read: invalid uio_rw = %x", uio
->uio_rw
);
118 if (vp
->v_type
== VLNK
) {
119 if ((int)ip
->i_size
< vp
->v_mount
->mnt_maxsymlinklen
)
120 panic("ffs_read: short symlink = %d", ip
->i_size
);
121 } else if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
)
122 panic("ffs_read: invalid v_type = %x", vp
->v_type
);
125 if (uio
->uio_offset
< 0)
127 if (uio
->uio_offset
> fs
->fs_maxfilesize
)
130 if (UBCINFOEXISTS(vp
)) {
131 error
= cluster_read(vp
, uio
, (off_t
)ip
->i_size
, 0);
133 for (error
= 0, bp
= NULL
; uio_resid(uio
) > 0;
137 if ((bytesinfile
= ip
->i_size
- uio
->uio_offset
) <= 0)
139 lbn
= lblkno(fs
, uio
->uio_offset
);
141 size
= BLKSIZE(fs
, ip
, lbn
);
142 blkoffset
= blkoff(fs
, uio
->uio_offset
);
143 xfersize
= fs
->fs_bsize
- blkoffset
;
144 // LP64todo - fix this
145 if (uio_resid(uio
) < xfersize
)
146 xfersize
= uio_resid(uio
);
147 if (bytesinfile
< xfersize
)
148 xfersize
= bytesinfile
;
150 if (lblktosize(fs
, nextlbn
) >= ip
->i_size
)
151 error
= (int)buf_bread(vp
, (daddr64_t
)((unsigned)lbn
), size
, NOCRED
, &bp
);
152 else if (lbn
- 1 == ip
->i_lastr
&& !(vp
->v_flag
& VRAOFF
)) {
153 int nextsize
= BLKSIZE(fs
, ip
, nextlbn
);
154 error
= (int)buf_breadn(vp
, (daddr64_t
)((unsigned)lbn
),
155 size
, &nextlbn
, &nextsize
, 1, NOCRED
, &bp
);
157 error
= (int)buf_bread(vp
, lbn
, size
, NOCRED
, &bp
);
163 * We should only get non-zero buffer resid when an I/O error
164 * has occurred, which should cause us to break above.
165 * However, if the short read did not cause an error,
166 * then we want to ensure that we do not uiomove bad
167 * or uninitialized data.
169 size
-= buf_resid(bp
);
170 if (size
< xfersize
) {
175 buf_data
= (char *)buf_dataptr(bp
);
177 if (rev_endian
&& S_ISDIR(mode
)) {
178 byte_swap_dir_block_in(buf_data
+ blkoffset
, xfersize
);
180 #endif /* REV_ENDIAN_FS */
182 uiomove(buf_data
+ blkoffset
, (int)xfersize
, uio
)) {
184 if (rev_endian
&& S_ISDIR(mode
)) {
185 byte_swap_dir_block_in(buf_data
+ blkoffset
, xfersize
);
187 #endif /* REV_ENDIAN_FS */
192 if (rev_endian
&& S_ISDIR(mode
)) {
193 byte_swap_dir_out(buf_data
+ blkoffset
, xfersize
);
195 #endif /* REV_ENDIAN_FS */
196 if (S_ISREG(mode
) && (xfersize
+ blkoffset
== fs
->fs_bsize
||
197 uio
->uio_offset
== ip
->i_size
))
204 ip
->i_flag
|= IN_ACCESS
;
209 * Vnode op for writing.
212 struct vnop_write_args
/* {
216 vfs_context_t a_context;
219 return(ffs_write_internal(ap
->a_vp
, ap
->a_uio
, ap
->a_ioflag
, vfs_context_ucred(ap
->a_context
)));
223 ffs_write_internal(vnode_t vp
, struct uio
*uio
, int ioflag
, ucred_t cred
)
231 int blkoffset
, flags
, resid
, rsd
, size
, xfersize
;
232 int save_error
=0, save_size
=0;
235 int file_extended
= 0;
236 int doingdirectory
= 0;
240 #endif /* REV_ENDIAN_FS */
244 rev_endian
=(vp
->v_mount
->mnt_flag
& MNT_REVEND
);
245 #endif /* REV_ENDIAN_FS */
248 if (uio
->uio_rw
!= UIO_WRITE
)
249 panic("ffs_write: uio_rw = %x\n", uio
->uio_rw
);
252 switch (vp
->v_type
) {
254 if (ioflag
& IO_APPEND
)
255 uio
->uio_offset
= ip
->i_size
;
256 if ((ip
->i_flags
& APPEND
) && uio
->uio_offset
!= ip
->i_size
)
263 if ((ioflag
& IO_SYNC
) == 0)
264 panic("ffs_write: nonsync dir write");
267 panic("ffs_write: invalid v_type=%x", vp
->v_type
);
271 if (uio
->uio_offset
< 0 ||
272 (u_int64_t
)uio
->uio_offset
+ uio_resid(uio
) > fs
->fs_maxfilesize
)
274 if (uio_resid(uio
) == 0)
277 // LP64todo - fix this
278 resid
= uio_resid(uio
);
281 if ((ioflag
& IO_SYNC
) && !((vp
)->v_mount
->mnt_flag
& MNT_ASYNC
))
284 if (UBCINFOEXISTS(vp
)) {
295 // LP64todo - fix this
296 endofwrite
= uio
->uio_offset
+ uio_resid(uio
);
298 if (endofwrite
> ip
->i_size
) {
299 filesize
= endofwrite
;
302 filesize
= ip
->i_size
;
304 head_offset
= ip
->i_size
;
306 /* Go ahead and allocate the block that are going to be written */
307 // LP64todo - fix this
308 rsd
= uio_resid(uio
);
309 local_offset
= uio
->uio_offset
;
311 if ((ioflag
& IO_SYNC
) && !((vp
)->v_mount
->mnt_flag
& MNT_ASYNC
))
312 local_flags
= B_SYNC
;
313 local_flags
|= B_NOBUFF
;
320 for (error
= 0; rsd
> 0;) {
322 lbn
= lblkno(fs
, local_offset
);
323 blkoffset
= blkoff(fs
, local_offset
);
324 xfersize
= fs
->fs_bsize
- blkoffset
;
329 if (fs
->fs_bsize
> xfersize
)
330 local_flags
|= B_CLRBUF
;
332 local_flags
&= ~B_CLRBUF
;
334 /* Allocate block without reading into a buf */
335 error
= ffs_balloc(ip
,
336 lbn
, blkoffset
+ xfersize
, cred
,
337 &bp
, local_flags
, &blkalloc
);
347 local_offset
+= (off_t
)xfersize
;
348 if (local_offset
> ip
->i_size
)
349 ip
->i_size
= local_offset
;
355 uio_setresid(uio
, (uio_resid(uio
) - rsd
));
360 flags
= ioflag
& IO_SYNC
? IO_SYNC
: 0;
361 /* flags |= IO_NOZEROVALID; */
363 if((error
== 0) && fblk
&& fboff
) {
364 if( fblk
> fs
->fs_bsize
)
365 panic("ffs_balloc : allocated more than bsize(head)");
366 /* We need to zero out the head */
367 head_offset
= uio
->uio_offset
- (off_t
)fboff
;
368 flags
|= IO_HEADZEROFILL
;
369 /* flags &= ~IO_NOZEROVALID; */
372 if((error
== 0) && blkalloc
&& ((blkalloc
- xfersize
) > 0)) {
373 /* We need to zero out the tail */
374 if( blkalloc
> fs
->fs_bsize
)
375 panic("ffs_balloc : allocated more than bsize(tail)");
376 local_offset
+= (blkalloc
- xfersize
);
377 if (loopcount
== 1) {
378 /* blkalloc is same as fblk; so no need to check again*/
379 local_offset
-= fboff
;
381 flags
|= IO_TAILZEROFILL
;
382 /* Freshly allocated block; bzero even if
385 /* flags &= ~IO_NOZEROVALID; */
388 * if the write starts beyond the current EOF then
389 * we we'll zero fill from the current EOF to where the write begins
392 error
= cluster_write(vp
, uio
, osize
, filesize
, head_offset
, local_offset
, flags
);
394 if (uio
->uio_offset
> osize
) {
395 if (error
&& ((ioflag
& IO_UNIT
)==0))
396 (void)ffs_truncate_internal(vp
, uio
->uio_offset
, ioflag
& IO_SYNC
, cred
);
397 ip
->i_size
= uio
->uio_offset
;
398 ubc_setsize(vp
, (off_t
)ip
->i_size
);
401 uio_setresid(uio
, (uio_resid(uio
) + save_size
));
405 ip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
408 if ((ioflag
& IO_SYNC
) && !((vp
)->v_mount
->mnt_flag
& MNT_ASYNC
))
411 for (error
= 0; uio_resid(uio
) > 0;) {
414 lbn
= lblkno(fs
, uio
->uio_offset
);
415 blkoffset
= blkoff(fs
, uio
->uio_offset
);
416 xfersize
= fs
->fs_bsize
- blkoffset
;
417 if (uio_resid(uio
) < xfersize
)
418 // LP64todo - fix this
419 xfersize
= uio_resid(uio
);
421 if (fs
->fs_bsize
> xfersize
)
426 error
= ffs_balloc(ip
, lbn
, blkoffset
+ xfersize
, cred
, &bp
, flags
, 0);
429 if (uio
->uio_offset
+ xfersize
> ip
->i_size
) {
430 ip
->i_size
= uio
->uio_offset
+ xfersize
;
431 ubc_setsize(vp
, (u_long
)ip
->i_size
);
434 size
= BLKSIZE(fs
, ip
, lbn
) - buf_resid(bp
);
438 buf_data
= (char *)buf_dataptr(bp
);
440 error
= uiomove(buf_data
+ blkoffset
, (int)xfersize
, uio
);
442 if (rev_endian
&& S_ISDIR(ip
->i_mode
)) {
443 byte_swap_dir_out(buf_data
+ blkoffset
, xfersize
);
445 #endif /* REV_ENDIAN_FS */
446 if (doingdirectory
== 0 && (ioflag
& IO_SYNC
))
447 (void)buf_bwrite(bp
);
448 else if (xfersize
+ blkoffset
== fs
->fs_bsize
) {
454 if (error
|| xfersize
== 0)
456 ip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
460 * If we successfully wrote any data, and we are not the superuser
461 * we clear the setuid and setgid bits as a precaution against
464 if (resid
> uio_resid(uio
) && cred
&& suser(cred
, NULL
))
465 ip
->i_mode
&= ~(ISUID
| ISGID
);
466 if (resid
> uio_resid(uio
))
467 VN_KNOTE(vp
, NOTE_WRITE
| (file_extended
? NOTE_EXTEND
: 0));
469 if (ioflag
& IO_UNIT
) {
470 (void)ffs_truncate_internal(vp
, osize
, ioflag
& IO_SYNC
, cred
);
471 // LP64todo - fix this
472 uio
->uio_offset
-= resid
- uio_resid(uio
);
473 uio_setresid(uio
, resid
);
475 } else if (resid
> uio_resid(uio
) && (ioflag
& IO_SYNC
)) {
479 error
= ffs_update(vp
, &tv
, &tv
, 1);
485 * Vnode op for pagein.
486 * Similar to ffs_read()
490 struct vnop_pagein_args
/* {
493 vm_offset_t a_pl_offset,
497 vfs_context_t a_context;
500 register struct vnode
*vp
= ap
->a_vp
;
502 size_t size
= ap
->a_size
;
503 off_t f_offset
= ap
->a_f_offset
;
504 vm_offset_t pl_offset
= ap
->a_pl_offset
;
505 int flags
= ap
->a_flags
;
506 register struct inode
*ip
;
511 /* check pageins for reg file only and ubc info is present*/
513 panic("ffs_pagein: Not a VREG: vp=%x", vp
);
514 if (UBCINFOMISSING(vp
))
515 panic("ffs_pagein: No mapping: vp=%x", vp
);
518 if (vp
->v_type
== VLNK
) {
519 if ((int)ip
->i_size
< vp
->v_mount
->mnt_maxsymlinklen
)
520 panic("%s: short symlink", "ffs_pagein");
521 } else if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
)
522 panic("%s: type %d", "ffs_pagein", vp
->v_type
);
525 error
= cluster_pagein(vp
, pl
, pl_offset
, f_offset
, size
, (off_t
)ip
->i_size
, flags
);
527 /* ip->i_flag |= IN_ACCESS; */
532 * Vnode op for pageout.
533 * Similar to ffs_write()
534 * make sure the buf is not in hash queue when you return
537 struct vnop_pageout_args
/* {
540 vm_offset_t a_pl_offset,
544 vfs_context_t a_context;
547 register struct vnode
*vp
= ap
->a_vp
;
549 size_t size
= ap
->a_size
;
550 off_t f_offset
= ap
->a_f_offset
;
551 vm_offset_t pl_offset
= ap
->a_pl_offset
;
552 int flags
= ap
->a_flags
;
553 register struct inode
*ip
;
556 size_t xfer_size
= 0;
559 int resid
, blkoffset
;
562 int save_error
=0, save_size
=0;
563 vm_offset_t lupl_offset
;
564 int nocommit
= flags
& UPL_NOCOMMIT
;
565 int devBlockSize
= 0;
570 /* check pageouts for reg file only and ubc info is present*/
572 panic("ffs_pageout: Not a VREG: vp=%x", vp
);
573 if (UBCINFOMISSING(vp
))
574 panic("ffs_pageout: No mapping: vp=%x", vp
);
576 if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) {
578 ubc_upl_abort_range(pl
, pl_offset
, size
,
579 UPL_ABORT_FREE_ON_EMPTY
);
584 if (f_offset
< 0 || f_offset
>= ip
->i_size
) {
586 ubc_upl_abort_range(pl
, pl_offset
, size
,
587 UPL_ABORT_FREE_ON_EMPTY
);
592 * once we enable multi-page pageouts we will
593 * need to make sure we abort any pages in the upl
594 * that we don't issue an I/O for
596 if (f_offset
+ size
> ip
->i_size
)
597 xfer_size
= ip
->i_size
- f_offset
;
601 devBlockSize
= vfs_devblocksize(vnode_mount(vp
));
603 if (xfer_size
& (PAGE_SIZE
- 1)) {
604 /* if not a multiple of page size
605 * then round up to be a multiple
606 * the physical disk block size
608 xfer_size
= (xfer_size
+ (devBlockSize
- 1)) & ~(devBlockSize
- 1);
612 * once the block allocation is moved to ufs_blockmap
613 * we can remove all the size and offset checks above
614 * cluster_pageout does all of this now
615 * we need to continue to do it here so as not to
616 * allocate blocks that aren't going to be used because
617 * of a bogus parameter being passed in
621 local_offset
= f_offset
;
622 for (error
= 0; resid
> 0;) {
623 lbn
= lblkno(fs
, local_offset
);
624 blkoffset
= blkoff(fs
, local_offset
);
625 xsize
= fs
->fs_bsize
- blkoffset
;
628 /* Allocate block without reading into a buf */
629 error
= ffs_blkalloc(ip
,
630 lbn
, blkoffset
+ xsize
, vfs_context_ucred(ap
->a_context
),
635 local_offset
+= (off_t
)xsize
;
641 xfer_size
-= save_size
;
645 error
= cluster_pageout(vp
, pl
, pl_offset
, f_offset
, round_page_32(xfer_size
), ip
->i_size
, flags
);
648 lupl_offset
= size
- save_size
;
649 resid
= round_page_32(save_size
);
651 ubc_upl_abort_range(pl
, lupl_offset
, resid
,
652 UPL_ABORT_FREE_ON_EMPTY
);