]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
91447636 | 2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
37839358 A |
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. | |
1c79356b | 11 | * |
37839358 A |
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 | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
37839358 A |
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. | |
1c79356b A |
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> |
91447636 | 27 | #include <sys/malloc.h> |
1c79356b A |
28 | #include <sys/mount.h> |
29 | #include <sys/vnode.h> | |
30 | ||
31 | ||
32 | #include "hfs.h" | |
9bccf70c | 33 | #include "hfs_cnode.h" |
1c79356b A |
34 | #include "hfs_dbg.h" |
35 | #include "hfs_endian.h" | |
36 | ||
37 | #include "hfscommon/headers/FileMgrInternal.h" | |
38 | #include "hfscommon/headers/BTreesPrivate.h" | |
39 | ||
40 | #define FORCESYNCBTREEWRITES 0 | |
41 | ||
1c79356b A |
42 | |
43 | static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount); | |
44 | ||
45 | ||
9bccf70c | 46 | __private_extern__ |
1c79356b A |
47 | OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount) |
48 | { | |
49 | BTreeControlBlockPtr bTreePtr; | |
50 | ||
51 | DBG_ASSERT(vp != NULL); | |
1c79356b A |
52 | DBG_ASSERT(blockSize >= kMinNodeSize); |
53 | if (blockSize > MAXBSIZE ) | |
54 | return (fsBTBadNodeSize); | |
55 | ||
9bccf70c | 56 | bTreePtr = (BTreeControlBlockPtr)VTOF(vp)->fcbBTCBPtr; |
1c79356b A |
57 | bTreePtr->nodeSize = blockSize; |
58 | ||
59 | return (E_NONE); | |
60 | } | |
61 | ||
62 | ||
9bccf70c | 63 | __private_extern__ |
1c79356b A |
64 | OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block) |
65 | { | |
66 | OSStatus retval = E_NONE; | |
67 | struct buf *bp = NULL; | |
68 | ||
91447636 A |
69 | if (options & kGetEmptyBlock) { |
70 | daddr64_t blkno; | |
71 | off_t offset; | |
1c79356b | 72 | |
91447636 A |
73 | offset = (daddr64_t)blockNum * (daddr64_t)block->blockSize; |
74 | bp = buf_getblk(vp, (daddr64_t)blockNum, block->blockSize, 0, 0, BLK_META); | |
75 | if (bp && | |
76 | VNOP_BLOCKMAP(vp, offset, block->blockSize, &blkno, NULL, NULL, 0, NULL) == 0) { | |
77 | buf_setblkno(bp, blkno); | |
78 | } | |
79 | } else { | |
80 | retval = buf_meta_bread(vp, (daddr64_t)blockNum, block->blockSize, NOCRED, &bp); | |
81 | } | |
1c79356b A |
82 | if (bp == NULL) |
83 | retval = -1; //XXX need better error | |
84 | ||
85 | if (retval == E_NONE) { | |
86 | block->blockHeader = bp; | |
91447636 | 87 | block->buffer = (char *)buf_dataptr(bp); |
3a60a9f5 | 88 | block->blockNum = buf_lblkno(bp); |
91447636 | 89 | block->blockReadFromDisk = (buf_fromcache(bp) == 0); /* not found in cache ==> came from disk */ |
1c79356b | 90 | |
b4c24cb9 A |
91 | // XXXdbg |
92 | block->isModified = 0; | |
93 | ||
3a60a9f5 | 94 | /* Check and endian swap B-Tree node (only if it's a valid block) */ |
1c79356b A |
95 | if (!(options & kGetEmptyBlock)) { |
96 | /* This happens when we first open the b-tree, we might not have all the node data on hand */ | |
97 | if ((((BTNodeDescriptor *)block->buffer)->kind == kBTHeaderNode) && | |
91447636 A |
98 | (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize != buf_count(bp)) && |
99 | (SWAP_BE16 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize) != buf_count(bp))) { | |
1c79356b | 100 | |
3a60a9f5 A |
101 | /* |
102 | * Don't swap the node descriptor, record offsets, or other records. | |
103 | * This record will be invalidated and re-read with the correct node | |
104 | * size once the B-tree control block is set up with the node size | |
105 | * from the header record. | |
106 | */ | |
107 | retval = hfs_swap_BTNode (block, vp, kSwapBTNodeHeaderRecordOnly); | |
108 | ||
109 | } else if (block->blockReadFromDisk) { | |
110 | /* | |
111 | * The node was just read from disk, so always swap/check it. | |
112 | * This is necessary on big endian since the test below won't trigger. | |
113 | */ | |
114 | retval = hfs_swap_BTNode (block, vp, kSwapBTNodeBigToHost); | |
1c79356b | 115 | } else if (*((UInt16 *)((char *)block->buffer + (block->blockSize - sizeof (UInt16)))) == 0x0e00) { |
3a60a9f5 A |
116 | /* |
117 | * The node was left in the cache in non-native order, so swap it. | |
118 | * This only happens on little endian, after the node is written | |
119 | * back to disk. | |
120 | */ | |
121 | retval = hfs_swap_BTNode (block, vp, kSwapBTNodeBigToHost); | |
1c79356b | 122 | } |
3a60a9f5 A |
123 | |
124 | /* | |
125 | * If we got an error, then the node is only partially swapped. | |
126 | * We mark the buffer invalid so that the next attempt to get the | |
127 | * node will read it and attempt to swap again, and will notice | |
128 | * the error again. If we didn't do this, the next attempt to get | |
129 | * the node might use the partially swapped node as-is. | |
130 | */ | |
131 | if (retval) | |
132 | buf_markinvalid(bp); | |
1c79356b | 133 | } |
3a60a9f5 A |
134 | } |
135 | ||
136 | if (retval) { | |
1c79356b | 137 | if (bp) |
3a60a9f5 | 138 | buf_brelse(bp); |
1c79356b A |
139 | block->blockHeader = NULL; |
140 | block->buffer = NULL; | |
141 | } | |
142 | ||
143 | return (retval); | |
144 | } | |
145 | ||
146 | ||
b4c24cb9 A |
147 | __private_extern__ |
148 | void ModifyBlockStart(FileReference vp, BlockDescPtr blockPtr) | |
149 | { | |
150 | struct hfsmount *hfsmp = VTOHFS(vp); | |
151 | struct buf *bp = NULL; | |
152 | ||
153 | if (hfsmp->jnl == NULL) { | |
154 | return; | |
155 | } | |
156 | ||
157 | bp = (struct buf *) blockPtr->blockHeader; | |
158 | if (bp == NULL) { | |
159 | panic("ModifyBlockStart: null bp for blockdescptr 0x%x?!?\n", blockPtr); | |
160 | return; | |
161 | } | |
162 | ||
163 | journal_modify_block_start(hfsmp->jnl, bp); | |
164 | blockPtr->isModified = 1; | |
165 | } | |
166 | ||
55e303ae A |
167 | static int |
168 | btree_journal_modify_block_end(struct hfsmount *hfsmp, struct buf *bp) | |
169 | { | |
3a60a9f5 | 170 | int retval; |
91447636 | 171 | struct vnode *vp = buf_vnode(bp); |
55e303ae A |
172 | BlockDescriptor block; |
173 | ||
174 | /* Prepare the block pointer */ | |
175 | block.blockHeader = bp; | |
91447636 | 176 | block.buffer = (char *)buf_dataptr(bp); |
3a60a9f5 | 177 | block.blockNum = buf_lblkno(bp); |
55e303ae | 178 | /* not found in cache ==> came from disk */ |
91447636 A |
179 | block.blockReadFromDisk = (buf_fromcache(bp) == 0); |
180 | block.blockSize = buf_count(bp); | |
55e303ae A |
181 | |
182 | // XXXdbg have to swap the data before it goes in the journal | |
3a60a9f5 A |
183 | retval = hfs_swap_BTNode (&block, vp, kSwapBTNodeHostToBig); |
184 | if (retval) | |
185 | panic("btree_journal_modify_block_end: about to write corrupt node!\n"); | |
55e303ae A |
186 | |
187 | return journal_modify_block_end(hfsmp->jnl, bp); | |
188 | } | |
189 | ||
b4c24cb9 | 190 | |
9bccf70c | 191 | __private_extern__ |
1c79356b A |
192 | OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options) |
193 | { | |
b4c24cb9 | 194 | struct hfsmount *hfsmp = VTOHFS(vp); |
9bccf70c | 195 | extern int bdwrite_internal(struct buf *, int); |
1c79356b A |
196 | OSStatus retval = E_NONE; |
197 | struct buf *bp = NULL; | |
198 | ||
199 | bp = (struct buf *) blockPtr->blockHeader; | |
200 | ||
201 | if (bp == NULL) { | |
1c79356b A |
202 | retval = -1; |
203 | goto exit; | |
204 | } | |
205 | ||
206 | if (options & kTrashBlock) { | |
91447636 A |
207 | buf_markinvalid(bp); |
208 | ||
209 | if (hfsmp->jnl && (buf_flags(bp) & B_LOCKED)) { | |
b4c24cb9 A |
210 | journal_kill_block(hfsmp->jnl, bp); |
211 | } else { | |
91447636 | 212 | buf_brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */ |
b4c24cb9 | 213 | } |
1c79356b A |
214 | } else { |
215 | if (options & kForceWriteBlock) { | |
b4c24cb9 A |
216 | if (hfsmp->jnl) { |
217 | if (blockPtr->isModified == 0) { | |
218 | panic("hfs: releaseblock: modified is 0 but forcewrite set! bp 0x%x\n", bp); | |
219 | } | |
55e303ae A |
220 | |
221 | retval = btree_journal_modify_block_end(hfsmp, bp); | |
b4c24cb9 A |
222 | blockPtr->isModified = 0; |
223 | } else { | |
91447636 | 224 | retval = VNOP_BWRITE(bp); |
b4c24cb9 | 225 | } |
1c79356b | 226 | } else if (options & kMarkBlockDirty) { |
91447636 A |
227 | struct timeval tv; |
228 | microuptime(&tv); | |
b4c24cb9 | 229 | if ((options & kLockTransaction) && hfsmp->jnl == NULL) { |
9bccf70c A |
230 | /* |
231 | * | |
91447636 | 232 | * Set the B_LOCKED flag and unlock the buffer, causing buf_brelse to move |
9bccf70c | 233 | * the buffer onto the LOCKED free list. This is necessary, otherwise |
91447636 | 234 | * getnewbuf() would try to reclaim the buffers using buf_bawrite, which |
9bccf70c A |
235 | * isn't going to work. |
236 | * | |
237 | */ | |
91447636 A |
238 | extern int count_lock_queue(void); |
239 | ||
9bccf70c A |
240 | /* Don't hog all the buffers... */ |
241 | if (count_lock_queue() > kMaxLockedMetaBuffers) { | |
242 | hfs_btsync(vp, HFS_SYNCTRANS); | |
243 | /* Rollback sync time to cause a sync on lock release... */ | |
91447636 | 244 | (void) BTSetLastSync(VTOF(vp), tv.tv_sec - (kMaxSecsForFsync + 1)); |
9bccf70c | 245 | } |
91447636 | 246 | buf_setflags(bp, B_LOCKED); |
b4c24cb9 A |
247 | } |
248 | ||
9bccf70c A |
249 | /* |
250 | * Delay-write this block. | |
251 | * If the maximum delayed buffers has been exceeded then | |
252 | * free up some buffers and fall back to an asynchronous write. | |
253 | */ | |
b4c24cb9 A |
254 | if (hfsmp->jnl) { |
255 | if (blockPtr->isModified == 0) { | |
256 | panic("hfs: releaseblock: modified is 0 but markdirty set! bp 0x%x\n", bp); | |
257 | } | |
55e303ae | 258 | retval = btree_journal_modify_block_end(hfsmp, bp); |
b4c24cb9 A |
259 | blockPtr->isModified = 0; |
260 | } else if (bdwrite_internal(bp, 1) != 0) { | |
9bccf70c A |
261 | hfs_btsync(vp, 0); |
262 | /* Rollback sync time to cause a sync on lock release... */ | |
91447636 A |
263 | (void) BTSetLastSync(VTOF(vp), tv.tv_sec - (kMaxSecsForFsync + 1)); |
264 | ||
265 | buf_clearflags(bp, B_LOCKED); | |
266 | buf_bawrite(bp); | |
9bccf70c | 267 | } |
1c79356b | 268 | } else { |
b4c24cb9 | 269 | // check if we had previously called journal_modify_block_start() |
91447636 | 270 | // on this block and if so, abort it (which will call buf_brelse()). |
b4c24cb9 A |
271 | if (hfsmp->jnl && blockPtr->isModified) { |
272 | // XXXdbg - I don't want to call modify_block_abort() | |
273 | // because I think it may be screwing up the | |
274 | // journal and blowing away a block that has | |
275 | // valid data in it. | |
276 | // | |
277 | // journal_modify_block_abort(hfsmp->jnl, bp); | |
278 | //panic("hfs: releaseblock called for 0x%x but mod_block_start previously called.\n", bp); | |
55e303ae | 279 | btree_journal_modify_block_end(hfsmp, bp); |
b4c24cb9 A |
280 | blockPtr->isModified = 0; |
281 | } else { | |
91447636 | 282 | buf_brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */ |
b4c24cb9 | 283 | } |
1c79356b A |
284 | }; |
285 | }; | |
286 | ||
287 | exit: | |
288 | return (retval); | |
289 | } | |
290 | ||
291 | ||
c0fea474 A |
292 | #define HFS_CLUMP_ADJ_LIMIT (200*1024*1024) |
293 | ||
9bccf70c | 294 | __private_extern__ |
1c79356b A |
295 | OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF) |
296 | { | |
297 | #pragma unused (maxEOF) | |
298 | ||
91447636 | 299 | OSStatus retval = 0, ret = 0; |
b4c24cb9 | 300 | UInt64 actualBytesAdded, origSize; |
1c79356b | 301 | UInt64 bytesToAdd; |
9bccf70c A |
302 | u_int32_t startAllocation; |
303 | u_int32_t fileblocks; | |
1c79356b A |
304 | BTreeInfoRec btInfo; |
305 | ExtendedVCB *vcb; | |
306 | FCB *filePtr; | |
307 | struct proc *p = NULL; | |
91447636 A |
308 | UInt64 trim = 0; |
309 | int lockflags = 0; | |
1c79356b A |
310 | |
311 | filePtr = GetFileControlBlock(vp); | |
312 | ||
313 | if ( minEOF > filePtr->fcbEOF ) | |
314 | { | |
315 | bytesToAdd = minEOF - filePtr->fcbEOF; | |
316 | ||
9bccf70c A |
317 | if (bytesToAdd < filePtr->ff_clumpsize) |
318 | bytesToAdd = filePtr->ff_clumpsize; //XXX why not always be a mutiple of clump size? | |
1c79356b A |
319 | } |
320 | else | |
321 | { | |
1c79356b A |
322 | return -1; |
323 | } | |
324 | ||
9bccf70c | 325 | vcb = VTOVCB(vp); |
c0fea474 A |
326 | |
327 | /* Take past growth into account when extending the catalog file. */ | |
328 | if ((VTOC(vp)->c_fileid == kHFSCatalogFileID) && | |
329 | (bytesToAdd / vcb->blockSize) < filePtr->fcbExtents[0].blockCount) { | |
330 | bytesToAdd = filePtr->fcbExtents[0].blockCount * (UInt64)vcb->blockSize; | |
331 | bytesToAdd = MIN(bytesToAdd, HFS_CLUMP_ADJ_LIMIT); | |
332 | } | |
1c79356b A |
333 | /* |
334 | * The Extents B-tree can't have overflow extents. ExtendFileC will | |
335 | * return an error if an attempt is made to extend the Extents B-tree | |
336 | * when the resident extents are exhausted. | |
337 | */ | |
1c79356b | 338 | |
91447636 A |
339 | /* Protect allocation bitmap and extents overflow file. */ |
340 | lockflags = SFL_BITMAP; | |
341 | if (VTOC(vp)->c_fileid != kHFSExtentsFileID) | |
342 | lockflags |= SFL_EXTENTS; | |
343 | lockflags = hfs_systemfile_lock(vcb, lockflags, HFS_EXCLUSIVE_LOCK); | |
344 | ||
345 | (void) BTGetInformation(filePtr, 0, &btInfo); | |
1c79356b | 346 | |
b4c24cb9 | 347 | #if 0 // XXXdbg |
1c79356b | 348 | /* |
9bccf70c | 349 | * The b-tree code expects nodes to be contiguous. So when |
1c79356b | 350 | * the allocation block size is less than the b-tree node |
9bccf70c A |
351 | * size, we need to force disk allocations to be contiguous. |
352 | */ | |
1c79356b A |
353 | if (vcb->blockSize >= btInfo.nodeSize) { |
354 | extendFlags = 0; | |
355 | } else { | |
356 | /* Ensure that all b-tree nodes are contiguous on disk */ | |
b4c24cb9 | 357 | extendFlags = kEFContigMask; |
1c79356b | 358 | } |
b4c24cb9 | 359 | #endif |
1c79356b | 360 | |
b4c24cb9 | 361 | origSize = filePtr->fcbEOF; |
9bccf70c A |
362 | fileblocks = filePtr->ff_blocks; |
363 | startAllocation = vcb->nextAllocation; | |
1c79356b | 364 | |
b4c24cb9 A |
365 | // loop trying to get a contiguous chunk that's an integer multiple |
366 | // of the btree node size. if we can't get a contiguous chunk that | |
367 | // is at least the node size then we break out of the loop and let | |
368 | // the error propagate back up. | |
369 | do { | |
55e303ae A |
370 | retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0, |
371 | kEFContigMask | kEFMetadataMask, | |
372 | &actualBytesAdded); | |
b4c24cb9 A |
373 | if (retval == dskFulErr && actualBytesAdded == 0) { |
374 | ||
375 | if (bytesToAdd == btInfo.nodeSize || bytesToAdd < (minEOF - origSize)) { | |
376 | // if we're here there's nothing else to try, we're out | |
377 | // of space so we break and bail out. | |
378 | break; | |
379 | } else { | |
380 | bytesToAdd >>= 1; | |
381 | if (bytesToAdd < btInfo.nodeSize) { | |
382 | bytesToAdd = btInfo.nodeSize; | |
383 | } else if ((bytesToAdd % btInfo.nodeSize) != 0) { | |
384 | // make sure it's an integer multiple of the nodeSize | |
385 | bytesToAdd -= (bytesToAdd % btInfo.nodeSize); | |
386 | } | |
387 | } | |
388 | } | |
389 | } while (retval == dskFulErr && actualBytesAdded == 0); | |
91447636 | 390 | |
9bccf70c A |
391 | /* |
392 | * If a new extent was added then move the roving allocator | |
393 | * reference forward by the current b-tree file size so | |
394 | * there's plenty of room to grow. | |
395 | */ | |
396 | if ((retval == 0) && | |
55e303ae | 397 | ((VCBTOHFS(vcb)->hfs_flags & HFS_METADATA_ZONE) == 0) && |
9bccf70c A |
398 | (vcb->nextAllocation > startAllocation) && |
399 | ((vcb->nextAllocation + fileblocks) < vcb->totalBlocks)) { | |
400 | vcb->nextAllocation += fileblocks; | |
401 | } | |
402 | ||
b4c24cb9 A |
403 | filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize; |
404 | ||
405 | // XXXdbg ExtendFileC() could have returned an error even though | |
406 | // it grew the file to be big enough for our needs. If this is | |
407 | // the case, we don't care about retval so we blow it away. | |
408 | // | |
409 | if (filePtr->fcbEOF >= minEOF && retval != 0) { | |
410 | retval = 0; | |
411 | } | |
412 | ||
413 | // XXXdbg if the file grew but isn't large enough or isn't an | |
414 | // even multiple of the nodeSize then trim things back. if | |
415 | // the file isn't large enough we trim back to the original | |
416 | // size. otherwise we trim back to be an even multiple of the | |
417 | // btree node size. | |
418 | // | |
419 | if ((filePtr->fcbEOF < minEOF) || (actualBytesAdded % btInfo.nodeSize) != 0) { | |
420 | ||
421 | if (filePtr->fcbEOF < minEOF) { | |
422 | retval = dskFulErr; | |
423 | ||
424 | if (filePtr->fcbEOF < origSize) { | |
425 | panic("hfs: btree file eof %lld less than orig size %lld!\n", | |
426 | filePtr->fcbEOF, origSize); | |
427 | } | |
428 | ||
429 | trim = filePtr->fcbEOF - origSize; | |
430 | if (trim != actualBytesAdded) { | |
431 | panic("hfs: trim == %lld but actualBytesAdded == %lld\n", | |
432 | trim, actualBytesAdded); | |
433 | } | |
434 | } else { | |
435 | trim = (actualBytesAdded % btInfo.nodeSize); | |
436 | } | |
437 | ||
438 | ret = TruncateFileC(vcb, filePtr, filePtr->fcbEOF - trim, 0); | |
439 | filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize; | |
440 | ||
441 | // XXXdbg - panic if the file didn't get trimmed back properly | |
442 | if ((filePtr->fcbEOF % btInfo.nodeSize) != 0) { | |
443 | panic("hfs: truncate file didn't! fcbEOF %lld nsize %d fcb 0x%x\n", | |
444 | filePtr->fcbEOF, btInfo.nodeSize, filePtr); | |
445 | } | |
446 | ||
447 | if (ret) { | |
448 | // XXXdbg - this probably doesn't need to be a panic() | |
449 | panic("hfs: error truncating btree files (sz 0x%llx, trim %lld, ret %d)\n", | |
450 | filePtr->fcbEOF, trim, ret); | |
91447636 | 451 | goto out; |
b4c24cb9 A |
452 | } |
453 | actualBytesAdded -= trim; | |
454 | } | |
455 | ||
9bccf70c A |
456 | if(VTOC(vp)->c_fileid != kHFSExtentsFileID) { |
457 | /* | |
458 | * Get any extents overflow b-tree changes to disk ASAP! | |
459 | */ | |
b4c24cb9 | 460 | (void) BTFlushPath(VTOF(vcb->extentsRefNum)); |
91447636 | 461 | (void) hfs_fsync(vcb->extentsRefNum, MNT_WAIT, 0, p); |
9bccf70c | 462 | } |
91447636 A |
463 | hfs_systemfile_unlock(vcb, lockflags); |
464 | lockflags = 0; | |
1c79356b | 465 | |
b4c24cb9 A |
466 | if ((filePtr->fcbEOF % btInfo.nodeSize) != 0) { |
467 | panic("hfs: extendbtree: fcb 0x%x has eof 0x%llx not a multiple of 0x%x (trim %llx)\n", | |
468 | filePtr, filePtr->fcbEOF, btInfo.nodeSize, trim); | |
469 | } | |
470 | ||
1c79356b A |
471 | /* |
472 | * Update the Alternate MDB or Alternate VolumeHeader | |
473 | */ | |
9bccf70c A |
474 | if ((VTOC(vp)->c_fileid == kHFSExtentsFileID) || |
475 | (VTOC(vp)->c_fileid == kHFSCatalogFileID) || | |
476 | (VTOC(vp)->c_fileid == kHFSAttributesFileID) | |
1c79356b | 477 | ) { |
91447636 | 478 | VTOC(vp)->c_flag |= C_MODIFIED; |
1c79356b | 479 | MarkVCBDirty( vcb ); |
b4c24cb9 | 480 | ret = hfs_flushvolumeheader(VCBTOHFS(vcb), MNT_WAIT, HFS_ALTFLUSH); |
55e303ae | 481 | } else { |
91447636 A |
482 | VTOC(vp)->c_touch_chgtime = TRUE; |
483 | VTOC(vp)->c_touch_modtime = TRUE; | |
484 | (void) hfs_update(vp, TRUE); | |
1c79356b | 485 | } |
b4c24cb9 A |
486 | |
487 | ret = ClearBTNodes(vp, btInfo.nodeSize, filePtr->fcbEOF - actualBytesAdded, actualBytesAdded); | |
91447636 A |
488 | out: |
489 | if (retval == 0) | |
490 | retval = ret; | |
491 | ||
492 | if (lockflags) | |
493 | hfs_systemfile_unlock(vcb, lockflags); | |
1c79356b A |
494 | |
495 | return retval; | |
496 | } | |
497 | ||
498 | ||
1c79356b A |
499 | /* |
500 | * Clear out (zero) new b-tree nodes on disk. | |
501 | */ | |
502 | static int | |
503 | ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount) | |
504 | { | |
b4c24cb9 | 505 | struct hfsmount *hfsmp = VTOHFS(vp); |
1c79356b | 506 | struct buf *bp = NULL; |
91447636 A |
507 | daddr64_t blk; |
508 | daddr64_t blkcnt; | |
1c79356b A |
509 | |
510 | blk = offset / blksize; | |
511 | blkcnt = amount / blksize; | |
512 | ||
513 | while (blkcnt > 0) { | |
91447636 | 514 | bp = buf_getblk(vp, blk, blksize, 0, 0, BLK_META); |
1c79356b A |
515 | if (bp == NULL) |
516 | continue; | |
b4c24cb9 A |
517 | |
518 | // XXXdbg | |
519 | if (hfsmp->jnl) { | |
520 | // XXXdbg -- skipping this for now since it makes a transaction | |
521 | // become *way* too large | |
522 | //journal_modify_block_start(hfsmp->jnl, bp); | |
523 | } | |
91447636 | 524 | bzero((char *)buf_dataptr(bp), blksize); |
b4c24cb9 | 525 | |
91447636 | 526 | buf_markaged(bp); |
1c79356b | 527 | |
b4c24cb9 A |
528 | // XXXdbg |
529 | if (hfsmp->jnl) { | |
530 | // XXXdbg -- skipping this for now since it makes a transaction | |
531 | // become *way* too large | |
532 | //journal_modify_block_end(hfsmp->jnl, bp); | |
533 | ||
534 | // XXXdbg - remove this once we decide what to do with the | |
535 | // writes to the journal | |
536 | if ((blk % 32) == 0) | |
91447636 | 537 | VNOP_BWRITE(bp); |
b4c24cb9 | 538 | else |
91447636 | 539 | buf_bawrite(bp); |
b4c24cb9 A |
540 | } else { |
541 | /* wait/yield every 32 blocks so we don't hog all the buffers */ | |
542 | if ((blk % 32) == 0) | |
91447636 | 543 | VNOP_BWRITE(bp); |
b4c24cb9 | 544 | else |
91447636 | 545 | buf_bawrite(bp); |
b4c24cb9 | 546 | } |
1c79356b A |
547 | --blkcnt; |
548 | ++blk; | |
549 | } | |
550 | ||
551 | return (0); | |
552 | } | |
91447636 A |
553 | |
554 | ||
555 | extern char hfs_attrname[]; | |
556 | ||
557 | extern int hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey); | |
558 | ||
559 | int hfs_create_attr_btree(struct hfsmount *hfsmp, uint32_t nodesize, uint32_t nodecnt); | |
560 | ||
561 | /* | |
562 | * Create an HFS+ Attribute B-tree File. | |
563 | * | |
564 | * A journal transaction must be already started. | |
565 | */ | |
566 | int | |
567 | hfs_create_attr_btree(struct hfsmount *hfsmp, uint32_t nodesize, uint32_t nodecnt) | |
568 | { | |
569 | struct vnode* vp = NULL; | |
570 | struct cat_desc cndesc; | |
571 | struct cat_attr cnattr; | |
572 | struct cat_fork cfork; | |
573 | BlockDescriptor blkdesc; | |
574 | BTNodeDescriptor *ndp; | |
575 | BTHeaderRec *bthp; | |
576 | BTreeControlBlockPtr btcb = NULL; | |
577 | struct buf *bp = NULL; | |
578 | void * buffer; | |
579 | u_int16_t *index; | |
580 | u_int16_t offset; | |
581 | int result; | |
582 | ||
583 | printf("Creating HFS+ Attribute B-tree File (%d nodes) on %s\n", nodecnt, hfsmp->vcbVN); | |
584 | ||
585 | /* | |
586 | * Set up Attribute B-tree vnode | |
587 | */ | |
588 | bzero(&cndesc, sizeof(cndesc)); | |
589 | cndesc.cd_parentcnid = kHFSRootParentID; | |
590 | cndesc.cd_flags |= CD_ISMETA; | |
591 | cndesc.cd_nameptr = hfs_attrname; | |
592 | cndesc.cd_namelen = strlen(hfs_attrname); | |
593 | cndesc.cd_cnid = kHFSAttributesFileID; | |
594 | ||
595 | bzero(&cnattr, sizeof(cnattr)); | |
596 | cnattr.ca_nlink = 1; | |
597 | cnattr.ca_mode = S_IFREG; | |
598 | cnattr.ca_fileid = cndesc.cd_cnid; | |
599 | ||
600 | bzero(&cfork, sizeof(cfork)); | |
601 | cfork.cf_clump = nodesize * nodecnt; | |
602 | ||
603 | result = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork, &vp); | |
604 | if (result) | |
605 | return (result); | |
606 | ||
607 | /* | |
608 | * Set up Attribute B-tree control block | |
609 | */ | |
610 | MALLOC(btcb, BTreeControlBlock *, sizeof(BTreeControlBlock), M_TEMP, M_WAITOK); | |
611 | bzero(btcb, sizeof(BTreeControlBlock)); | |
612 | ||
613 | btcb->nodeSize = nodesize; | |
614 | btcb->maxKeyLength = kHFSPlusAttrKeyMaximumLength; | |
615 | btcb->btreeType = 0xFF; | |
616 | btcb->attributes = kBTVariableIndexKeysMask | kBTBigKeysMask; | |
617 | btcb->version = kBTreeVersion; | |
618 | btcb->writeCount = 1; | |
619 | btcb->flags = 0; /* kBTHeaderDirty */ | |
620 | btcb->fileRefNum = vp; | |
621 | btcb->getBlockProc = GetBTreeBlock; | |
622 | btcb->releaseBlockProc = ReleaseBTreeBlock; | |
623 | btcb->setEndOfForkProc = ExtendBTreeFile; | |
624 | btcb->keyCompareProc = (KeyCompareProcPtr)hfs_attrkeycompare; | |
625 | VTOF(vp)->fcbBTCBPtr = btcb; | |
626 | ||
627 | /* | |
628 | * Allocate some space | |
629 | */ | |
630 | result = ExtendBTreeFile(vp, nodesize, cfork.cf_clump); | |
631 | if (result) | |
632 | goto exit; | |
633 | ||
634 | btcb->totalNodes = VTOF(vp)->ff_size / nodesize; | |
635 | btcb->freeNodes = btcb->totalNodes - 1; | |
636 | ||
637 | /* | |
638 | * Initialize the b-tree header on disk | |
639 | */ | |
640 | bp = buf_getblk(vp, 0, nodesize, 0, 0, BLK_META); | |
641 | if (bp == NULL) { | |
642 | result = EIO; | |
643 | goto exit; | |
644 | } | |
645 | ||
646 | buffer = (void *)buf_dataptr(bp); | |
647 | blkdesc.buffer = buffer; | |
648 | blkdesc.blockHeader = (void *)bp; | |
649 | blkdesc.blockReadFromDisk = 0; | |
650 | blkdesc.isModified = 0; | |
651 | ||
652 | ModifyBlockStart(vp, &blkdesc); | |
653 | ||
654 | if (buf_size(bp) != nodesize) | |
655 | panic("hfs_create_attr_btree: bad buffer size (%d)\n", buf_size(bp)); | |
656 | ||
657 | bzero(buffer, nodesize); | |
658 | index = (int16_t *)buffer; | |
659 | ||
660 | /* FILL IN THE NODE DESCRIPTOR: */ | |
661 | ndp = (BTNodeDescriptor *)buffer; | |
662 | ndp->kind = kBTHeaderNode; | |
663 | ndp->numRecords = 3; | |
664 | offset = sizeof(BTNodeDescriptor); | |
665 | index[(nodesize / 2) - 1] = offset; | |
666 | ||
667 | /* FILL IN THE HEADER RECORD: */ | |
668 | bthp = (BTHeaderRec *)((UInt8 *)buffer + offset); | |
669 | bthp->nodeSize = nodesize; | |
670 | bthp->totalNodes = btcb->totalNodes; | |
671 | bthp->freeNodes = btcb->freeNodes; | |
672 | bthp->clumpSize = cfork.cf_clump; | |
673 | bthp->btreeType = 0xFF; | |
674 | bthp->attributes = kBTVariableIndexKeysMask | kBTBigKeysMask; | |
675 | bthp->maxKeyLength = kHFSPlusAttrKeyMaximumLength; | |
676 | bthp->keyCompareType = kHFSBinaryCompare; | |
677 | offset += sizeof(BTHeaderRec); | |
678 | index[(nodesize / 2) - 2] = offset; | |
679 | ||
680 | /* FILL IN THE USER RECORD: */ | |
681 | offset += kBTreeHeaderUserBytes; | |
682 | index[(nodesize / 2) - 3] = offset; | |
683 | ||
684 | /* FILL IN THE MAP RECORD (only one node in use). */ | |
685 | *((u_int8_t *)buffer + offset) = 0x80; | |
686 | offset += nodesize - sizeof(BTNodeDescriptor) - sizeof(BTHeaderRec) | |
687 | - kBTreeHeaderUserBytes - (4 * sizeof(int16_t)); | |
688 | index[(nodesize / 2) - 4] = offset; | |
689 | ||
690 | if (hfsmp->jnl) { | |
691 | result = btree_journal_modify_block_end(hfsmp, bp); | |
692 | } else { | |
693 | result = VNOP_BWRITE(bp); | |
694 | } | |
695 | if (result) | |
696 | goto exit; | |
697 | ||
698 | /* Publish new btree file */ | |
699 | hfsmp->hfs_attribute_vp = vp; | |
700 | (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH); | |
701 | ||
702 | exit: | |
703 | hfs_unlock(VTOC(vp)); | |
704 | if (result) { | |
705 | if (btcb) { | |
706 | FREE (btcb, M_TEMP); | |
707 | } | |
708 | vnode_put(vp); | |
709 | // hfs_truncate(); /* XXX need to give back blocks */ | |
710 | } | |
711 | return (result); | |
712 | } | |
713 | ||
714 | ||
715 |