2 * Copyright (c) 2000 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@
24 * (c) 1998, 2000 Apple Computer, Inc. All Rights Reserved
26 * hfs_btreeio.c -- I/O Routines for the HFS B-tree files.
29 * 15-Feb-2000 Don Brady Added ClearBTNodes.
30 * 16-Jul-1998 Don Brady In ExtendBtreeFile force all b-tree nodes to be contiguous on disk.
31 * 4-Jun-1998 Pat Dirks Changed to do all B*-Tree writes synchronously (FORCESYNCBTREEWRITES = 1)
32 * 18-apr-1998 Don Brady Call brelse on bread failure.
33 * 17-Apr-1998 Pat Dirks Fixed ReleaseBTreeBlock to not call brelse when bwrite or bdwrite is called.
34 * 13-apr-1998 Don Brady Add ExtendBTreeFile routine (from BTreeWrapper.c).
35 * 26-mar-1998 Don Brady SetBTreeBlockSize was incorrectly excluding 512 byte blockSize.
36 * 18-feb-1998 Don Brady Initially created file.
40 #include <sys/param.h>
41 #include <sys/systm.h>
43 #include <sys/mount.h>
44 #include <sys/vnode.h>
49 #include "hfs_endian.h"
51 #include "hfscommon/headers/FileMgrInternal.h"
52 #include "hfscommon/headers/BTreesPrivate.h"
54 #define FORCESYNCBTREEWRITES 0
56 static OSStatus
FlushAlternate( ExtendedVCB
*vcb
);
58 static int ClearBTNodes(struct vnode
*vp
, long blksize
, off_t offset
, off_t amount
);
61 OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
)
63 BTreeControlBlockPtr bTreePtr
;
65 DBG_ASSERT(vp
!= NULL
);
66 DBG_ASSERT(VTOFCB(vp
) != NULL
);
67 DBG_ASSERT(VTOFCB(vp
)->fcbBTCBPtr
!= NULL
);
68 DBG_ASSERT(blockSize
>= kMinNodeSize
);
69 if (blockSize
> MAXBSIZE
)
70 return (fsBTBadNodeSize
);
72 DBG_TREE(("SetBlockSizeProc: blockSize=%ld for file %ld\n", blockSize
, H_FILEID(VTOH(vp
))));
74 bTreePtr
= (BTreeControlBlockPtr
)(VTOH(vp
)->fcbBTCBPtr
);
75 bTreePtr
->nodeSize
= blockSize
;
81 OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
)
83 OSStatus retval
= E_NONE
;
84 struct buf
*bp
= NULL
;
86 if (options
& kGetEmptyBlock
)
88 IOBLKNOFORBLK(blockNum
, VTOHFS(vp
)->hfs_phys_block_size
),
89 IOBYTECCNTFORBLK(blockNum
, block
->blockSize
, VTOHFS(vp
)->hfs_phys_block_size
),
94 retval
= meta_bread(vp
,
95 IOBLKNOFORBLK(blockNum
, VTOHFS(vp
)->hfs_phys_block_size
),
96 IOBYTECCNTFORBLK(blockNum
, block
->blockSize
, VTOHFS(vp
)->hfs_phys_block_size
),
100 DBG_ASSERT(bp
!= NULL
);
101 DBG_ASSERT(bp
->b_data
!= NULL
);
102 DBG_ASSERT(bp
->b_bcount
== block
->blockSize
);
103 DBG_ASSERT(bp
->b_lblkno
== blockNum
);
106 retval
= -1; //XXX need better error
108 if (retval
== E_NONE
) {
109 block
->blockHeader
= bp
;
110 block
->buffer
= bp
->b_data
+ IOBYTEOFFSETFORBLK(bp
->b_blkno
, VTOHFS(vp
)->hfs_phys_block_size
);
111 block
->blockReadFromDisk
= (bp
->b_flags
& B_CACHE
) == 0; /* not found in cache ==> came from disk */
113 #if BYTE_ORDER == LITTLE_ENDIAN
114 /* Endian swap B-Tree node (only if it's a valid block) */
115 if (!(options
& kGetEmptyBlock
)) {
116 /* This happens when we first open the b-tree, we might not have all the node data on hand */
117 if ((((BTNodeDescriptor
*)block
->buffer
)->kind
== kBTHeaderNode
) &&
118 (((BTHeaderRec
*)((char *)block
->buffer
+ 14))->nodeSize
!= bp
->b_bcount
) &&
119 (SWAP_BE16 (((BTHeaderRec
*)((char *)block
->buffer
+ 14))->nodeSize
) != bp
->b_bcount
)) {
121 /* Don't swap the descriptors at all, we don't care (this block will be invalidated) */
122 SWAP_BT_NODE (block
, ISHFSPLUS(VTOVCB(vp
)), H_FILEID(VTOH(vp
)), 3);
124 /* The node needs swapping */
125 } else if (*((UInt16
*)((char *)block
->buffer
+ (block
->blockSize
- sizeof (UInt16
)))) == 0x0e00) {
126 SWAP_BT_NODE (block
, ISHFSPLUS(VTOVCB(vp
)), H_FILEID(VTOH(vp
)), 0);
128 /* The node is not already in native byte order, hence corrupt */
129 } else if (*((UInt16
*)((char *)block
->buffer
+ (block
->blockSize
- sizeof (UInt16
)))) != 0x000e) {
130 panic ("%s Corrupt B-Tree node detected!\n", "GetBTreeBlock:");
138 block
->blockHeader
= NULL
;
139 block
->buffer
= NULL
;
146 OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
)
148 OSStatus retval
= E_NONE
;
149 struct buf
*bp
= NULL
;
151 bp
= (struct buf
*) blockPtr
->blockHeader
;
154 DBG_TREE(("ReleaseBlockProc: blockHeader is zero!\n"));
159 if (options
& kTrashBlock
) {
160 bp
->b_flags
|= B_INVAL
;
161 brelse(bp
); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
163 if (options
& kForceWriteBlock
) {
164 bp
->b_flags
|= B_DIRTY
;
165 retval
= VOP_BWRITE(bp
);
166 } else if (options
& kMarkBlockDirty
) {
167 bp
->b_flags
|= B_DIRTY
;
168 #if FORCESYNCBTREEWRITES
171 if (options
& kLockTransaction
) {
175 * Set the B_LOCKED flag and unlock the buffer, causing brelse to move
176 * the buffer onto the LOCKED free list. This is necessary, otherwise
177 * getnewbuf() would try to reclaim the buffers using bawrite, which
178 * isn't going to work.
181 bp
->b_flags
|= B_LOCKED
;
187 brelse(bp
); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
196 OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
)
198 #pragma unused (maxEOF)
201 UInt64 actualBytesAdded
;
207 struct proc
*p
= NULL
;
210 filePtr
= GetFileControlBlock(vp
);
212 if ( minEOF
> filePtr
->fcbEOF
)
214 bytesToAdd
= minEOF
- filePtr
->fcbEOF
;
216 if (bytesToAdd
< filePtr
->fcbClmpSize
)
217 bytesToAdd
= filePtr
->fcbClmpSize
; //XXX why not always be a mutiple of clump size?
221 DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!"));
225 vcb
= FCBTOVCB(filePtr
);
228 * The Extents B-tree can't have overflow extents. ExtendFileC will
229 * return an error if an attempt is made to extend the Extents B-tree
230 * when the resident extents are exhausted.
232 /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */
233 if(H_FILEID(filePtr
) != kHFSExtentsFileID
)
236 /* lock extents b-tree (also protects volume bitmap) */
237 retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
242 (void) BTGetInformation(filePtr
, 0, &btInfo
);
245 * The b-tree code expects nodes to be contiguous. So when
246 * the allocation block size is less than the b-tree node
247 * size, we need to force disk allocations to be contiguous.
249 if (vcb
->blockSize
>= btInfo
.nodeSize
) {
252 /* Ensure that all b-tree nodes are contiguous on disk */
253 extendFlags
= kEFAllMask
| kEFContigMask
;
256 retval
= ExtendFileC(vcb
, filePtr
, bytesToAdd
, extendFlags
, &actualBytesAdded
);
258 if(H_FILEID(filePtr
) != kHFSExtentsFileID
)
259 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_RELEASE
, p
);
264 if (actualBytesAdded
< bytesToAdd
)
265 DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!"));
267 filePtr
->fcbEOF
= filePtr
->fcbPLen
;
269 retval
= ClearBTNodes(vp
, btInfo
.nodeSize
, filePtr
->fcbEOF
- actualBytesAdded
, actualBytesAdded
);
274 * Update the Alternate MDB or Alternate VolumeHeader
276 if ((H_FILEID(filePtr
) == kHFSExtentsFileID
) ||
277 (H_FILEID(filePtr
) == kHFSCatalogFileID
) ||
278 (H_FILEID(filePtr
) == kHFSAttributesFileID
)
281 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
282 retval
= hfs_flushvolumeheader(VCBTOHFS(vcb
), 0);
284 retval
= hfs_flushMDB(VCBTOHFS(vcb
), 0);
287 retval
= FlushAlternate(vcb
);
296 FlushAlternate( ExtendedVCB
*vcb
)
302 /* Get the main MDB/VolumeHeader block */
303 result
= GetBlock_glue(gbDefault
,
304 (vcb
->hfsPlusIOPosOffset
/ kHFSBlockSize
) + kMasterDirectoryBlock
,
305 (Ptr
*)&maindata
, kNoFileReference
, vcb
);
306 if (result
) return (result
);
308 /* Get the alternate MDB/VolumeHeader block */
309 result
= GetBlock_glue( gbDefault
, vcb
->altIDSector
,
310 (Ptr
*)&altdata
, kNoFileReference
, vcb
);
313 bcopy(maindata
, altdata
, kMDBSize
);
315 result
= RelBlock_glue( (Ptr
)altdata
, rbWriteMask
);
318 (void) RelBlock_glue( (Ptr
)maindata
, rbFreeMask
);
325 * Clear out (zero) new b-tree nodes on disk.
328 ClearBTNodes(struct vnode
*vp
, long blksize
, off_t offset
, off_t amount
)
330 struct buf
*bp
= NULL
;
334 blk
= offset
/ blksize
;
335 blkcnt
= amount
/ blksize
;
338 bp
= getblk(vp
, blk
, blksize
, 0, 0, BLK_META
);
341 bzero((char *)bp
->b_data
, blksize
);
342 bp
->b_flags
|= (B_DIRTY
| B_AGE
);
344 /* wait/yield every 32 blocks so we don't hog all the buffers */