]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_btreeio.c
2bb3c7695efdc4d1a68abcb7c481ad66bf506453
[apple/xnu.git] / bsd / hfs / hfs_btreeio.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
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 */
22 /* @(#)hfs_btreeio.c
23 *
24 * (c) 1998, 2000 Apple Computer, Inc. All Rights Reserved
25 *
26 * hfs_btreeio.c -- I/O Routines for the HFS B-tree files.
27 *
28 * HISTORY
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.
37 *
38 */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/buf.h>
43 #include <sys/mount.h>
44 #include <sys/vnode.h>
45
46
47 #include "hfs.h"
48 #include "hfs_dbg.h"
49 #include "hfs_endian.h"
50
51 #include "hfscommon/headers/FileMgrInternal.h"
52 #include "hfscommon/headers/BTreesPrivate.h"
53
54 #define FORCESYNCBTREEWRITES 0
55
56 static OSStatus FlushAlternate( ExtendedVCB *vcb );
57
58 static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount);
59
60
61 OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount)
62 {
63 BTreeControlBlockPtr bTreePtr;
64
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);
71
72 DBG_TREE(("SetBlockSizeProc: blockSize=%ld for file %ld\n", blockSize, H_FILEID(VTOH(vp))));
73
74 bTreePtr = (BTreeControlBlockPtr)(VTOH(vp)->fcbBTCBPtr);
75 bTreePtr->nodeSize = blockSize;
76
77 return (E_NONE);
78 }
79
80
81 OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block)
82 {
83 OSStatus retval = E_NONE;
84 struct buf *bp = NULL;
85
86 if (options & kGetEmptyBlock)
87 bp = getblk(vp, blockNum, block->blockSize, 0, 0, BLK_META);
88 else
89 retval = meta_bread(vp, blockNum, block->blockSize, NOCRED, &bp);
90
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);
95
96 if (bp == NULL)
97 retval = -1; //XXX need better error
98
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 */
103
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)) {
111
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);
114
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);
118 #if 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:");
122 #endif
123 }
124 }
125 #endif
126 } else {
127 if (bp)
128 brelse(bp);
129 block->blockHeader = NULL;
130 block->buffer = NULL;
131 }
132
133 return (retval);
134 }
135
136
137 OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options)
138 {
139 OSStatus retval = E_NONE;
140 struct buf *bp = NULL;
141
142 bp = (struct buf *) blockPtr->blockHeader;
143
144 if (bp == NULL) {
145 DBG_TREE(("ReleaseBlockProc: blockHeader is zero!\n"));
146 retval = -1;
147 goto exit;
148 }
149
150 if (options & kTrashBlock) {
151 bp->b_flags |= B_INVAL;
152 brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
153 } else {
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
160 VOP_BWRITE(bp);
161 #else
162 if (options & kLockTransaction) {
163 /*
164 *
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.
169 *
170 */
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;
176 };
177 bdwrite(bp);
178
179 #endif
180 } else {
181 brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
182 };
183 };
184
185 exit:
186 return (retval);
187 }
188
189
190 OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF)
191 {
192 #pragma unused (maxEOF)
193
194 OSStatus retval;
195 UInt64 actualBytesAdded;
196 UInt64 bytesToAdd;
197 UInt32 extendFlags;
198 BTreeInfoRec btInfo;
199 ExtendedVCB *vcb;
200 FCB *filePtr;
201 struct proc *p = NULL;
202
203
204 filePtr = GetFileControlBlock(vp);
205
206 if ( minEOF > filePtr->fcbEOF )
207 {
208 bytesToAdd = minEOF - filePtr->fcbEOF;
209
210 if (bytesToAdd < filePtr->fcbClmpSize)
211 bytesToAdd = filePtr->fcbClmpSize; //XXX why not always be a mutiple of clump size?
212 }
213 else
214 {
215 DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!"));
216 return -1;
217 }
218
219 vcb = FCBTOVCB(filePtr);
220
221 /*
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.
225 */
226 /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */
227 if(H_FILEID(filePtr) != kHFSExtentsFileID)
228 {
229 p = current_proc();
230 /* lock extents b-tree (also protects volume bitmap) */
231 retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p);
232 if (retval)
233 return (retval);
234 }
235
236 (void) BTGetInformation(filePtr, 0, &btInfo);
237
238 /*
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.
242 */
243 if (vcb->blockSize >= btInfo.nodeSize) {
244 extendFlags = 0;
245 } else {
246 /* Ensure that all b-tree nodes are contiguous on disk */
247 extendFlags = kEFAllMask | kEFContigMask;
248 }
249
250 retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0, extendFlags, &actualBytesAdded);
251
252 if(H_FILEID(filePtr) != kHFSExtentsFileID)
253 (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
254
255 if (retval)
256 return (retval);
257
258 if (actualBytesAdded < bytesToAdd)
259 DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!"));
260
261 filePtr->fcbEOF = filePtr->fcbPLen;
262
263 retval = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded);
264 if (retval)
265 return (retval);
266
267 /*
268 * Update the Alternate MDB or Alternate VolumeHeader
269 */
270 if ((H_FILEID(filePtr) == kHFSExtentsFileID) ||
271 (H_FILEID(filePtr) == kHFSCatalogFileID) ||
272 (H_FILEID(filePtr) == kHFSAttributesFileID)
273 ) {
274 MarkVCBDirty( vcb );
275 if (vcb->vcbSigWord == kHFSPlusSigWord) {
276 retval = hfs_flushvolumeheader(VCBTOHFS(vcb), 0);
277 } else {
278 retval = hfs_flushMDB(VCBTOHFS(vcb), 0);
279 }
280 if (retval == 0) {
281 retval = FlushAlternate(vcb);
282 }
283 }
284
285 return retval;
286 }
287
288
289 static OSStatus
290 FlushAlternate( ExtendedVCB *vcb )
291 {
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;
296 int sectorsize;
297 u_long priIDSector;
298 u_long altIDSector;
299 int result;
300
301 sectorsize = hfsmp->hfs_phys_block_size;
302 priIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) +
303 HFS_PRI_SECTOR(sectorsize);
304
305 altIDSector = (vcb->hfsPlusIOPosOffset / sectorsize) +
306 HFS_ALT_SECTOR(sectorsize, hfsmp->hfs_phys_block_count);
307
308 /* Get the main MDB/VolumeHeader block */
309 result = meta_bread(dev_vp, priIDSector, sectorsize, NOCRED, &pri_bp);
310 if (result)
311 goto exit;
312
313 /* Get the alternate MDB/VolumeHeader block */
314 result = meta_bread(dev_vp, altIDSector, sectorsize, NOCRED, &alt_bp);
315 if (result)
316 goto exit;
317
318 bcopy(pri_bp->b_data + HFS_PRI_OFFSET(sectorsize),
319 alt_bp->b_data + HFS_ALT_OFFSET(sectorsize), kMDBSize);
320
321 result = VOP_BWRITE(alt_bp);
322 alt_bp = NULL;
323 exit:
324 if (alt_bp)
325 brelse(alt_bp);
326 if (pri_bp)
327 brelse(pri_bp);
328
329 return (result);
330 }
331
332
333 /*
334 * Clear out (zero) new b-tree nodes on disk.
335 */
336 static int
337 ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount)
338 {
339 struct buf *bp = NULL;
340 daddr_t blk;
341 daddr_t blkcnt;
342
343 blk = offset / blksize;
344 blkcnt = amount / blksize;
345
346 while (blkcnt > 0) {
347 bp = getblk(vp, blk, blksize, 0, 0, BLK_META);
348 if (bp == NULL)
349 continue;
350 bzero((char *)bp->b_data, blksize);
351 bp->b_flags |= (B_DIRTY | B_AGE);
352
353 /* wait/yield every 32 blocks so we don't hog all the buffers */
354 if ((blk % 32) == 0)
355 VOP_BWRITE(bp);
356 else
357 bawrite(bp);
358 --blkcnt;
359 ++blk;
360 }
361
362 return (0);
363 }