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
) {
174 * Set the B_LOCKED flag and unlock the buffer, causing brelse to move
175 * the buffer onto the LOCKED free list. This is necessary, otherwise
176 * getnewbuf() would try to reclaim the buffers using bawrite, which
177 * isn't going to work.
180 extern int count_lock_queue
__P((void));
181 /* Don't hog all the buffers... */
182 if (count_lock_queue() > kMaxLockedMetaBuffers
)
183 hfs_fsync_transaction(vp
);
184 bp
->b_flags
|= B_LOCKED
;
190 brelse(bp
); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
199 OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
)
201 #pragma unused (maxEOF)
204 UInt64 actualBytesAdded
;
210 struct proc
*p
= NULL
;
213 filePtr
= GetFileControlBlock(vp
);
215 if ( minEOF
> filePtr
->fcbEOF
)
217 bytesToAdd
= minEOF
- filePtr
->fcbEOF
;
219 if (bytesToAdd
< filePtr
->fcbClmpSize
)
220 bytesToAdd
= filePtr
->fcbClmpSize
; //XXX why not always be a mutiple of clump size?
224 DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!"));
228 vcb
= FCBTOVCB(filePtr
);
231 * The Extents B-tree can't have overflow extents. ExtendFileC will
232 * return an error if an attempt is made to extend the Extents B-tree
233 * when the resident extents are exhausted.
235 /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */
236 if(H_FILEID(filePtr
) != kHFSExtentsFileID
)
239 /* lock extents b-tree (also protects volume bitmap) */
240 retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
245 (void) BTGetInformation(filePtr
, 0, &btInfo
);
248 * The b-tree code expects nodes to be contiguous. So when
249 * the allocation block size is less than the b-tree node
250 * size, we need to force disk allocations to be contiguous.
252 if (vcb
->blockSize
>= btInfo
.nodeSize
) {
255 /* Ensure that all b-tree nodes are contiguous on disk */
256 extendFlags
= kEFAllMask
| kEFContigMask
;
259 retval
= ExtendFileC(vcb
, filePtr
, bytesToAdd
, 0, extendFlags
, &actualBytesAdded
);
261 if(H_FILEID(filePtr
) != kHFSExtentsFileID
)
262 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_RELEASE
, p
);
267 if (actualBytesAdded
< bytesToAdd
)
268 DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!"));
270 filePtr
->fcbEOF
= filePtr
->fcbPLen
;
272 retval
= ClearBTNodes(vp
, btInfo
.nodeSize
, filePtr
->fcbEOF
- actualBytesAdded
, actualBytesAdded
);
277 * Update the Alternate MDB or Alternate VolumeHeader
279 if ((H_FILEID(filePtr
) == kHFSExtentsFileID
) ||
280 (H_FILEID(filePtr
) == kHFSCatalogFileID
) ||
281 (H_FILEID(filePtr
) == kHFSAttributesFileID
)
284 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
285 retval
= hfs_flushvolumeheader(VCBTOHFS(vcb
), 0);
287 retval
= hfs_flushMDB(VCBTOHFS(vcb
), 0);
290 retval
= FlushAlternate(vcb
);
299 FlushAlternate( ExtendedVCB
*vcb
)
305 /* Get the main MDB/VolumeHeader block */
306 result
= GetBlock_glue(gbDefault
,
307 (vcb
->hfsPlusIOPosOffset
/ kHFSBlockSize
) + kMasterDirectoryBlock
,
308 (Ptr
*)&maindata
, kNoFileReference
, vcb
);
309 if (result
) return (result
);
311 /* Get the alternate MDB/VolumeHeader block */
312 result
= GetBlock_glue( gbDefault
, vcb
->altIDSector
,
313 (Ptr
*)&altdata
, kNoFileReference
, vcb
);
316 bcopy(maindata
, altdata
, kMDBSize
);
318 result
= RelBlock_glue( (Ptr
)altdata
, rbWriteMask
);
321 (void) RelBlock_glue( (Ptr
)maindata
, rbFreeMask
);
328 * Clear out (zero) new b-tree nodes on disk.
331 ClearBTNodes(struct vnode
*vp
, long blksize
, off_t offset
, off_t amount
)
333 struct buf
*bp
= NULL
;
337 blk
= offset
/ blksize
;
338 blkcnt
= amount
/ blksize
;
341 bp
= getblk(vp
, blk
, blksize
, 0, 0, BLK_META
);
344 bzero((char *)bp
->b_data
, blksize
);
345 bp
->b_flags
|= (B_DIRTY
| B_AGE
);
347 /* wait/yield every 32 blocks so we don't hog all the buffers */