]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_btreeio.c
xnu-123.5.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_btreeio.c
CommitLineData
1c79356b
A
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
56static OSStatus FlushAlternate( ExtendedVCB *vcb );
57
58static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount);
59
60
61OSStatus 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
81OSStatus 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
146OSStatus 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 *
175 * Set the B_LOCKED flag and unlock the buffer, causing brelse to move
176 * the buffer onto the LOCKED free list. This is necessary, otherwise
177 * getnewbuf() would try to reclaim the buffers using bawrite, which
178 * isn't going to work.
179 *
180 */
181 bp->b_flags |= B_LOCKED;
182 };
183 bdwrite(bp);
184
185#endif
186 } else {
187 brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
188 };
189 };
190
191exit:
192 return (retval);
193}
194
195
196OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF)
197{
198#pragma unused (maxEOF)
199
200 OSStatus retval;
201 UInt64 actualBytesAdded;
202 UInt64 bytesToAdd;
203 UInt32 extendFlags;
204 BTreeInfoRec btInfo;
205 ExtendedVCB *vcb;
206 FCB *filePtr;
207 struct proc *p = NULL;
208
209
210 filePtr = GetFileControlBlock(vp);
211
212 if ( minEOF > filePtr->fcbEOF )
213 {
214 bytesToAdd = minEOF - filePtr->fcbEOF;
215
216 if (bytesToAdd < filePtr->fcbClmpSize)
217 bytesToAdd = filePtr->fcbClmpSize; //XXX why not always be a mutiple of clump size?
218 }
219 else
220 {
221 DBG_TREE((" ExtendBTreeFile: minEOF is smaller than current size!"));
222 return -1;
223 }
224
225 vcb = FCBTOVCB(filePtr);
226
227 /*
228 * The Extents B-tree can't have overflow extents. ExtendFileC will
229 * return an error if an attempt is made to extend the Extents B-tree
230 * when the resident extents are exhausted.
231 */
232 /* XXX warning - this can leave the volume bitmap unprotected during ExtendFileC call */
233 if(H_FILEID(filePtr) != kHFSExtentsFileID)
234 {
235 p = current_proc();
236 /* lock extents b-tree (also protects volume bitmap) */
237 retval = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE, p);
238 if (retval)
239 return (retval);
240 }
241
242 (void) BTGetInformation(filePtr, 0, &btInfo);
243
244 /*
245 * The b-tree code expects nodes to be contiguous. So when
246 * the allocation block size is less than the b-tree node
247 * size, we need to force disk allocations to be contiguous.
248 */
249 if (vcb->blockSize >= btInfo.nodeSize) {
250 extendFlags = 0;
251 } else {
252 /* Ensure that all b-tree nodes are contiguous on disk */
253 extendFlags = kEFAllMask | kEFContigMask;
254 }
255
256 retval = ExtendFileC(vcb, filePtr, bytesToAdd, extendFlags, &actualBytesAdded );
257
258 if(H_FILEID(filePtr) != kHFSExtentsFileID)
259 (void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
260
261 if (retval)
262 return (retval);
263
264 if (actualBytesAdded < bytesToAdd)
265 DBG_TREE((" ExtendBTreeFile: actualBytesAdded < bytesToAdd!"));
266
267 filePtr->fcbEOF = filePtr->fcbPLen;
268
269 retval = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded);
270 if (retval)
271 return (retval);
272
273 /*
274 * Update the Alternate MDB or Alternate VolumeHeader
275 */
276 if ((H_FILEID(filePtr) == kHFSExtentsFileID) ||
277 (H_FILEID(filePtr) == kHFSCatalogFileID) ||
278 (H_FILEID(filePtr) == kHFSAttributesFileID)
279 ) {
280 MarkVCBDirty( vcb );
281 if (vcb->vcbSigWord == kHFSPlusSigWord) {
282 retval = hfs_flushvolumeheader(VCBTOHFS(vcb), 0);
283 } else {
284 retval = hfs_flushMDB(VCBTOHFS(vcb), 0);
285 }
286 if (retval == 0) {
287 retval = FlushAlternate(vcb);
288 }
289 }
290
291 return retval;
292}
293
294
295static OSStatus
296FlushAlternate( ExtendedVCB *vcb )
297{
298 void *maindata;
299 void *altdata;
300 int result;
301
302 /* Get the main MDB/VolumeHeader block */
303 result = GetBlock_glue(gbDefault,
304 (vcb->hfsPlusIOPosOffset / kHFSBlockSize) + kMasterDirectoryBlock,
305 (Ptr *)&maindata, kNoFileReference, vcb);
306 if (result) return (result);
307
308 /* Get the alternate MDB/VolumeHeader block */
309 result = GetBlock_glue( gbDefault, vcb->altIDSector,
310 (Ptr *)&altdata, kNoFileReference, vcb );
311
312 if (result == 0) {
313 bcopy(maindata, altdata, kMDBSize);
314
315 result = RelBlock_glue( (Ptr)altdata, rbWriteMask );
316 }
317
318 (void) RelBlock_glue( (Ptr)maindata, rbFreeMask );
319
320 return (result);
321}
322
323
324/*
325 * Clear out (zero) new b-tree nodes on disk.
326 */
327static int
328ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount)
329{
330 struct buf *bp = NULL;
331 daddr_t blk;
332 daddr_t blkcnt;
333
334 blk = offset / blksize;
335 blkcnt = amount / blksize;
336
337 while (blkcnt > 0) {
338 bp = getblk(vp, blk, blksize, 0, 0, BLK_META);
339 if (bp == NULL)
340 continue;
341 bzero((char *)bp->b_data, blksize);
342 bp->b_flags |= (B_DIRTY | B_AGE);
343
344 /* wait/yield every 32 blocks so we don't hog all the buffers */
345 if ((blk % 32) == 0)
346 VOP_BWRITE(bp);
347 else
348 bawrite(bp);
349 --blkcnt;
350 ++blk;
351 }
352
353 return (0);
354}