]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_btreeio.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_btreeio.c
CommitLineData
1c79356b 1/*
9bccf70c 2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
1c79356b
A
22
23#include <sys/param.h>
24#include <sys/systm.h>
25#include <sys/buf.h>
9bccf70c 26#include <sys/kernel.h>
1c79356b
A
27#include <sys/mount.h>
28#include <sys/vnode.h>
29
30
31#include "hfs.h"
9bccf70c 32#include "hfs_cnode.h"
1c79356b
A
33#include "hfs_dbg.h"
34#include "hfs_endian.h"
35
36#include "hfscommon/headers/FileMgrInternal.h"
37#include "hfscommon/headers/BTreesPrivate.h"
38
39#define FORCESYNCBTREEWRITES 0
40
1c79356b
A
41
42static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount);
43
44
9bccf70c 45__private_extern__
1c79356b
A
46OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount)
47{
48 BTreeControlBlockPtr bTreePtr;
49
50 DBG_ASSERT(vp != NULL);
1c79356b
A
51 DBG_ASSERT(blockSize >= kMinNodeSize);
52 if (blockSize > MAXBSIZE )
53 return (fsBTBadNodeSize);
54
9bccf70c 55 bTreePtr = (BTreeControlBlockPtr)VTOF(vp)->fcbBTCBPtr;
1c79356b
A
56 bTreePtr->nodeSize = blockSize;
57
58 return (E_NONE);
59}
60
61
9bccf70c 62__private_extern__
1c79356b
A
63OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block)
64{
65 OSStatus retval = E_NONE;
66 struct buf *bp = NULL;
67
9bccf70c
A
68 if (options & kGetEmptyBlock)
69 bp = getblk(vp, blockNum, block->blockSize, 0, 0, BLK_META);
70 else
71 retval = meta_bread(vp, blockNum, block->blockSize, NOCRED, &bp);
1c79356b
A
72
73 DBG_ASSERT(bp != NULL);
74 DBG_ASSERT(bp->b_data != NULL);
75 DBG_ASSERT(bp->b_bcount == block->blockSize);
76 DBG_ASSERT(bp->b_lblkno == blockNum);
77
78 if (bp == NULL)
79 retval = -1; //XXX need better error
80
81 if (retval == E_NONE) {
82 block->blockHeader = bp;
d52fe63f 83 block->buffer = bp->b_data;
1c79356b
A
84 block->blockReadFromDisk = (bp->b_flags & B_CACHE) == 0; /* not found in cache ==> came from disk */
85
86#if BYTE_ORDER == LITTLE_ENDIAN
87 /* Endian swap B-Tree node (only if it's a valid block) */
88 if (!(options & kGetEmptyBlock)) {
89 /* This happens when we first open the b-tree, we might not have all the node data on hand */
90 if ((((BTNodeDescriptor *)block->buffer)->kind == kBTHeaderNode) &&
91 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize != bp->b_bcount) &&
92 (SWAP_BE16 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize) != bp->b_bcount)) {
93
94 /* Don't swap the descriptors at all, we don't care (this block will be invalidated) */
9bccf70c 95 SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), VTOC(vp)->c_fileid, 3);
1c79356b
A
96
97 /* The node needs swapping */
98 } else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) == 0x0e00) {
9bccf70c 99 SWAP_BT_NODE (block, ISHFSPLUS(VTOVCB(vp)), VTOC(vp)->c_fileid, 0);
1c79356b
A
100#if 0
101 /* The node is not already in native byte order, hence corrupt */
102 } else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) != 0x000e) {
103 panic ("%s Corrupt B-Tree node detected!\n", "GetBTreeBlock:");
104#endif
105 }
106 }
107#endif
108 } else {
109 if (bp)
110 brelse(bp);
111 block->blockHeader = NULL;
112 block->buffer = NULL;
113 }
114
115 return (retval);
116}
117
118
9bccf70c 119__private_extern__
1c79356b
A
120OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options)
121{
9bccf70c 122 extern int bdwrite_internal(struct buf *, int);
1c79356b
A
123 OSStatus retval = E_NONE;
124 struct buf *bp = NULL;
125
126 bp = (struct buf *) blockPtr->blockHeader;
127
128 if (bp == NULL) {
1c79356b
A
129 retval = -1;
130 goto exit;
131 }
132
133 if (options & kTrashBlock) {
134 bp->b_flags |= B_INVAL;
135 brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
136 } else {
137 if (options & kForceWriteBlock) {
1c79356b
A
138 retval = VOP_BWRITE(bp);
139 } else if (options & kMarkBlockDirty) {
1c79356b
A
140#if FORCESYNCBTREEWRITES
141 VOP_BWRITE(bp);
142#else
9bccf70c
A
143 if (options & kLockTransaction) {
144 /*
145 *
146 * Set the B_LOCKED flag and unlock the buffer, causing brelse to move
147 * the buffer onto the LOCKED free list. This is necessary, otherwise
148 * getnewbuf() would try to reclaim the buffers using bawrite, which
149 * isn't going to work.
150 *
151 */
152 extern int count_lock_queue __P((void));
153 /* Don't hog all the buffers... */
154 if (count_lock_queue() > kMaxLockedMetaBuffers) {
155 hfs_btsync(vp, HFS_SYNCTRANS);
156 /* Rollback sync time to cause a sync on lock release... */
157 (void) BTSetLastSync(VTOF(vp), time.tv_sec - (kMaxSecsForFsync + 1));
158 }
159 bp->b_flags |= B_LOCKED;
160 }
161 /*
162 * Delay-write this block.
163 * If the maximum delayed buffers has been exceeded then
164 * free up some buffers and fall back to an asynchronous write.
165 */
166 if (bdwrite_internal(bp, 1) != 0) {
167 hfs_btsync(vp, 0);
168 /* Rollback sync time to cause a sync on lock release... */
169 (void) BTSetLastSync(VTOF(vp), time.tv_sec - (kMaxSecsForFsync + 1));
170 bp->b_flags &= ~B_LOCKED;
171 bawrite(bp);
172 }
1c79356b
A
173
174#endif
175 } else {
176 brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
177 };
178 };
179
180exit:
181 return (retval);
182}
183
184
9bccf70c 185__private_extern__
1c79356b
A
186OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF)
187{
188#pragma unused (maxEOF)
189
190 OSStatus retval;
191 UInt64 actualBytesAdded;
192 UInt64 bytesToAdd;
193 UInt32 extendFlags;
9bccf70c
A
194 u_int32_t startAllocation;
195 u_int32_t fileblocks;
1c79356b
A
196 BTreeInfoRec btInfo;
197 ExtendedVCB *vcb;
198 FCB *filePtr;
199 struct proc *p = NULL;
200
201
202 filePtr = GetFileControlBlock(vp);
203
204 if ( minEOF > filePtr->fcbEOF )
205 {
206 bytesToAdd = minEOF - filePtr->fcbEOF;
207
9bccf70c
A
208 if (bytesToAdd < filePtr->ff_clumpsize)
209 bytesToAdd = filePtr->ff_clumpsize; //XXX why not always be a mutiple of clump size?
1c79356b
A
210 }
211 else
212 {
1c79356b
A
213 return -1;
214 }
215
9bccf70c 216 vcb = VTOVCB(vp);
1c79356b
A
217
218 /*
219 * The Extents B-tree can't have overflow extents. ExtendFileC will
220 * return an error if an attempt is made to extend the Extents B-tree
221 * when the resident extents are exhausted.
222 */
223 /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */
9bccf70c 224 if(VTOC(vp)->c_fileid != kHFSExtentsFileID)
1c79356b
A
225 {
226 p = current_proc();
227 /* lock extents b-tree (also protects volume bitmap) */
228 retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p);
229 if (retval)
230 return (retval);
231 }
232
233 (void) BTGetInformation(filePtr, 0, &btInfo);
234
235 /*
9bccf70c 236 * The b-tree code expects nodes to be contiguous. So when
1c79356b 237 * the allocation block size is less than the b-tree node
9bccf70c
A
238 * size, we need to force disk allocations to be contiguous.
239 */
1c79356b
A
240 if (vcb->blockSize >= btInfo.nodeSize) {
241 extendFlags = 0;
242 } else {
243 /* Ensure that all b-tree nodes are contiguous on disk */
244 extendFlags = kEFAllMask | kEFContigMask;
245 }
246
9bccf70c
A
247 fileblocks = filePtr->ff_blocks;
248 startAllocation = vcb->nextAllocation;
1c79356b 249
9bccf70c 250 retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0, extendFlags, &actualBytesAdded);
1c79356b 251
9bccf70c
A
252 /*
253 * If a new extent was added then move the roving allocator
254 * reference forward by the current b-tree file size so
255 * there's plenty of room to grow.
256 */
257 if ((retval == 0) &&
258 (vcb->nextAllocation > startAllocation) &&
259 ((vcb->nextAllocation + fileblocks) < vcb->totalBlocks)) {
260 vcb->nextAllocation += fileblocks;
261 }
262
263 if(VTOC(vp)->c_fileid != kHFSExtentsFileID) {
264 /*
265 * Get any extents overflow b-tree changes to disk ASAP!
266 */
267 if (retval == 0) {
268 (void) BTFlushPath(VTOF(vcb->extentsRefNum));
269 (void) VOP_FSYNC(vcb->extentsRefNum, NOCRED, MNT_WAIT, p);
270 }
271 (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
272 }
1c79356b
A
273 if (retval)
274 return (retval);
1c79356b 275
9bccf70c 276 filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize;
1c79356b
A
277
278 retval = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded);
279 if (retval)
280 return (retval);
281
282 /*
283 * Update the Alternate MDB or Alternate VolumeHeader
284 */
9bccf70c
A
285 if ((VTOC(vp)->c_fileid == kHFSExtentsFileID) ||
286 (VTOC(vp)->c_fileid == kHFSCatalogFileID) ||
287 (VTOC(vp)->c_fileid == kHFSAttributesFileID)
1c79356b
A
288 ) {
289 MarkVCBDirty( vcb );
9bccf70c 290 retval = hfs_flushvolumeheader(VCBTOHFS(vcb), MNT_WAIT, HFS_ALTFLUSH);
1c79356b
A
291 }
292
293 return retval;
294}
295
296
1c79356b
A
297/*
298 * Clear out (zero) new b-tree nodes on disk.
299 */
300static int
301ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount)
302{
303 struct buf *bp = NULL;
304 daddr_t blk;
305 daddr_t blkcnt;
306
307 blk = offset / blksize;
308 blkcnt = amount / blksize;
309
310 while (blkcnt > 0) {
311 bp = getblk(vp, blk, blksize, 0, 0, BLK_META);
312 if (bp == NULL)
313 continue;
314 bzero((char *)bp->b_data, blksize);
9bccf70c 315 bp->b_flags |= B_AGE;
1c79356b
A
316
317 /* wait/yield every 32 blocks so we don't hog all the buffers */
318 if ((blk % 32) == 0)
319 VOP_BWRITE(bp);
320 else
321 bawrite(bp);
322 --blkcnt;
323 ++blk;
324 }
325
326 return (0);
327}