]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_btreeio.c
xnu-201.19.3.tar.gz
[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,
88 IOBLKNOFORBLK(blockNum, VTOHFS(vp)->hfs_phys_block_size),
89 IOBYTECCNTFORBLK(blockNum, block->blockSize, VTOHFS(vp)->hfs_phys_block_size),
90 0,
91 0,
92 BLK_META);
93 else
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),
97 NOCRED,
98 &bp);
99
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);
104
105 if (bp == NULL)
106 retval = -1; //XXX need better error
107
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 */
112
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)) {
120
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);
123
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);
127 #if 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:");
131 #endif
132 }
133 }
134 #endif
135 } else {
136 if (bp)
137 brelse(bp);
138 block->blockHeader = NULL;
139 block->buffer = NULL;
140 }
141
142 return (retval);
143 }
144
145
146 OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options)
147 {
148 OSStatus retval = E_NONE;
149 struct buf *bp = NULL;
150
151 bp = (struct buf *) blockPtr->blockHeader;
152
153 if (bp == NULL) {
154 DBG_TREE(("ReleaseBlockProc: blockHeader is zero!\n"));
155 retval = -1;
156 goto exit;
157 }
158
159 if (options & kTrashBlock) {
160 bp->b_flags |= B_INVAL;
161 brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
162 } else {
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
169 VOP_BWRITE(bp);
170 #else
171 if (options & kLockTransaction) {
172 /*
173 *
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.
178 *
179 */
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;
185 };
186 bdwrite(bp);
187
188 #endif
189 } else {
190 brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
191 };
192 };
193
194 exit:
195 return (retval);
196 }
197
198
199 OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF)
200 {
201 #pragma unused (maxEOF)
202
203 OSStatus retval;
204 UInt64 actualBytesAdded;
205 UInt64 bytesToAdd;
206 UInt32 extendFlags;
207 BTreeInfoRec btInfo;
208 ExtendedVCB *vcb;
209 FCB *filePtr;
210 struct proc *p = NULL;
211
212
213 filePtr = GetFileControlBlock(vp);
214
215 if ( minEOF > filePtr->fcbEOF )
216 {
217 bytesToAdd = minEOF - filePtr->fcbEOF;
218
219 if (bytesToAdd < filePtr->fcbClmpSize)
220 bytesToAdd = filePtr->fcbClmpSize; //XXX why not always be a mutiple of clump size?
221 }
222 else
223 {
224 DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!"));
225 return -1;
226 }
227
228 vcb = FCBTOVCB(filePtr);
229
230 /*
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.
234 */
235 /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */
236 if(H_FILEID(filePtr) != kHFSExtentsFileID)
237 {
238 p = current_proc();
239 /* lock extents b-tree (also protects volume bitmap) */
240 retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p);
241 if (retval)
242 return (retval);
243 }
244
245 (void) BTGetInformation(filePtr, 0, &btInfo);
246
247 /*
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.
251 */
252 if (vcb->blockSize >= btInfo.nodeSize) {
253 extendFlags = 0;
254 } else {
255 /* Ensure that all b-tree nodes are contiguous on disk */
256 extendFlags = kEFAllMask | kEFContigMask;
257 }
258
259 retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0, extendFlags, &actualBytesAdded);
260
261 if(H_FILEID(filePtr) != kHFSExtentsFileID)
262 (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
263
264 if (retval)
265 return (retval);
266
267 if (actualBytesAdded < bytesToAdd)
268 DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!"));
269
270 filePtr->fcbEOF = filePtr->fcbPLen;
271
272 retval = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded);
273 if (retval)
274 return (retval);
275
276 /*
277 * Update the Alternate MDB or Alternate VolumeHeader
278 */
279 if ((H_FILEID(filePtr) == kHFSExtentsFileID) ||
280 (H_FILEID(filePtr) == kHFSCatalogFileID) ||
281 (H_FILEID(filePtr) == kHFSAttributesFileID)
282 ) {
283 MarkVCBDirty( vcb );
284 if (vcb->vcbSigWord == kHFSPlusSigWord) {
285 retval = hfs_flushvolumeheader(VCBTOHFS(vcb), 0);
286 } else {
287 retval = hfs_flushMDB(VCBTOHFS(vcb), 0);
288 }
289 if (retval == 0) {
290 retval = FlushAlternate(vcb);
291 }
292 }
293
294 return retval;
295 }
296
297
298 static OSStatus
299 FlushAlternate( ExtendedVCB *vcb )
300 {
301 void *maindata;
302 void *altdata;
303 int result;
304
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);
310
311 /* Get the alternate MDB/VolumeHeader block */
312 result = GetBlock_glue( gbDefault, vcb->altIDSector,
313 (Ptr *)&altdata, kNoFileReference, vcb );
314
315 if (result == 0) {
316 bcopy(maindata, altdata, kMDBSize);
317
318 result = RelBlock_glue( (Ptr)altdata, rbWriteMask );
319 }
320
321 (void) RelBlock_glue( (Ptr)maindata, rbFreeMask );
322
323 return (result);
324 }
325
326
327 /*
328 * Clear out (zero) new b-tree nodes on disk.
329 */
330 static int
331 ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount)
332 {
333 struct buf *bp = NULL;
334 daddr_t blk;
335 daddr_t blkcnt;
336
337 blk = offset / blksize;
338 blkcnt = amount / blksize;
339
340 while (blkcnt > 0) {
341 bp = getblk(vp, blk, blksize, 0, 0, BLK_META);
342 if (bp == NULL)
343 continue;
344 bzero((char *)bp->b_data, blksize);
345 bp->b_flags |= (B_DIRTY | B_AGE);
346
347 /* wait/yield every 32 blocks so we don't hog all the buffers */
348 if ((blk % 32) == 0)
349 VOP_BWRITE(bp);
350 else
351 bawrite(bp);
352 --blkcnt;
353 ++blk;
354 }
355
356 return (0);
357 }