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
)
87 bp
= getblk(vp
, blockNum
, block
->blockSize
, 0, 0, BLK_META
);
89 retval
= meta_bread(vp
, blockNum
, block
->blockSize
, NOCRED
, &bp
);
91 DBG_ASSERT(bp
!= NULL
);
92 DBG_ASSERT(bp
->b_data
!= NULL
);
93 DBG_ASSERT(bp
->b_bcount
== block
->blockSize
);
94 DBG_ASSERT(bp
->b_lblkno
== blockNum
);
97 retval
= -1; //XXX need better error
99 if (retval
== E_NONE
) {
100 block
->blockHeader
= bp
;
101 block
->buffer
= bp
->b_data
;
102 block
->blockReadFromDisk
= (bp
->b_flags
& B_CACHE
) == 0; /* not found in cache ==> came from disk */
104 #if BYTE_ORDER == LITTLE_ENDIAN
105 /* Endian swap B-Tree node (only if it's a valid block) */
106 if (!(options
& kGetEmptyBlock
)) {
107 /* This happens when we first open the b-tree, we might not have all the node data on hand */
108 if ((((BTNodeDescriptor
*)block
->buffer
)->kind
== kBTHeaderNode
) &&
109 (((BTHeaderRec
*)((char *)block
->buffer
+ 14))->nodeSize
!= bp
->b_bcount
) &&
110 (SWAP_BE16 (((BTHeaderRec
*)((char *)block
->buffer
+ 14))->nodeSize
) != bp
->b_bcount
)) {
112 /* Don't swap the descriptors at all, we don't care (this block will be invalidated) */
113 SWAP_BT_NODE (block
, ISHFSPLUS(VTOVCB(vp
)), H_FILEID(VTOH(vp
)), 3);
115 /* The node needs swapping */
116 } else if (*((UInt16
*)((char *)block
->buffer
+ (block
->blockSize
- sizeof (UInt16
)))) == 0x0e00) {
117 SWAP_BT_NODE (block
, ISHFSPLUS(VTOVCB(vp
)), H_FILEID(VTOH(vp
)), 0);
119 /* The node is not already in native byte order, hence corrupt */
120 } else if (*((UInt16
*)((char *)block
->buffer
+ (block
->blockSize
- sizeof (UInt16
)))) != 0x000e) {
121 panic ("%s Corrupt B-Tree node detected!\n", "GetBTreeBlock:");
129 block
->blockHeader
= NULL
;
130 block
->buffer
= NULL
;
137 OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
)
139 OSStatus retval
= E_NONE
;
140 struct buf
*bp
= NULL
;
142 bp
= (struct buf
*) blockPtr
->blockHeader
;
145 DBG_TREE(("ReleaseBlockProc: blockHeader is zero!\n"));
150 if (options
& kTrashBlock
) {
151 bp
->b_flags
|= B_INVAL
;
152 brelse(bp
); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
154 if (options
& kForceWriteBlock
) {
155 bp
->b_flags
|= B_DIRTY
;
156 retval
= VOP_BWRITE(bp
);
157 } else if (options
& kMarkBlockDirty
) {
158 bp
->b_flags
|= B_DIRTY
;
159 #if FORCESYNCBTREEWRITES
162 if (options
& kLockTransaction
) {
165 * Set the B_LOCKED flag and unlock the buffer, causing brelse to move
166 * the buffer onto the LOCKED free list. This is necessary, otherwise
167 * getnewbuf() would try to reclaim the buffers using bawrite, which
168 * isn't going to work.
171 extern int count_lock_queue
__P((void));
172 /* Don't hog all the buffers... */
173 if (count_lock_queue() > kMaxLockedMetaBuffers
)
174 hfs_fsync_transaction(vp
);
175 bp
->b_flags
|= B_LOCKED
;
181 brelse(bp
); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
190 OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
)
192 #pragma unused (maxEOF)
195 UInt64 actualBytesAdded
;
201 struct proc
*p
= NULL
;
204 filePtr
= GetFileControlBlock(vp
);
206 if ( minEOF
> filePtr
->fcbEOF
)
208 bytesToAdd
= minEOF
- filePtr
->fcbEOF
;
210 if (bytesToAdd
< filePtr
->fcbClmpSize
)
211 bytesToAdd
= filePtr
->fcbClmpSize
; //XXX why not always be a mutiple of clump size?
215 DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!"));
219 vcb
= FCBTOVCB(filePtr
);
222 * The Extents B-tree can't have overflow extents. ExtendFileC will
223 * return an error if an attempt is made to extend the Extents B-tree
224 * when the resident extents are exhausted.
226 /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */
227 if(H_FILEID(filePtr
) != kHFSExtentsFileID
)
230 /* lock extents b-tree (also protects volume bitmap) */
231 retval
= hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_EXCLUSIVE
, p
);
236 (void) BTGetInformation(filePtr
, 0, &btInfo
);
239 * The b-tree code expects nodes to be contiguous. So when
240 * the allocation block size is less than the b-tree node
241 * size, we need to force disk allocations to be contiguous.
243 if (vcb
->blockSize
>= btInfo
.nodeSize
) {
246 /* Ensure that all b-tree nodes are contiguous on disk */
247 extendFlags
= kEFAllMask
| kEFContigMask
;
250 retval
= ExtendFileC(vcb
, filePtr
, bytesToAdd
, 0, extendFlags
, &actualBytesAdded
);
252 if(H_FILEID(filePtr
) != kHFSExtentsFileID
)
253 (void) hfs_metafilelocking(VTOHFS(vp
), kHFSExtentsFileID
, LK_RELEASE
, p
);
258 if (actualBytesAdded
< bytesToAdd
)
259 DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!"));
261 filePtr
->fcbEOF
= filePtr
->fcbPLen
;
263 retval
= ClearBTNodes(vp
, btInfo
.nodeSize
, filePtr
->fcbEOF
- actualBytesAdded
, actualBytesAdded
);
268 * Update the Alternate MDB or Alternate VolumeHeader
270 if ((H_FILEID(filePtr
) == kHFSExtentsFileID
) ||
271 (H_FILEID(filePtr
) == kHFSCatalogFileID
) ||
272 (H_FILEID(filePtr
) == kHFSAttributesFileID
)
275 if (vcb
->vcbSigWord
== kHFSPlusSigWord
) {
276 retval
= hfs_flushvolumeheader(VCBTOHFS(vcb
), 0);
278 retval
= hfs_flushMDB(VCBTOHFS(vcb
), 0);
281 retval
= FlushAlternate(vcb
);
290 FlushAlternate( ExtendedVCB
*vcb
)
292 struct hfsmount
*hfsmp
= VCBTOHFS(vcb
);
293 struct vnode
*dev_vp
= hfsmp
->hfs_devvp
;
294 struct buf
*pri_bp
= NULL
;
295 struct buf
*alt_bp
= NULL
;
301 sectorsize
= hfsmp
->hfs_phys_block_size
;
302 priIDSector
= (vcb
->hfsPlusIOPosOffset
/ sectorsize
) +
303 HFS_PRI_SECTOR(sectorsize
);
305 altIDSector
= (vcb
->hfsPlusIOPosOffset
/ sectorsize
) +
306 HFS_ALT_SECTOR(sectorsize
, hfsmp
->hfs_phys_block_count
);
308 /* Get the main MDB/VolumeHeader block */
309 result
= meta_bread(dev_vp
, priIDSector
, sectorsize
, NOCRED
, &pri_bp
);
313 /* Get the alternate MDB/VolumeHeader block */
314 result
= meta_bread(dev_vp
, altIDSector
, sectorsize
, NOCRED
, &alt_bp
);
318 bcopy(pri_bp
->b_data
+ HFS_PRI_OFFSET(sectorsize
),
319 alt_bp
->b_data
+ HFS_ALT_OFFSET(sectorsize
), kMDBSize
);
321 result
= VOP_BWRITE(alt_bp
);
334 * Clear out (zero) new b-tree nodes on disk.
337 ClearBTNodes(struct vnode
*vp
, long blksize
, off_t offset
, off_t amount
)
339 struct buf
*bp
= NULL
;
343 blk
= offset
/ blksize
;
344 blkcnt
= amount
/ blksize
;
347 bp
= getblk(vp
, blk
, blksize
, 0, 0, BLK_META
);
350 bzero((char *)bp
->b_data
, blksize
);
351 bp
->b_flags
|= (B_DIRTY
| B_AGE
);
353 /* wait/yield every 32 blocks so we don't hog all the buffers */