]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_btreeio.c
xnu-1504.3.12.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_btreeio.c
CommitLineData
1c79356b 1/*
935ed37a 2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b 27 */
1c79356b
A
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/buf.h>
2d21ac55 32#include <sys/buf_internal.h>
9bccf70c 33#include <sys/kernel.h>
91447636 34#include <sys/malloc.h>
1c79356b
A
35#include <sys/mount.h>
36#include <sys/vnode.h>
37
38
39#include "hfs.h"
9bccf70c 40#include "hfs_cnode.h"
1c79356b
A
41#include "hfs_dbg.h"
42#include "hfs_endian.h"
2d21ac55 43#include "hfs_btreeio.h"
1c79356b
A
44
45#include "hfscommon/headers/FileMgrInternal.h"
46#include "hfscommon/headers/BTreesPrivate.h"
47
48#define FORCESYNCBTREEWRITES 0
49
2d21ac55
A
50/* From bsd/vfs/vfs_bio.c */
51extern int bdwrite_internal(struct buf *, int);
1c79356b
A
52
53static int ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount);
2d21ac55 54static int btree_journal_modify_block_end(struct hfsmount *hfsmp, struct buf *bp);
1c79356b
A
55
56
9bccf70c 57__private_extern__
2d21ac55 58OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, __unused ItemCount minBlockCount)
1c79356b
A
59{
60 BTreeControlBlockPtr bTreePtr;
61
62 DBG_ASSERT(vp != NULL);
1c79356b
A
63 DBG_ASSERT(blockSize >= kMinNodeSize);
64 if (blockSize > MAXBSIZE )
65 return (fsBTBadNodeSize);
66
9bccf70c 67 bTreePtr = (BTreeControlBlockPtr)VTOF(vp)->fcbBTCBPtr;
1c79356b
A
68 bTreePtr->nodeSize = blockSize;
69
70 return (E_NONE);
71}
72
73
9bccf70c 74__private_extern__
2d21ac55 75OSStatus GetBTreeBlock(FileReference vp, u_int32_t blockNum, GetBlockOptions options, BlockDescriptor *block)
1c79356b
A
76{
77 OSStatus retval = E_NONE;
78 struct buf *bp = NULL;
935ed37a
A
79 u_int8_t allow_empty_node;
80
81 /* If the btree block is being read using hint, it is
82 * fine for the swap code to find zeroed out nodes.
83 */
84 if (options & kGetBlockHint) {
85 allow_empty_node = true;
86 } else {
87 allow_empty_node = false;
88 }
1c79356b 89
91447636
A
90 if (options & kGetEmptyBlock) {
91 daddr64_t blkno;
92 off_t offset;
1c79356b 93
91447636
A
94 offset = (daddr64_t)blockNum * (daddr64_t)block->blockSize;
95 bp = buf_getblk(vp, (daddr64_t)blockNum, block->blockSize, 0, 0, BLK_META);
96 if (bp &&
97 VNOP_BLOCKMAP(vp, offset, block->blockSize, &blkno, NULL, NULL, 0, NULL) == 0) {
98 buf_setblkno(bp, blkno);
99 }
100 } else {
101 retval = buf_meta_bread(vp, (daddr64_t)blockNum, block->blockSize, NOCRED, &bp);
102 }
1c79356b
A
103 if (bp == NULL)
104 retval = -1; //XXX need better error
105
106 if (retval == E_NONE) {
107 block->blockHeader = bp;
91447636 108 block->buffer = (char *)buf_dataptr(bp);
3a60a9f5 109 block->blockNum = buf_lblkno(bp);
91447636 110 block->blockReadFromDisk = (buf_fromcache(bp) == 0); /* not found in cache ==> came from disk */
1c79356b 111
b4c24cb9
A
112 // XXXdbg
113 block->isModified = 0;
114
3a60a9f5 115 /* Check and endian swap B-Tree node (only if it's a valid block) */
1c79356b
A
116 if (!(options & kGetEmptyBlock)) {
117 /* This happens when we first open the b-tree, we might not have all the node data on hand */
118 if ((((BTNodeDescriptor *)block->buffer)->kind == kBTHeaderNode) &&
91447636
A
119 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize != buf_count(bp)) &&
120 (SWAP_BE16 (((BTHeaderRec *)((char *)block->buffer + 14))->nodeSize) != buf_count(bp))) {
1c79356b 121
3a60a9f5
A
122 /*
123 * Don't swap the node descriptor, record offsets, or other records.
124 * This record will be invalidated and re-read with the correct node
125 * size once the B-tree control block is set up with the node size
126 * from the header record.
127 */
935ed37a 128 retval = hfs_swap_BTNode (block, vp, kSwapBTNodeHeaderRecordOnly, allow_empty_node);
3a60a9f5
A
129
130 } else if (block->blockReadFromDisk) {
131 /*
132 * The node was just read from disk, so always swap/check it.
133 * This is necessary on big endian since the test below won't trigger.
134 */
935ed37a 135 retval = hfs_swap_BTNode (block, vp, kSwapBTNodeBigToHost, allow_empty_node);
2d21ac55 136 } else if (*((u_int16_t *)((char *)block->buffer + (block->blockSize - sizeof (u_int16_t)))) == 0x0e00) {
3a60a9f5
A
137 /*
138 * The node was left in the cache in non-native order, so swap it.
139 * This only happens on little endian, after the node is written
140 * back to disk.
141 */
935ed37a 142 retval = hfs_swap_BTNode (block, vp, kSwapBTNodeBigToHost, allow_empty_node);
1c79356b 143 }
3a60a9f5
A
144
145 /*
146 * If we got an error, then the node is only partially swapped.
147 * We mark the buffer invalid so that the next attempt to get the
148 * node will read it and attempt to swap again, and will notice
149 * the error again. If we didn't do this, the next attempt to get
150 * the node might use the partially swapped node as-is.
151 */
152 if (retval)
153 buf_markinvalid(bp);
1c79356b 154 }
3a60a9f5
A
155 }
156
157 if (retval) {
1c79356b 158 if (bp)
3a60a9f5 159 buf_brelse(bp);
1c79356b
A
160 block->blockHeader = NULL;
161 block->buffer = NULL;
162 }
163
164 return (retval);
165}
166
167
b4c24cb9
A
168__private_extern__
169void ModifyBlockStart(FileReference vp, BlockDescPtr blockPtr)
170{
171 struct hfsmount *hfsmp = VTOHFS(vp);
172 struct buf *bp = NULL;
173
174 if (hfsmp->jnl == NULL) {
175 return;
176 }
177
178 bp = (struct buf *) blockPtr->blockHeader;
179 if (bp == NULL) {
b0d623f7 180 panic("hfs: ModifyBlockStart: null bp for blockdescptr %p?!?\n", blockPtr);
b4c24cb9
A
181 return;
182 }
183
184 journal_modify_block_start(hfsmp->jnl, bp);
185 blockPtr->isModified = 1;
186}
187
2d21ac55
A
188static void
189btree_swap_node(struct buf *bp, __unused void *arg)
55e303ae 190{
2d21ac55 191 // struct hfsmount *hfsmp = (struct hfsmount *)arg;
3a60a9f5 192 int retval;
91447636 193 struct vnode *vp = buf_vnode(bp);
55e303ae
A
194 BlockDescriptor block;
195
196 /* Prepare the block pointer */
197 block.blockHeader = bp;
91447636 198 block.buffer = (char *)buf_dataptr(bp);
3a60a9f5 199 block.blockNum = buf_lblkno(bp);
55e303ae 200 /* not found in cache ==> came from disk */
91447636
A
201 block.blockReadFromDisk = (buf_fromcache(bp) == 0);
202 block.blockSize = buf_count(bp);
55e303ae 203
935ed37a
A
204 /* Swap the data now that this node is ready to go to disk.
205 * We allow swapping of zeroed out nodes here because we might
206 * be writing node whose last record just got deleted.
207 */
208 retval = hfs_swap_BTNode (&block, vp, kSwapBTNodeHostToBig, true);
3a60a9f5 209 if (retval)
b0d623f7 210 panic("hfs: btree_swap_node: about to write corrupt node!\n");
2d21ac55
A
211}
212
55e303ae 213
2d21ac55
A
214static int
215btree_journal_modify_block_end(struct hfsmount *hfsmp, struct buf *bp)
216{
217 return journal_modify_block_end(hfsmp->jnl, bp, btree_swap_node, hfsmp);
55e303ae
A
218}
219
b4c24cb9 220
9bccf70c 221__private_extern__
1c79356b
A
222OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options)
223{
b4c24cb9 224 struct hfsmount *hfsmp = VTOHFS(vp);
1c79356b
A
225 OSStatus retval = E_NONE;
226 struct buf *bp = NULL;
227
228 bp = (struct buf *) blockPtr->blockHeader;
229
230 if (bp == NULL) {
1c79356b
A
231 retval = -1;
232 goto exit;
233 }
234
235 if (options & kTrashBlock) {
91447636
A
236 buf_markinvalid(bp);
237
238 if (hfsmp->jnl && (buf_flags(bp) & B_LOCKED)) {
b4c24cb9
A
239 journal_kill_block(hfsmp->jnl, bp);
240 } else {
91447636 241 buf_brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
b4c24cb9 242 }
1c79356b
A
243 } else {
244 if (options & kForceWriteBlock) {
b4c24cb9
A
245 if (hfsmp->jnl) {
246 if (blockPtr->isModified == 0) {
2d21ac55 247 panic("hfs: releaseblock: modified is 0 but forcewrite set! bp %p\n", bp);
b4c24cb9 248 }
55e303ae
A
249
250 retval = btree_journal_modify_block_end(hfsmp, bp);
b4c24cb9
A
251 blockPtr->isModified = 0;
252 } else {
91447636 253 retval = VNOP_BWRITE(bp);
b4c24cb9 254 }
1c79356b 255 } else if (options & kMarkBlockDirty) {
91447636
A
256 struct timeval tv;
257 microuptime(&tv);
b4c24cb9 258 if ((options & kLockTransaction) && hfsmp->jnl == NULL) {
9bccf70c
A
259 /*
260 *
91447636 261 * Set the B_LOCKED flag and unlock the buffer, causing buf_brelse to move
9bccf70c 262 * the buffer onto the LOCKED free list. This is necessary, otherwise
91447636 263 * getnewbuf() would try to reclaim the buffers using buf_bawrite, which
9bccf70c
A
264 * isn't going to work.
265 *
266 */
9bccf70c
A
267 /* Don't hog all the buffers... */
268 if (count_lock_queue() > kMaxLockedMetaBuffers) {
269 hfs_btsync(vp, HFS_SYNCTRANS);
270 /* Rollback sync time to cause a sync on lock release... */
91447636 271 (void) BTSetLastSync(VTOF(vp), tv.tv_sec - (kMaxSecsForFsync + 1));
9bccf70c 272 }
91447636 273 buf_setflags(bp, B_LOCKED);
b4c24cb9
A
274 }
275
9bccf70c
A
276 /*
277 * Delay-write this block.
278 * If the maximum delayed buffers has been exceeded then
279 * free up some buffers and fall back to an asynchronous write.
280 */
b4c24cb9
A
281 if (hfsmp->jnl) {
282 if (blockPtr->isModified == 0) {
2d21ac55 283 panic("hfs: releaseblock: modified is 0 but markdirty set! bp %p\n", bp);
b4c24cb9 284 }
55e303ae 285 retval = btree_journal_modify_block_end(hfsmp, bp);
b4c24cb9
A
286 blockPtr->isModified = 0;
287 } else if (bdwrite_internal(bp, 1) != 0) {
9bccf70c
A
288 hfs_btsync(vp, 0);
289 /* Rollback sync time to cause a sync on lock release... */
91447636
A
290 (void) BTSetLastSync(VTOF(vp), tv.tv_sec - (kMaxSecsForFsync + 1));
291
292 buf_clearflags(bp, B_LOCKED);
293 buf_bawrite(bp);
9bccf70c 294 }
1c79356b 295 } else {
b4c24cb9 296 // check if we had previously called journal_modify_block_start()
91447636 297 // on this block and if so, abort it (which will call buf_brelse()).
b4c24cb9
A
298 if (hfsmp->jnl && blockPtr->isModified) {
299 // XXXdbg - I don't want to call modify_block_abort()
300 // because I think it may be screwing up the
301 // journal and blowing away a block that has
302 // valid data in it.
303 //
304 // journal_modify_block_abort(hfsmp->jnl, bp);
305 //panic("hfs: releaseblock called for 0x%x but mod_block_start previously called.\n", bp);
55e303ae 306 btree_journal_modify_block_end(hfsmp, bp);
b4c24cb9
A
307 blockPtr->isModified = 0;
308 } else {
91447636 309 buf_brelse(bp); /* note: B-tree code will clear blockPtr->blockHeader and blockPtr->buffer */
b4c24cb9 310 }
1c79356b
A
311 };
312 };
313
314exit:
315 return (retval);
316}
317
318
9bccf70c 319__private_extern__
1c79356b
A
320OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF)
321{
322#pragma unused (maxEOF)
323
91447636 324 OSStatus retval = 0, ret = 0;
6601e61a
A
325 int64_t actualBytesAdded, origSize;
326 u_int64_t bytesToAdd;
9bccf70c
A
327 u_int32_t startAllocation;
328 u_int32_t fileblocks;
6601e61a 329 BTreeInfoRec btInfo;
1c79356b 330 ExtendedVCB *vcb;
6601e61a
A
331 FCB *filePtr;
332 struct proc *p = NULL;
333 int64_t trim = 0;
334 int lockflags = 0;
1c79356b
A
335
336 filePtr = GetFileControlBlock(vp);
337
6601e61a 338 if ( (off_t)minEOF > filePtr->fcbEOF )
1c79356b
A
339 {
340 bytesToAdd = minEOF - filePtr->fcbEOF;
341
9bccf70c
A
342 if (bytesToAdd < filePtr->ff_clumpsize)
343 bytesToAdd = filePtr->ff_clumpsize; //XXX why not always be a mutiple of clump size?
1c79356b
A
344 }
345 else
346 {
1c79356b
A
347 return -1;
348 }
349
9bccf70c 350 vcb = VTOVCB(vp);
6601e61a 351
1c79356b
A
352 /*
353 * The Extents B-tree can't have overflow extents. ExtendFileC will
354 * return an error if an attempt is made to extend the Extents B-tree
355 * when the resident extents are exhausted.
356 */
1c79356b 357
91447636
A
358 /* Protect allocation bitmap and extents overflow file. */
359 lockflags = SFL_BITMAP;
360 if (VTOC(vp)->c_fileid != kHFSExtentsFileID)
361 lockflags |= SFL_EXTENTS;
362 lockflags = hfs_systemfile_lock(vcb, lockflags, HFS_EXCLUSIVE_LOCK);
363
364 (void) BTGetInformation(filePtr, 0, &btInfo);
1c79356b 365
b4c24cb9 366#if 0 // XXXdbg
1c79356b 367 /*
9bccf70c 368 * The b-tree code expects nodes to be contiguous. So when
1c79356b 369 * the allocation block size is less than the b-tree node
9bccf70c
A
370 * size, we need to force disk allocations to be contiguous.
371 */
1c79356b
A
372 if (vcb->blockSize >= btInfo.nodeSize) {
373 extendFlags = 0;
374 } else {
375 /* Ensure that all b-tree nodes are contiguous on disk */
b4c24cb9 376 extendFlags = kEFContigMask;
1c79356b 377 }
b4c24cb9 378#endif
1c79356b 379
b4c24cb9 380 origSize = filePtr->fcbEOF;
9bccf70c
A
381 fileblocks = filePtr->ff_blocks;
382 startAllocation = vcb->nextAllocation;
1c79356b 383
b4c24cb9
A
384 // loop trying to get a contiguous chunk that's an integer multiple
385 // of the btree node size. if we can't get a contiguous chunk that
386 // is at least the node size then we break out of the loop and let
387 // the error propagate back up.
6601e61a
A
388 while((off_t)bytesToAdd >= btInfo.nodeSize) {
389 do {
55e303ae 390 retval = ExtendFileC(vcb, filePtr, bytesToAdd, 0,
6601e61a
A
391 kEFContigMask | kEFMetadataMask | kEFNoClumpMask,
392 (int64_t *)&actualBytesAdded);
b4c24cb9 393 if (retval == dskFulErr && actualBytesAdded == 0) {
6601e61a
A
394 bytesToAdd >>= 1;
395 if (bytesToAdd < btInfo.nodeSize) {
396 break;
397 } else if ((bytesToAdd % btInfo.nodeSize) != 0) {
398 // make sure it's an integer multiple of the nodeSize
399 bytesToAdd -= (bytesToAdd % btInfo.nodeSize);
400 }
b4c24cb9 401 }
6601e61a
A
402 } while (retval == dskFulErr && actualBytesAdded == 0);
403
404 if (retval == dskFulErr && actualBytesAdded == 0 && bytesToAdd <= btInfo.nodeSize) {
405 break;
406 }
407
408 filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize;
409 bytesToAdd = minEOF - filePtr->fcbEOF;
410 }
91447636 411
9bccf70c
A
412 /*
413 * If a new extent was added then move the roving allocator
414 * reference forward by the current b-tree file size so
415 * there's plenty of room to grow.
416 */
417 if ((retval == 0) &&
55e303ae 418 ((VCBTOHFS(vcb)->hfs_flags & HFS_METADATA_ZONE) == 0) &&
9bccf70c 419 (vcb->nextAllocation > startAllocation) &&
2d21ac55
A
420 ((vcb->nextAllocation + fileblocks) < vcb->allocLimit)) {
421 HFS_UPDATE_NEXT_ALLOCATION(vcb, vcb->nextAllocation + fileblocks);
9bccf70c
A
422 }
423
b4c24cb9
A
424 filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize;
425
426 // XXXdbg ExtendFileC() could have returned an error even though
427 // it grew the file to be big enough for our needs. If this is
428 // the case, we don't care about retval so we blow it away.
429 //
6601e61a 430 if (filePtr->fcbEOF >= (off_t)minEOF && retval != 0) {
b4c24cb9
A
431 retval = 0;
432 }
433
434 // XXXdbg if the file grew but isn't large enough or isn't an
435 // even multiple of the nodeSize then trim things back. if
436 // the file isn't large enough we trim back to the original
437 // size. otherwise we trim back to be an even multiple of the
438 // btree node size.
439 //
6601e61a 440 if ((filePtr->fcbEOF < (off_t)minEOF) || ((filePtr->fcbEOF - origSize) % btInfo.nodeSize) != 0) {
b4c24cb9 441
6601e61a 442 if (filePtr->fcbEOF < (off_t)minEOF) {
b4c24cb9
A
443 retval = dskFulErr;
444
445 if (filePtr->fcbEOF < origSize) {
446 panic("hfs: btree file eof %lld less than orig size %lld!\n",
447 filePtr->fcbEOF, origSize);
448 }
449
450 trim = filePtr->fcbEOF - origSize;
b4c24cb9 451 } else {
6601e61a 452 trim = ((filePtr->fcbEOF - origSize) % btInfo.nodeSize);
b4c24cb9
A
453 }
454
455 ret = TruncateFileC(vcb, filePtr, filePtr->fcbEOF - trim, 0);
456 filePtr->fcbEOF = (u_int64_t)filePtr->ff_blocks * (u_int64_t)vcb->blockSize;
457
458 // XXXdbg - panic if the file didn't get trimmed back properly
459 if ((filePtr->fcbEOF % btInfo.nodeSize) != 0) {
6601e61a 460 panic("hfs: truncate file didn't! fcbEOF %lld nsize %d fcb %p\n",
b4c24cb9
A
461 filePtr->fcbEOF, btInfo.nodeSize, filePtr);
462 }
463
464 if (ret) {
465 // XXXdbg - this probably doesn't need to be a panic()
6601e61a 466 panic("hfs: error truncating btree files (sz 0x%llx, trim %lld, ret %ld)\n",
b0d623f7 467 filePtr->fcbEOF, trim, (long)ret);
91447636 468 goto out;
b4c24cb9 469 }
b4c24cb9
A
470 }
471
9bccf70c
A
472 if(VTOC(vp)->c_fileid != kHFSExtentsFileID) {
473 /*
474 * Get any extents overflow b-tree changes to disk ASAP!
475 */
b4c24cb9 476 (void) BTFlushPath(VTOF(vcb->extentsRefNum));
91447636 477 (void) hfs_fsync(vcb->extentsRefNum, MNT_WAIT, 0, p);
9bccf70c 478 }
91447636
A
479 hfs_systemfile_unlock(vcb, lockflags);
480 lockflags = 0;
1c79356b 481
b4c24cb9 482 if ((filePtr->fcbEOF % btInfo.nodeSize) != 0) {
6601e61a 483 panic("hfs: extendbtree: fcb %p has eof 0x%llx not a multiple of 0x%x (trim %llx)\n",
b4c24cb9
A
484 filePtr, filePtr->fcbEOF, btInfo.nodeSize, trim);
485 }
486
1c79356b
A
487 /*
488 * Update the Alternate MDB or Alternate VolumeHeader
489 */
9bccf70c
A
490 if ((VTOC(vp)->c_fileid == kHFSExtentsFileID) ||
491 (VTOC(vp)->c_fileid == kHFSCatalogFileID) ||
492 (VTOC(vp)->c_fileid == kHFSAttributesFileID)
1c79356b 493 ) {
91447636 494 VTOC(vp)->c_flag |= C_MODIFIED;
1c79356b 495 MarkVCBDirty( vcb );
b4c24cb9 496 ret = hfs_flushvolumeheader(VCBTOHFS(vcb), MNT_WAIT, HFS_ALTFLUSH);
55e303ae 497 } else {
91447636
A
498 VTOC(vp)->c_touch_chgtime = TRUE;
499 VTOC(vp)->c_touch_modtime = TRUE;
500 (void) hfs_update(vp, TRUE);
1c79356b 501 }
b4c24cb9 502
6601e61a 503 ret = ClearBTNodes(vp, btInfo.nodeSize, origSize, (filePtr->fcbEOF - origSize));
91447636
A
504out:
505 if (retval == 0)
506 retval = ret;
507
508 if (lockflags)
509 hfs_systemfile_unlock(vcb, lockflags);
1c79356b
A
510
511 return retval;
512}
513
514
1c79356b
A
515/*
516 * Clear out (zero) new b-tree nodes on disk.
517 */
518static int
519ClearBTNodes(struct vnode *vp, long blksize, off_t offset, off_t amount)
520{
b4c24cb9 521 struct hfsmount *hfsmp = VTOHFS(vp);
1c79356b 522 struct buf *bp = NULL;
91447636
A
523 daddr64_t blk;
524 daddr64_t blkcnt;
1c79356b
A
525
526 blk = offset / blksize;
527 blkcnt = amount / blksize;
528
529 while (blkcnt > 0) {
91447636 530 bp = buf_getblk(vp, blk, blksize, 0, 0, BLK_META);
1c79356b
A
531 if (bp == NULL)
532 continue;
b4c24cb9
A
533
534 // XXXdbg
535 if (hfsmp->jnl) {
536 // XXXdbg -- skipping this for now since it makes a transaction
537 // become *way* too large
538 //journal_modify_block_start(hfsmp->jnl, bp);
539 }
91447636 540 bzero((char *)buf_dataptr(bp), blksize);
b4c24cb9 541
91447636 542 buf_markaged(bp);
1c79356b 543
b4c24cb9
A
544 // XXXdbg
545 if (hfsmp->jnl) {
546 // XXXdbg -- skipping this for now since it makes a transaction
547 // become *way* too large
548 //journal_modify_block_end(hfsmp->jnl, bp);
549
550 // XXXdbg - remove this once we decide what to do with the
551 // writes to the journal
552 if ((blk % 32) == 0)
91447636 553 VNOP_BWRITE(bp);
b4c24cb9 554 else
91447636 555 buf_bawrite(bp);
b4c24cb9
A
556 } else {
557 /* wait/yield every 32 blocks so we don't hog all the buffers */
558 if ((blk % 32) == 0)
91447636 559 VNOP_BWRITE(bp);
b4c24cb9 560 else
91447636 561 buf_bawrite(bp);
b4c24cb9 562 }
1c79356b
A
563 --blkcnt;
564 ++blk;
565 }
566
567 return (0);
568}
91447636
A
569
570
571extern char hfs_attrname[];
572
91447636
A
573/*
574 * Create an HFS+ Attribute B-tree File.
575 *
2d21ac55 576 * No global resources should be held.
91447636
A
577 */
578int
2d21ac55 579hfs_create_attr_btree(struct hfsmount *hfsmp, u_int32_t nodesize, u_int32_t nodecnt)
91447636 580{
2d21ac55 581 struct vnode* vp = NULLVP;
91447636
A
582 struct cat_desc cndesc;
583 struct cat_attr cnattr;
584 struct cat_fork cfork;
585 BlockDescriptor blkdesc;
586 BTNodeDescriptor *ndp;
587 BTHeaderRec *bthp;
588 BTreeControlBlockPtr btcb = NULL;
589 struct buf *bp = NULL;
590 void * buffer;
b0d623f7 591 u_int8_t *bitmap;
91447636 592 u_int16_t *index;
b0d623f7
A
593 u_int32_t node_num, num_map_nodes;
594 u_int32_t bytes_per_map_record;
595 u_int32_t temp;
91447636 596 u_int16_t offset;
2d21ac55 597 int intrans = 0;
91447636 598 int result;
2d21ac55
A
599again:
600 /*
601 * Serialize creation using HFS_CREATING_BTREE flag.
602 */
603 lck_mtx_lock(&hfsmp->hfs_mutex);
604 if (hfsmp->hfs_flags & HFS_CREATING_BTREE) {
605 /* Someone else beat us, wait for them to finish. */
606 (void) msleep(hfsmp->hfs_attribute_cp, &hfsmp->hfs_mutex,
607 PDROP | PINOD, "hfs_create_attr_btree", 0);
608 if (hfsmp->hfs_attribute_vp) {
609 return (0);
610 }
611 goto again;
612 }
613 hfsmp->hfs_flags |= HFS_CREATING_BTREE;
614 lck_mtx_unlock(&hfsmp->hfs_mutex);
91447636 615
2d21ac55
A
616 /* Check if were out of usable disk space. */
617 if ((hfs_freeblks(hfsmp, 1) == 0)) {
618 result = ENOSPC;
619 goto exit;
620 }
91447636
A
621
622 /*
623 * Set up Attribute B-tree vnode
2d21ac55
A
624 * (this must be done before we start a transaction
625 * or take any system file locks)
91447636
A
626 */
627 bzero(&cndesc, sizeof(cndesc));
628 cndesc.cd_parentcnid = kHFSRootParentID;
629 cndesc.cd_flags |= CD_ISMETA;
2d21ac55 630 cndesc.cd_nameptr = (const u_int8_t *)hfs_attrname;
91447636
A
631 cndesc.cd_namelen = strlen(hfs_attrname);
632 cndesc.cd_cnid = kHFSAttributesFileID;
633
634 bzero(&cnattr, sizeof(cnattr));
2d21ac55 635 cnattr.ca_linkcount = 1;
91447636
A
636 cnattr.ca_mode = S_IFREG;
637 cnattr.ca_fileid = cndesc.cd_cnid;
638
639 bzero(&cfork, sizeof(cfork));
640 cfork.cf_clump = nodesize * nodecnt;
641
642 result = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork, &vp);
2d21ac55
A
643 if (result) {
644 goto exit;
645 }
91447636
A
646 /*
647 * Set up Attribute B-tree control block
648 */
649 MALLOC(btcb, BTreeControlBlock *, sizeof(BTreeControlBlock), M_TEMP, M_WAITOK);
650 bzero(btcb, sizeof(BTreeControlBlock));
651
652 btcb->nodeSize = nodesize;
653 btcb->maxKeyLength = kHFSPlusAttrKeyMaximumLength;
654 btcb->btreeType = 0xFF;
655 btcb->attributes = kBTVariableIndexKeysMask | kBTBigKeysMask;
656 btcb->version = kBTreeVersion;
657 btcb->writeCount = 1;
658 btcb->flags = 0; /* kBTHeaderDirty */
659 btcb->fileRefNum = vp;
660 btcb->getBlockProc = GetBTreeBlock;
661 btcb->releaseBlockProc = ReleaseBTreeBlock;
662 btcb->setEndOfForkProc = ExtendBTreeFile;
663 btcb->keyCompareProc = (KeyCompareProcPtr)hfs_attrkeycompare;
664 VTOF(vp)->fcbBTCBPtr = btcb;
665
666 /*
667 * Allocate some space
668 */
2d21ac55
A
669 if (hfs_start_transaction(hfsmp) != 0) {
670 result = EINVAL;
671 goto exit;
672 }
673 intrans = 1;
674
675 /* Note ExtendBTreeFile will acquire the necessary system file locks. */
91447636
A
676 result = ExtendBTreeFile(vp, nodesize, cfork.cf_clump);
677 if (result)
678 goto exit;
679
680 btcb->totalNodes = VTOF(vp)->ff_size / nodesize;
91447636 681
b0d623f7
A
682 /*
683 * Figure out how many map nodes we'll need.
684 *
685 * bytes_per_map_record = the number of bytes in the map record of a
686 * map node. Since that is the only record in the node, it is the size
687 * of the node minus the node descriptor at the start, and two record
688 * offsets at the end of the node. The "- 2" is to round the size down
689 * to a multiple of 4 bytes (since sizeof(BTNodeDescriptor) is not a
690 * multiple of 4).
691 *
692 * The value "temp" here is the number of *bits* in the map record of
693 * the header node.
694 */
695 bytes_per_map_record = nodesize - sizeof(BTNodeDescriptor) - 2*sizeof(u_int16_t) - 2;
696 temp = 8 * (nodesize - sizeof(BTNodeDescriptor)
697 - sizeof(BTHeaderRec)
698 - kBTreeHeaderUserBytes
699 - 4 * sizeof(u_int16_t));
700 if (btcb->totalNodes > temp) {
701 num_map_nodes = howmany(btcb->totalNodes - temp, bytes_per_map_record * 8);
702 }
703 else {
704 num_map_nodes = 0;
705 }
706
707 btcb->freeNodes = btcb->totalNodes - 1 - num_map_nodes;
708
91447636
A
709 /*
710 * Initialize the b-tree header on disk
711 */
712 bp = buf_getblk(vp, 0, nodesize, 0, 0, BLK_META);
713 if (bp == NULL) {
714 result = EIO;
715 goto exit;
716 }
717
718 buffer = (void *)buf_dataptr(bp);
719 blkdesc.buffer = buffer;
720 blkdesc.blockHeader = (void *)bp;
721 blkdesc.blockReadFromDisk = 0;
722 blkdesc.isModified = 0;
723
724 ModifyBlockStart(vp, &blkdesc);
725
726 if (buf_size(bp) != nodesize)
727 panic("hfs_create_attr_btree: bad buffer size (%d)\n", buf_size(bp));
728
729 bzero(buffer, nodesize);
2d21ac55 730 index = (u_int16_t *)buffer;
91447636
A
731
732 /* FILL IN THE NODE DESCRIPTOR: */
733 ndp = (BTNodeDescriptor *)buffer;
b0d623f7
A
734 if (num_map_nodes != 0)
735 ndp->fLink = 1;
91447636
A
736 ndp->kind = kBTHeaderNode;
737 ndp->numRecords = 3;
738 offset = sizeof(BTNodeDescriptor);
739 index[(nodesize / 2) - 1] = offset;
740
741 /* FILL IN THE HEADER RECORD: */
2d21ac55 742 bthp = (BTHeaderRec *)((u_int8_t *)buffer + offset);
91447636
A
743 bthp->nodeSize = nodesize;
744 bthp->totalNodes = btcb->totalNodes;
745 bthp->freeNodes = btcb->freeNodes;
746 bthp->clumpSize = cfork.cf_clump;
747 bthp->btreeType = 0xFF;
748 bthp->attributes = kBTVariableIndexKeysMask | kBTBigKeysMask;
749 bthp->maxKeyLength = kHFSPlusAttrKeyMaximumLength;
750 bthp->keyCompareType = kHFSBinaryCompare;
751 offset += sizeof(BTHeaderRec);
752 index[(nodesize / 2) - 2] = offset;
753
754 /* FILL IN THE USER RECORD: */
755 offset += kBTreeHeaderUserBytes;
756 index[(nodesize / 2) - 3] = offset;
757
b0d623f7
A
758 /* Mark the header node and map nodes in use in the map record.
759 *
760 * NOTE: Assumes that the header node's map record has at least
761 * (num_map_nodes + 1) bits.
762 */
763 bitmap = (u_int8_t *) buffer + offset;
764 temp = num_map_nodes + 1; /* +1 for the header node */
765 while (temp >= 8) {
766 *(bitmap++) = 0xFF;
767 temp -= 8;
768 }
769 *bitmap = ~(0xFF >> temp);
770
91447636
A
771 offset += nodesize - sizeof(BTNodeDescriptor) - sizeof(BTHeaderRec)
772 - kBTreeHeaderUserBytes - (4 * sizeof(int16_t));
773 index[(nodesize / 2) - 4] = offset;
774
775 if (hfsmp->jnl) {
776 result = btree_journal_modify_block_end(hfsmp, bp);
777 } else {
778 result = VNOP_BWRITE(bp);
779 }
780 if (result)
781 goto exit;
782
b0d623f7
A
783 /* Create the map nodes: node numbers 1 .. num_map_nodes */
784 for (node_num=1; node_num <= num_map_nodes; ++node_num) {
785 bp = buf_getblk(vp, node_num, nodesize, 0, 0, BLK_META);
786 if (bp == NULL) {
787 result = EIO;
788 goto exit;
789 }
790 buffer = (void *)buf_dataptr(bp);
791 blkdesc.buffer = buffer;
792 blkdesc.blockHeader = (void *)bp;
793 blkdesc.blockReadFromDisk = 0;
794 blkdesc.isModified = 0;
795
796 ModifyBlockStart(vp, &blkdesc);
797
798 bzero(buffer, nodesize);
799 index = (u_int16_t *)buffer;
800
801 /* Fill in the node descriptor */
802 ndp = (BTNodeDescriptor *)buffer;
803 if (node_num != num_map_nodes)
804 ndp->fLink = node_num + 1;
805 ndp->kind = kBTMapNode;
806 ndp->numRecords = 1;
807 offset = sizeof(BTNodeDescriptor);
808 index[(nodesize / 2) - 1] = offset;
809
810
811 /* Fill in the map record's offset */
812 /* Note: We assume that the map record is all zeroes */
813 offset = sizeof(BTNodeDescriptor) + bytes_per_map_record;
814 index[(nodesize / 2) - 2] = offset;
815
816 if (hfsmp->jnl) {
817 result = btree_journal_modify_block_end(hfsmp, bp);
818 } else {
819 result = VNOP_BWRITE(bp);
820 }
821 if (result)
822 goto exit;
823 }
824
2d21ac55
A
825 /* Update vp/cp for attribute btree */
826 lck_mtx_lock(&hfsmp->hfs_mutex);
827 hfsmp->hfs_attribute_cp = VTOC(vp);
91447636 828 hfsmp->hfs_attribute_vp = vp;
2d21ac55 829 lck_mtx_unlock(&hfsmp->hfs_mutex);
91447636 830
2d21ac55 831 (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
91447636 832exit:
2d21ac55
A
833 if (vp) {
834 hfs_unlock(VTOC(vp));
835 }
91447636
A
836 if (result) {
837 if (btcb) {
838 FREE (btcb, M_TEMP);
839 }
2d21ac55
A
840 if (vp) {
841 vnode_put(vp);
842 }
843 /* XXX need to give back blocks ? */
844 }
845 if (intrans) {
846 hfs_end_transaction(hfsmp);
91447636 847 }
91447636 848
2d21ac55
A
849 /*
850 * All done, clear HFS_CREATING_BTREE, and wake up any sleepers.
851 */
852 lck_mtx_lock(&hfsmp->hfs_mutex);
853 hfsmp->hfs_flags &= ~HFS_CREATING_BTREE;
854 wakeup((caddr_t)hfsmp->hfs_attribute_cp);
855 lck_mtx_unlock(&hfsmp->hfs_mutex);
91447636 856
2d21ac55
A
857 return (result);
858}
91447636 859