]>
git.saurik.com Git - apple/xnu.git/blob - bsd/ufs/ufs/ufs_readwrite.c
9aa3b92cd80e6093841a6208865bf0d35af28c52
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
25 * The Regents of the University of California. All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95
58 #include <sys/buf_internal.h>
59 #include <sys/uio_internal.h>
62 #define BLKSIZE(a, b, c) blksize(a, b, c)
69 * Vnode op for reading.
73 struct vnop_read_args
/* {
77 vfs_context_t a_context;
80 return(ffs_read_internal(ap
->a_vp
, ap
->a_uio
, ap
->a_ioflag
));
85 ffs_read_internal(vnode_t vp
, struct uio
*uio
, int ioflag
)
89 buf_t bp
= (struct buf
*)0;
90 ufs_daddr_t lbn
, nextlbn
;
92 long size
, xfersize
, blkoffset
;
97 #endif /* REV_ENDIAN_FS */
103 rev_endian
=(vp
->v_mount
->mnt_flag
& MNT_REVEND
);
104 #endif /* REV_ENDIAN_FS */
107 if (uio
->uio_rw
!= UIO_READ
)
108 panic("ffs_read: invalid uio_rw = %x", uio
->uio_rw
);
110 if (vp
->v_type
== VLNK
) {
111 if ((int)ip
->i_size
< vp
->v_mount
->mnt_maxsymlinklen
)
112 panic("ffs_read: short symlink = %d", ip
->i_size
);
113 } else if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
)
114 panic("ffs_read: invalid v_type = %x", vp
->v_type
);
117 if (uio
->uio_offset
< 0)
119 if (uio
->uio_offset
> fs
->fs_maxfilesize
)
122 if (UBCINFOEXISTS(vp
)) {
123 error
= cluster_read(vp
, uio
, (off_t
)ip
->i_size
, 0);
125 for (error
= 0, bp
= NULL
; uio_resid(uio
) > 0;
129 if ((bytesinfile
= ip
->i_size
- uio
->uio_offset
) <= 0)
131 lbn
= lblkno(fs
, uio
->uio_offset
);
133 size
= BLKSIZE(fs
, ip
, lbn
);
134 blkoffset
= blkoff(fs
, uio
->uio_offset
);
135 xfersize
= fs
->fs_bsize
- blkoffset
;
136 // LP64todo - fix this
137 if (uio_resid(uio
) < xfersize
)
138 xfersize
= uio_resid(uio
);
139 if (bytesinfile
< xfersize
)
140 xfersize
= bytesinfile
;
142 if (lblktosize(fs
, nextlbn
) >= ip
->i_size
)
143 error
= (int)buf_bread(vp
, (daddr64_t
)((unsigned)lbn
), size
, NOCRED
, &bp
);
144 else if (lbn
- 1 == ip
->i_lastr
&& !(vp
->v_flag
& VRAOFF
)) {
145 int nextsize
= BLKSIZE(fs
, ip
, nextlbn
);
146 error
= (int)buf_breadn(vp
, (daddr64_t
)((unsigned)lbn
),
147 size
, &nextlbn
, &nextsize
, 1, NOCRED
, &bp
);
149 error
= (int)buf_bread(vp
, lbn
, size
, NOCRED
, &bp
);
155 * We should only get non-zero buffer resid when an I/O error
156 * has occurred, which should cause us to break above.
157 * However, if the short read did not cause an error,
158 * then we want to ensure that we do not uiomove bad
159 * or uninitialized data.
161 size
-= buf_resid(bp
);
162 if (size
< xfersize
) {
167 buf_data
= (char *)buf_dataptr(bp
);
169 if (rev_endian
&& S_ISDIR(mode
)) {
170 byte_swap_dir_block_in(buf_data
+ blkoffset
, xfersize
);
172 #endif /* REV_ENDIAN_FS */
174 uiomove(buf_data
+ blkoffset
, (int)xfersize
, uio
)) {
176 if (rev_endian
&& S_ISDIR(mode
)) {
177 byte_swap_dir_block_in(buf_data
+ blkoffset
, xfersize
);
179 #endif /* REV_ENDIAN_FS */
184 if (rev_endian
&& S_ISDIR(mode
)) {
185 byte_swap_dir_out(buf_data
+ blkoffset
, xfersize
);
187 #endif /* REV_ENDIAN_FS */
188 if (S_ISREG(mode
) && (xfersize
+ blkoffset
== fs
->fs_bsize
||
189 uio
->uio_offset
== ip
->i_size
))
196 ip
->i_flag
|= IN_ACCESS
;
201 * Vnode op for writing.
204 struct vnop_write_args
/* {
208 vfs_context_t a_context;
211 return(ffs_write_internal(ap
->a_vp
, ap
->a_uio
, ap
->a_ioflag
, vfs_context_ucred(ap
->a_context
)));
215 ffs_write_internal(vnode_t vp
, struct uio
*uio
, int ioflag
, ucred_t cred
)
223 int blkoffset
, flags
, resid
, rsd
, size
, xfersize
;
224 int save_error
=0, save_size
=0;
227 int file_extended
= 0;
228 int doingdirectory
= 0;
232 #endif /* REV_ENDIAN_FS */
236 rev_endian
=(vp
->v_mount
->mnt_flag
& MNT_REVEND
);
237 #endif /* REV_ENDIAN_FS */
240 if (uio
->uio_rw
!= UIO_WRITE
)
241 panic("ffs_write: uio_rw = %x\n", uio
->uio_rw
);
244 switch (vp
->v_type
) {
246 if (ioflag
& IO_APPEND
)
247 uio
->uio_offset
= ip
->i_size
;
248 if ((ip
->i_flags
& APPEND
) && uio
->uio_offset
!= ip
->i_size
)
255 if ((ioflag
& IO_SYNC
) == 0)
256 panic("ffs_write: nonsync dir write");
259 panic("ffs_write: invalid v_type=%x", vp
->v_type
);
263 if (uio
->uio_offset
< 0 ||
264 (u_int64_t
)uio
->uio_offset
+ uio_resid(uio
) > fs
->fs_maxfilesize
)
266 if (uio_resid(uio
) == 0)
269 // LP64todo - fix this
270 resid
= uio_resid(uio
);
273 if ((ioflag
& IO_SYNC
) && !((vp
)->v_mount
->mnt_flag
& MNT_ASYNC
))
276 if (UBCINFOEXISTS(vp
)) {
287 // LP64todo - fix this
288 endofwrite
= uio
->uio_offset
+ uio_resid(uio
);
290 if (endofwrite
> ip
->i_size
) {
291 filesize
= endofwrite
;
294 filesize
= ip
->i_size
;
296 head_offset
= ip
->i_size
;
298 /* Go ahead and allocate the block that are going to be written */
299 // LP64todo - fix this
300 rsd
= uio_resid(uio
);
301 local_offset
= uio
->uio_offset
;
303 if ((ioflag
& IO_SYNC
) && !((vp
)->v_mount
->mnt_flag
& MNT_ASYNC
))
304 local_flags
= B_SYNC
;
305 local_flags
|= B_NOBUFF
;
312 for (error
= 0; rsd
> 0;) {
314 lbn
= lblkno(fs
, local_offset
);
315 blkoffset
= blkoff(fs
, local_offset
);
316 xfersize
= fs
->fs_bsize
- blkoffset
;
321 if (fs
->fs_bsize
> xfersize
)
322 local_flags
|= B_CLRBUF
;
324 local_flags
&= ~B_CLRBUF
;
326 /* Allocate block without reading into a buf */
327 error
= ffs_balloc(ip
,
328 lbn
, blkoffset
+ xfersize
, cred
,
329 &bp
, local_flags
, &blkalloc
);
339 local_offset
+= (off_t
)xfersize
;
340 if (local_offset
> ip
->i_size
)
341 ip
->i_size
= local_offset
;
347 uio_setresid(uio
, (uio_resid(uio
) - rsd
));
352 flags
= ioflag
& IO_SYNC
? IO_SYNC
: 0;
353 /* flags |= IO_NOZEROVALID; */
355 if((error
== 0) && fblk
&& fboff
) {
356 if( fblk
> fs
->fs_bsize
)
357 panic("ffs_balloc : allocated more than bsize(head)");
358 /* We need to zero out the head */
359 head_offset
= uio
->uio_offset
- (off_t
)fboff
;
360 flags
|= IO_HEADZEROFILL
;
361 /* flags &= ~IO_NOZEROVALID; */
364 if((error
== 0) && blkalloc
&& ((blkalloc
- xfersize
) > 0)) {
365 /* We need to zero out the tail */
366 if( blkalloc
> fs
->fs_bsize
)
367 panic("ffs_balloc : allocated more than bsize(tail)");
368 local_offset
+= (blkalloc
- xfersize
);
369 if (loopcount
== 1) {
370 /* blkalloc is same as fblk; so no need to check again*/
371 local_offset
-= fboff
;
373 flags
|= IO_TAILZEROFILL
;
374 /* Freshly allocated block; bzero even if
377 /* flags &= ~IO_NOZEROVALID; */
380 * if the write starts beyond the current EOF then
381 * we we'll zero fill from the current EOF to where the write begins
384 error
= cluster_write(vp
, uio
, osize
, filesize
, head_offset
, local_offset
, flags
);
386 if (uio
->uio_offset
> osize
) {
387 if (error
&& ((ioflag
& IO_UNIT
)==0))
388 (void)ffs_truncate_internal(vp
, uio
->uio_offset
, ioflag
& IO_SYNC
, cred
);
389 ip
->i_size
= uio
->uio_offset
;
390 ubc_setsize(vp
, (off_t
)ip
->i_size
);
393 uio_setresid(uio
, (uio_resid(uio
) + save_size
));
397 ip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
400 if ((ioflag
& IO_SYNC
) && !((vp
)->v_mount
->mnt_flag
& MNT_ASYNC
))
403 for (error
= 0; uio_resid(uio
) > 0;) {
406 lbn
= lblkno(fs
, uio
->uio_offset
);
407 blkoffset
= blkoff(fs
, uio
->uio_offset
);
408 xfersize
= fs
->fs_bsize
- blkoffset
;
409 if (uio_resid(uio
) < xfersize
)
410 // LP64todo - fix this
411 xfersize
= uio_resid(uio
);
413 if (fs
->fs_bsize
> xfersize
)
418 error
= ffs_balloc(ip
, lbn
, blkoffset
+ xfersize
, cred
, &bp
, flags
, 0);
421 if (uio
->uio_offset
+ xfersize
> ip
->i_size
) {
422 ip
->i_size
= uio
->uio_offset
+ xfersize
;
423 ubc_setsize(vp
, (u_long
)ip
->i_size
);
426 size
= BLKSIZE(fs
, ip
, lbn
) - buf_resid(bp
);
430 buf_data
= (char *)buf_dataptr(bp
);
432 error
= uiomove(buf_data
+ blkoffset
, (int)xfersize
, uio
);
434 if (rev_endian
&& S_ISDIR(ip
->i_mode
)) {
435 byte_swap_dir_out(buf_data
+ blkoffset
, xfersize
);
437 #endif /* REV_ENDIAN_FS */
438 if (doingdirectory
== 0 && (ioflag
& IO_SYNC
))
439 (void)buf_bwrite(bp
);
440 else if (xfersize
+ blkoffset
== fs
->fs_bsize
) {
446 if (error
|| xfersize
== 0)
448 ip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
452 * If we successfully wrote any data, and we are not the superuser
453 * we clear the setuid and setgid bits as a precaution against
456 if (resid
> uio_resid(uio
) && cred
&& suser(cred
, NULL
))
457 ip
->i_mode
&= ~(ISUID
| ISGID
);
458 if (resid
> uio_resid(uio
))
459 VN_KNOTE(vp
, NOTE_WRITE
| (file_extended
? NOTE_EXTEND
: 0));
461 if (ioflag
& IO_UNIT
) {
462 (void)ffs_truncate_internal(vp
, osize
, ioflag
& IO_SYNC
, cred
);
463 // LP64todo - fix this
464 uio
->uio_offset
-= resid
- uio_resid(uio
);
465 uio_setresid(uio
, resid
);
467 } else if (resid
> uio_resid(uio
) && (ioflag
& IO_SYNC
)) {
471 error
= ffs_update(vp
, &tv
, &tv
, 1);
477 * Vnode op for pagein.
478 * Similar to ffs_read()
482 struct vnop_pagein_args
/* {
485 vm_offset_t a_pl_offset,
489 vfs_context_t a_context;
492 register struct vnode
*vp
= ap
->a_vp
;
494 size_t size
= ap
->a_size
;
495 off_t f_offset
= ap
->a_f_offset
;
496 vm_offset_t pl_offset
= ap
->a_pl_offset
;
497 int flags
= ap
->a_flags
;
498 register struct inode
*ip
;
503 /* check pageins for reg file only and ubc info is present*/
505 panic("ffs_pagein: Not a VREG: vp=%x", vp
);
506 if (UBCINFOMISSING(vp
))
507 panic("ffs_pagein: No mapping: vp=%x", vp
);
510 if (vp
->v_type
== VLNK
) {
511 if ((int)ip
->i_size
< vp
->v_mount
->mnt_maxsymlinklen
)
512 panic("%s: short symlink", "ffs_pagein");
513 } else if (vp
->v_type
!= VREG
&& vp
->v_type
!= VDIR
)
514 panic("%s: type %d", "ffs_pagein", vp
->v_type
);
517 error
= cluster_pagein(vp
, pl
, pl_offset
, f_offset
, size
, (off_t
)ip
->i_size
, flags
);
519 /* ip->i_flag |= IN_ACCESS; */
524 * Vnode op for pageout.
525 * Similar to ffs_write()
526 * make sure the buf is not in hash queue when you return
529 struct vnop_pageout_args
/* {
532 vm_offset_t a_pl_offset,
536 vfs_context_t a_context;
539 register struct vnode
*vp
= ap
->a_vp
;
541 size_t size
= ap
->a_size
;
542 off_t f_offset
= ap
->a_f_offset
;
543 vm_offset_t pl_offset
= ap
->a_pl_offset
;
544 int flags
= ap
->a_flags
;
545 register struct inode
*ip
;
548 size_t xfer_size
= 0;
551 int resid
, blkoffset
;
554 int save_error
=0, save_size
=0;
555 vm_offset_t lupl_offset
;
556 int nocommit
= flags
& UPL_NOCOMMIT
;
557 int devBlockSize
= 0;
562 /* check pageouts for reg file only and ubc info is present*/
564 panic("ffs_pageout: Not a VREG: vp=%x", vp
);
565 if (UBCINFOMISSING(vp
))
566 panic("ffs_pageout: No mapping: vp=%x", vp
);
568 if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) {
570 ubc_upl_abort_range(pl
, pl_offset
, size
,
571 UPL_ABORT_FREE_ON_EMPTY
);
576 if (f_offset
< 0 || f_offset
>= ip
->i_size
) {
578 ubc_upl_abort_range(pl
, pl_offset
, size
,
579 UPL_ABORT_FREE_ON_EMPTY
);
584 * once we enable multi-page pageouts we will
585 * need to make sure we abort any pages in the upl
586 * that we don't issue an I/O for
588 if (f_offset
+ size
> ip
->i_size
)
589 xfer_size
= ip
->i_size
- f_offset
;
593 devBlockSize
= vfs_devblocksize(vnode_mount(vp
));
595 if (xfer_size
& (PAGE_SIZE
- 1)) {
596 /* if not a multiple of page size
597 * then round up to be a multiple
598 * the physical disk block size
600 xfer_size
= (xfer_size
+ (devBlockSize
- 1)) & ~(devBlockSize
- 1);
604 * once the block allocation is moved to ufs_blockmap
605 * we can remove all the size and offset checks above
606 * cluster_pageout does all of this now
607 * we need to continue to do it here so as not to
608 * allocate blocks that aren't going to be used because
609 * of a bogus parameter being passed in
613 local_offset
= f_offset
;
614 for (error
= 0; resid
> 0;) {
615 lbn
= lblkno(fs
, local_offset
);
616 blkoffset
= blkoff(fs
, local_offset
);
617 xsize
= fs
->fs_bsize
- blkoffset
;
620 /* Allocate block without reading into a buf */
621 error
= ffs_blkalloc(ip
,
622 lbn
, blkoffset
+ xsize
, vfs_context_ucred(ap
->a_context
),
627 local_offset
+= (off_t
)xsize
;
633 xfer_size
-= save_size
;
637 error
= cluster_pageout(vp
, pl
, pl_offset
, f_offset
, round_page_32(xfer_size
), ip
->i_size
, flags
);
640 lupl_offset
= size
- save_size
;
641 resid
= round_page_32(save_size
);
643 ubc_upl_abort_range(pl
, lupl_offset
, resid
,
644 UPL_ABORT_FREE_ON_EMPTY
);