2 // lf_hfs_btree_allocate.c
5 // Created by Yakov Ben Zaken on 22/03/2018.
7 #include "lf_hfs_btrees_io.h"
8 #include "lf_hfs_endian.h"
9 #include "lf_hfs_btrees_private.h"
10 #include "lf_hfs_vfsutils.h"
11 #include "lf_hfs_generic_buf.h"
13 ///////////////////// Routines Internal To BTreeAllocate.c //////////////////////
15 static OSStatus
GetMapNode (BTreeControlBlockPtr btreePtr
,
16 BlockDescriptor
*nodePtr
,
20 /////////////////////////////////////////////////////////////////////////////////
22 /*-------------------------------------------------------------------------------
24 Routine: AllocateNode - Find Free Node, Mark It Used, and Return Node Number.
26 Function: Searches the map records for the first free node, marks it "in use" and
27 returns the node number found. This routine should really only be called
28 when we know there are free blocks, otherwise it's just a waste of time.
30 Note: We have to examine map nodes a word at a time rather than a long word
31 because the External BTree Mgr used map records that were not an integral
32 number of long words. Too bad. In our spare time could develop a more
33 sophisticated algorithm that read map records by long words (and long
34 word aligned) and handled the spare bytes at the beginning and end
37 Input: btreePtr - pointer to control block for BTree file
39 Output: nodeNum - number of node allocated
42 Result: noErr - success
43 fsBTNoMoreMapNodesErr - no free blocks were found
45 -------------------------------------------------------------------------------*/
47 OSStatus
AllocateNode (BTreeControlBlockPtr btreePtr
, u_int32_t
*nodeNum
)
51 u_int16_t
*mapPtr
, *pos
;
52 u_int16_t mapSize
, size
;
59 nodeNumber
= 0; // first node number of header map record
60 node
.buffer
= nil
; // clear node.buffer to get header node
61 // - and for ErrorExit
62 node
.blockHeader
= nil
;
66 err
= GetMapNode (btreePtr
, &node
, &mapPtr
, &mapSize
);
70 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
72 //////////////////////// Find Word with Free Bit ////////////////////////////
76 size
>>= 1; // convert to number of words
77 //assumes mapRecords contain an integral number of words
81 if ( *pos
++ != 0xFFFF ) // assume test fails, and increment pos
85 --pos
; // whoa! backup
87 if (*pos
!= 0xFFFF) // hey, we got one!
90 nodeNumber
+= mapSize
<< 3; // covert to number of bits (nodes)
93 ///////////////////////// Find Free Bit in Word /////////////////////////////
95 freeWord
= SWAP_BE16 (*pos
);
100 if ( (freeWord
& mask
) == 0)
103 } while (--bitOffset
);
105 ////////////////////// Calculate Free Node Number ///////////////////////////
107 nodeNumber
+= ((pos
- mapPtr
) << 4) + (15 - bitOffset
); // (pos-mapPtr) = # of words!
110 ///////////////////////// Check for End of Map //////////////////////////////
112 if (nodeNumber
>= btreePtr
->totalNodes
)
118 /////////////////////////// Allocate the Node ///////////////////////////////
120 *pos
|= SWAP_BE16 (mask
); // set the map bit for the node
122 err
= UpdateNode (btreePtr
, &node
, 0, kLockTransaction
);
125 --btreePtr
->freeNodes
;
126 M_BTreeHeaderDirty(btreePtr
);
128 /* Account for allocations from node reserve */
129 BTUpdateReserve(btreePtr
, 1);
131 *nodeNum
= nodeNumber
;
135 ////////////////////////////////// Error Exit ///////////////////////////////////
139 (void) ReleaseNode (btreePtr
, &node
);
147 /*-------------------------------------------------------------------------------
149 Routine: FreeNode - Clear allocation bit for node.
151 Function: Finds the bit representing the node specified by nodeNum in the node
152 map and clears the bit.
155 Input: btreePtr - pointer to control block for BTree file
156 nodeNum - number of node to mark free
160 Result: noErr - success
161 fsBTNoMoreMapNodesErr - node number is beyond end of node map
162 != noErr - GetNode or ReleaseNode encountered some difficulty
163 -------------------------------------------------------------------------------*/
165 OSStatus
FreeNode (BTreeControlBlockPtr btreePtr
, u_int32_t nodeNum
)
168 BlockDescriptor node
;
170 u_int16_t mapSize
= 0;
171 u_int16_t
*mapPos
= NULL
;
175 //////////////////////////// Find Map Record ////////////////////////////////
176 nodeIndex
= 0; // first node number of header map record
177 node
.buffer
= nil
; // invalidate node.buffer to get header node
178 node
.blockHeader
= nil
;
180 while (nodeNum
>= nodeIndex
)
182 err
= GetMapNode (btreePtr
, &node
, &mapPos
, &mapSize
);
185 nodeIndex
+= mapSize
<< 3; // covert to number of bits (nodes)
188 //////////////////////////// Mark Node Free /////////////////////////////////
191 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
193 nodeNum
-= (nodeIndex
- (mapSize
<< 3)); // relative to this map record
194 bitOffset
= 15 - (nodeNum
& 0x0000000F); // last 4 bits are bit offset
195 mapPos
+= nodeNum
>> 4; // point to word containing map bit
197 M_SWAP_BE16_ClearBitNum (*mapPos
, bitOffset
); // clear it
199 err
= UpdateNode (btreePtr
, &node
, 0, kLockTransaction
);
202 ++btreePtr
->freeNodes
;
203 M_BTreeHeaderDirty(btreePtr
);
209 (void) ReleaseNode (btreePtr
, &node
);
216 /*-------------------------------------------------------------------------------
218 Routine: ExtendBTree - Call FSAgent to extend file, and allocate necessary map nodes.
220 Function: This routine calls the the FSAgent to extend the end of fork, if necessary,
221 to accomodate the number of nodes requested. It then allocates as many
222 map nodes as are necessary to account for all the nodes in the B*Tree.
223 If newTotalNodes is less than the current number of nodes, no action is
226 Note: Internal HFS File Manager BTree Module counts on an integral number of
227 long words in map records, although they are not long word aligned.
229 Input: btreePtr - pointer to control block for BTree file
230 newTotalNodes - total number of nodes the B*Tree is to extended to
234 Result: noErr - success
236 -------------------------------------------------------------------------------*/
238 OSStatus
ExtendBTree (BTreeControlBlockPtr btreePtr
,
239 u_int32_t newTotalNodes
)
243 FSSize minEOF
, maxEOF
;
245 u_int32_t oldTotalNodes
;
246 u_int32_t newMapNodes
;
247 u_int32_t mapBits
, totalMapBits
;
248 u_int32_t recStartBit
;
249 u_int32_t nodeNum
, nextNodeNum
;
250 u_int32_t firstNewMapNodeNum
, lastNewMapNodeNum
;
251 BlockDescriptor mapNode
, newNode
;
255 u_int16_t mapNodeRecSize
;
256 u_int32_t bitInWord
, bitInRecord
;
260 oldTotalNodes
= btreePtr
->totalNodes
;
261 if (newTotalNodes
<= oldTotalNodes
) // we're done!
264 nodeSize
= btreePtr
->nodeSize
;
265 filePtr
= GetFileControlBlock(btreePtr
->fileRefNum
);
267 mapNode
.buffer
= nil
;
268 mapNode
.blockHeader
= nil
;
269 newNode
.buffer
= nil
;
270 newNode
.blockHeader
= nil
;
272 mapNodeRecSize
= nodeSize
- sizeof(BTNodeDescriptor
) - 6; // 2 bytes of free space (see note)
275 //////////////////////// Count Bits In Node Map /////////////////////////////
279 err
= GetMapNode (btreePtr
, &mapNode
, &mapStart
, &mapSize
);
282 mapBits
= mapSize
<< 3; // mapSize (in bytes) * 8
283 recStartBit
= totalMapBits
; // bit number of first bit in map record
284 totalMapBits
+= mapBits
;
286 } while ( ((BTNodeDescriptor
*)mapNode
.buffer
)->fLink
!= 0 );
289 if (totalMapBits
!= CalcMapBits (btreePtr
))
290 LFHFS_LOG(LEVEL_ERROR
, "ExtendBTree: totalMapBits != CalcMapBits");
293 /////////////////////// Extend LEOF If Necessary ////////////////////////////
295 minEOF
= (u_int64_t
)newTotalNodes
* (u_int64_t
)nodeSize
;
296 if ( (u_int64_t
)filePtr
->fcbEOF
< minEOF
)
298 maxEOF
= (u_int64_t
)0x7fffffffLL
* (u_int64_t
)nodeSize
;
300 err
= btreePtr
->setEndOfForkProc (btreePtr
->fileRefNum
, minEOF
, maxEOF
);
305 //////////////////// Calc New Total Number Of Nodes /////////////////////////
307 newTotalNodes
= (uint32_t)(filePtr
->fcbEOF
/ nodeSize
); // hack!
308 // do we wish to perform any verification of newTotalNodes at this point?
310 btreePtr
->totalNodes
= newTotalNodes
; // do we need to update freeNodes here too?
313 ////////////// Calculate Number Of New Map Nodes Required ///////////////////
316 if (newTotalNodes
> totalMapBits
)
318 newMapNodes
= (((newTotalNodes
- totalMapBits
) >> 3) / mapNodeRecSize
) + 1;
319 firstNewMapNodeNum
= oldTotalNodes
;
320 lastNewMapNodeNum
= firstNewMapNodeNum
+ newMapNodes
- 1;
324 err
= ReleaseNode (btreePtr
, &mapNode
);
331 /////////////////////// Initialize New Map Nodes ////////////////////////////
332 // XXXdbg - this is the correct place for this:
333 ModifyBlockStart(btreePtr
->fileRefNum
, &mapNode
);
335 ((BTNodeDescriptor
*)mapNode
.buffer
)->fLink
= firstNewMapNodeNum
;
337 nodeNum
= firstNewMapNodeNum
;
340 err
= GetNewNode (btreePtr
, nodeNum
, &newNode
);
344 ModifyBlockStart(btreePtr
->fileRefNum
, &newNode
);
346 ((NodeDescPtr
)newNode
.buffer
)->numRecords
= 1;
347 ((NodeDescPtr
)newNode
.buffer
)->kind
= kBTMapNode
;
349 // set free space offset
350 *(u_int16_t
*)((Ptr
)newNode
.buffer
+ nodeSize
- 4) = nodeSize
- 6;
352 if (nodeNum
++ == lastNewMapNodeNum
)
355 ((BTNodeDescriptor
*)newNode
.buffer
)->fLink
= nodeNum
; // point to next map node
357 err
= UpdateNode (btreePtr
, &newNode
, 0, kLockTransaction
);
361 err
= UpdateNode (btreePtr
, &newNode
, 0, kLockTransaction
);
365 ///////////////////// Mark New Map Nodes Allocated //////////////////////////
367 nodeNum
= firstNewMapNodeNum
;
369 bitInRecord
= nodeNum
- recStartBit
;
371 while (bitInRecord
>= mapBits
)
373 nextNodeNum
= ((NodeDescPtr
)mapNode
.buffer
)->fLink
;
374 if ( nextNodeNum
== 0)
376 err
= fsBTNoMoreMapNodesErr
;
380 err
= UpdateNode (btreePtr
, &mapNode
, 0, kLockTransaction
);
383 err
= GetNode (btreePtr
, nextNodeNum
, 0, &mapNode
);
387 ModifyBlockStart(btreePtr
->fileRefNum
, &mapNode
);
391 mapStart
= (u_int16_t
*) GetRecordAddress (btreePtr
, mapNode
.buffer
, mapIndex
);
392 mapSize
= GetRecordSize (btreePtr
, mapNode
.buffer
, mapIndex
);
395 if (mapSize
!= M_MapRecordSize (btreePtr
->nodeSize
) )
397 LFHFS_LOG(LEVEL_ERROR
, "ExtendBTree: mapSize != M_MapRecordSize");
401 mapBits
= mapSize
<< 3; // mapSize (in bytes) * 8
402 recStartBit
= totalMapBits
; // bit number of first bit in map record
403 totalMapBits
+= mapBits
;
405 bitInRecord
= nodeNum
- recStartBit
;
408 mapPos
= mapStart
+ ((nodeNum
- recStartBit
) >> 4);
409 bitInWord
= 15 - ((nodeNum
- recStartBit
) & 0x0000000F);
411 M_SWAP_BE16_SetBitNum (*mapPos
, bitInWord
);
415 } while (nodeNum
<= lastNewMapNodeNum
);
417 err
= UpdateNode (btreePtr
, &mapNode
, 0, kLockTransaction
);
421 //////////////////////////////// Success ////////////////////////////////////
425 btreePtr
->totalNodes
= newTotalNodes
;
426 btreePtr
->freeNodes
+= (newTotalNodes
- oldTotalNodes
) - newMapNodes
;
428 M_BTreeHeaderDirty(btreePtr
);
430 /* Force the b-tree header changes to disk */
431 (void) UpdateHeader (btreePtr
, true);
436 ////////////////////////////// Error Exit ///////////////////////////////////
440 (void) ReleaseNode (btreePtr
, &mapNode
);
441 (void) ReleaseNode (btreePtr
, &newNode
);
448 /*-------------------------------------------------------------------------------
450 Routine: GetMapNode - Get the next map node and pointer to the map record.
452 Function: Given a BlockDescriptor to a map node in nodePtr, GetMapNode releases
453 it and gets the next node. If nodePtr->buffer is nil, then the header
457 Input: btreePtr - pointer to control block for BTree file
458 nodePtr - pointer to a BlockDescriptor of a map node
460 Output: nodePtr - pointer to the BlockDescriptor for the next map node
461 mapPtr - pointer to the map record within the map node
462 mapSize - number of bytes in the map record
464 Result: noErr - success
465 fsBTNoMoreMapNodesErr - we've run out of map nodes
466 fsBTInvalidNodeErr - bad node, or not node type kMapNode
468 -------------------------------------------------------------------------------*/
471 OSStatus
GetMapNode (BTreeControlBlockPtr btreePtr
,
472 BlockDescriptor
*nodePtr
,
478 u_int32_t nextNodeNum
;
480 if (nodePtr
->buffer
!= nil
) // if iterator is valid...
482 nextNodeNum
= ((NodeDescPtr
)nodePtr
->buffer
)->fLink
;
483 if (nextNodeNum
== 0)
485 err
= fsBTNoMoreMapNodesErr
;
489 err
= ReleaseNode (btreePtr
, nodePtr
);
492 err
= GetNode (btreePtr
, nextNodeNum
, 0, nodePtr
);
495 if ( ((NodeDescPtr
)nodePtr
->buffer
)->kind
!= kBTMapNode
)
497 err
= fsBTBadNodeType
;
501 ++btreePtr
->numMapNodesRead
;
504 err
= GetNode (btreePtr
, kHeaderNodeNum
, 0, nodePtr
);
507 if ( ((NodeDescPtr
)nodePtr
->buffer
)->kind
!= kBTHeaderNode
)
509 err
= fsBTInvalidHeaderErr
; //or fsBTBadNodeType
517 *mapPtr
= (u_int16_t
*) GetRecordAddress (btreePtr
, nodePtr
->buffer
, mapIndex
);
518 *mapSize
= GetRecordSize (btreePtr
, nodePtr
->buffer
, mapIndex
);
525 (void) ReleaseNode (btreePtr
, nodePtr
);
535 ////////////////////////////////// CalcMapBits //////////////////////////////////
537 u_int32_t
CalcMapBits (BTreeControlBlockPtr btreePtr
)
541 mapBits
= (u_int32_t
)(M_HeaderMapRecordSize (btreePtr
->nodeSize
) << 3);
543 while (mapBits
< btreePtr
->totalNodes
)
544 mapBits
+= M_MapRecordSize (btreePtr
->nodeSize
) << 3;
549 /*-------------------------------------------------------------------------------
550 Routine: BTZeroUnusedNodes
552 Function: Write zeros to all nodes in the B-tree that are not currently in use.
553 -------------------------------------------------------------------------------*/
555 BTZeroUnusedNodes(FCB
*filePtr
)
558 u_int16_t
*mapPtr
, *pos
;
559 u_int16_t mapSize
, size
;
564 vnode_t vp
= FTOV(filePtr
);
565 BTreeControlBlockPtr btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
566 GenericLFBufPtr bp
= NULL
;
567 u_int32_t nodeNumber
= 0;
568 BlockDescriptor mapNode
= {0};
569 mapNode
.buffer
= nil
;
570 mapNode
.blockHeader
= nil
;
572 /* Iterate over map nodes. */
575 err
= GetMapNode (btreePtr
, &mapNode
, &mapPtr
, &mapSize
);
578 err
= MacToVFSError(err
);
584 size
>>= 1; /* convert to number of 16-bit words */
586 /* Iterate over 16-bit words in the map record. */
589 if (*pos
!= 0xFFFF) /* Anything free in this word? */
591 word
= SWAP_BE16(*pos
);
593 /* Iterate over bits in the word. */
594 for (bitNumber
= 0, mask
= 0x8000;
596 ++bitNumber
, mask
>>= 1)
599 continue; /* This node is in use. */
601 if (nodeNumber
+ bitNumber
>= btreePtr
->totalNodes
)
603 /* We've processed all of the nodes. */
608 * Get a buffer full of zeros and write it to the unused
609 * node. Since we'll probably be writing a lot of nodes,
610 * bypass the journal (to avoid a transaction that's too
611 * big). Instead, this behaves more like clearing out
612 * nodes when extending a B-tree (eg., ClearBTNodes).
614 bp
= lf_hfs_generic_buf_allocate(vp
, nodeNumber
+ bitNumber
, btreePtr
->nodeSize
, 0); // buf_getblk(vp, nodeNumber + bitNumber, btreePtr->nodeSize, 0, 0, BLK_META);
617 LFHFS_LOG(LEVEL_ERROR
, "BTZeroUnusedNodes: unable to read node %u\n", nodeNumber
+ bitNumber
);
622 if (bp
->uCacheFlags
& GEN_BUF_WRITE_LOCK
) {
624 * This node is already part of a transaction and will be written when
625 * the transaction is committed, so don't write it here. If we did, then
626 * we'd hit a panic in hfs_vnop_bwrite because the B_LOCKED bit is still set.
628 lf_hfs_generic_buf_release(bp
);
632 lf_hfs_generic_buf_clear(bp
);
634 err
= lf_hfs_generic_buf_write(bp
);
639 lf_hfs_generic_buf_release(bp
);
643 /* Go to the next word in the bitmap */
651 (void) ReleaseNode(btreePtr
, &mapNode
);