2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
4 * This file may be used under the terms of the MIT License.
8 //! BTree implementation
16 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
18 # define TRACE(x...) ;
20 # define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
23 BTree::Node::Node(Volume
* volume
)
33 BTree::Node::Node(Volume
* volume
, off_t block
)
60 block_cache_put(fVolume
->BlockCache(), fBlockNumber
);
67 BTree::Node::SetTo(off_t block
)
71 fNode
= (btrfs_stream
*)block_cache_get(fVolume
->BlockCache(), block
);
76 BTree::Node::SetToWritable(off_t block
, int32 transactionId
, bool empty
)
82 fNode
= (btrfs_stream
*)block_cache_get_empty(fVolume
->BlockCache(),
83 block
, transactionId
);
85 fNode
= (btrfs_stream
*)block_cache_get_writable(fVolume
->BlockCache(),
86 block
, transactionId
);
92 BTree::Node::SearchSlot(const btrfs_key
& key
, int* slot
, btree_traversing type
)
95 //binary search for item slot in a node
96 int entrySize
= sizeof(btrfs_entry
);
99 entrySize
= sizeof(btrfs_index
);
102 int high
= ItemCount();
103 int low
= 0, mid
= 0, comp
= 0;
104 uint8
* base
= (uint8
*)fNode
+ sizeof(btrfs_header
);
105 const btrfs_key
* other
;
107 mid
= (low
+ high
) / 2;
108 other
= (const btrfs_key
*)(base
+ mid
* entrySize
);
109 comp
= key
.Compare(*other
);
116 return B_OK
; //if key is in node
120 // |--item1--|--item2--|--item3--|--etc--|
124 if (type
== BTREE_BACKWARD
) {
128 } else if (type
== BTREE_FORWARD
) {
134 if (type
== BTREE_EXACT
|| *slot
< 0)
135 return B_ENTRY_NOT_FOUND
;
137 TRACE("SearchSlot() found slot %" B_PRId32
" comp %" B_PRId32
"\n",
144 * calculate used space except the header.
145 * type is only for leaf node
146 * type 1: only item space
147 * type 2: only item data space
148 * type 3: both type 1 and 2
151 BTree::Node::_CalculateSpace(uint32 from
, uint32 to
, uint8 type
) const
153 if (to
< from
|| to
>= ItemCount())
157 return sizeof(btrfs_index
) * (to
- from
+ 1);
160 if ((type
& 1) == 1) {
161 result
+= sizeof(btrfs_entry
) * (to
- from
+ 1);
163 if ((type
& 2) == 2) {
164 result
+= Item(from
)->Offset() + Item(from
)->Size()
165 - Item(to
)->Offset();
173 BTree::Node::SpaceUsed() const
176 return _CalculateSpace(0, ItemCount() - 1, 3);
177 return _CalculateSpace(0, ItemCount() - 1, 0);
182 BTree::Node::SpaceLeft() const
184 return fVolume
->BlockSize() - SpaceUsed() - sizeof(btrfs_header
);
189 BTree::Node::_SpaceCheck(int length
) const
191 // this is a little bit weird here because we can't find
192 // any suitable error code
193 if (length
< 0 && std::abs(length
) >= SpaceUsed())
194 return B_DIRECTORY_NOT_EMPTY
; // not enough data to delete
195 if (length
> 0 && length
>= SpaceLeft())
196 return B_DEVICE_FULL
; // no spare space
201 // #pragma mark - BTree::Path implementation
204 BTree::Path::Path(BTree
* tree
)
208 for (int i
= 0; i
< BTRFS_MAX_TREE_DEPTH
; ++i
) {
217 for (int i
= 0; i
< BTRFS_MAX_TREE_DEPTH
; ++i
) {
226 BTree::Path::GetNode(int level
, int* _slot
) const
229 *_slot
= fSlots
[level
];
230 return fNodes
[level
];
235 BTree::Path::SetNode(off_t block
, int slot
)
237 Node
node(fTree
->SystemVolume(), block
);
238 return SetNode(&node
, slot
);
243 BTree::Path::SetNode(const Node
* node
, int slot
)
245 uint8 level
= node
->Level();
246 if (fNodes
[level
] == NULL
) {
247 fNodes
[level
] = new Node(fTree
->SystemVolume(), node
->BlockNum());
248 if (fNodes
[level
] == NULL
)
251 fNodes
[level
]->SetTo(node
->BlockNum());
254 fSlots
[level
] = fNodes
[level
]->ItemCount() - 1;
256 fSlots
[level
] = slot
;
257 return fNodes
[level
];
262 BTree::Path::Move(int level
, int step
)
264 fSlots
[level
] += step
;
265 if (fSlots
[level
] < 0)
267 if (fSlots
[level
] >= fNodes
[level
]->ItemCount())
274 BTree::Path::GetEntry(int slot
, btrfs_key
* _key
, void** _value
, uint32
* _size
,
277 BTree::Node
* leaf
= fNodes
[0];
278 if (slot
< 0 || slot
>= leaf
->ItemCount())
279 return B_ENTRY_NOT_FOUND
;
282 *_key
= leaf
->Item(slot
)->key
;
284 uint32 itemSize
= leaf
->Item(slot
)->Size();
285 if (_value
!= NULL
) {
286 *_value
= malloc(itemSize
);
290 memcpy(*_value
, leaf
->ItemData(slot
), itemSize
);
297 *_offset
= leaf
->Item(slot
)->Offset();
303 // #pragma mark - BTree implementation
306 BTree::BTree(Volume
* volume
)
311 mutex_init(&fIteratorLock
, "btrfs b+tree iterator");
315 BTree::BTree(Volume
* volume
, btrfs_stream
* stream
)
320 mutex_init(&fIteratorLock
, "btrfs b+tree iterator");
324 BTree::BTree(Volume
* volume
, fsblock_t rootBlock
)
326 fRootBlock(rootBlock
),
329 mutex_init(&fIteratorLock
, "btrfs b+tree iterator");
335 // if there are any TreeIterators left, we need to stop them
336 // (can happen when the tree's inode gets deleted while
337 // traversing the tree - a TreeIterator doesn't lock the inode)
338 mutex_lock(&fIteratorLock
);
340 SinglyLinkedList
<TreeIterator
>::Iterator iterator
341 = fIterators
.GetIterator();
342 while (iterator
.HasNext())
343 iterator
.Next()->Stop();
344 mutex_destroy(&fIteratorLock
);
349 btrfs_key::Compare(const btrfs_key
& key
) const
351 if (ObjectID() > key
.ObjectID())
353 if (ObjectID() < key
.ObjectID())
355 if (Type() > key
.Type())
357 if (Type() < key
.Type())
359 if (Offset() > key
.Offset())
361 if (Offset() < key
.Offset())
367 /* Traverse from root to fill in the path along way its finding.
368 * Return current slot at leaf if successful.
371 BTree::Traverse(btree_traversing type
, Path
* path
, const btrfs_key
& key
)
374 TRACE("BTree::Traverse() objectid %" B_PRId64
" type %d offset %"
375 B_PRId64
" \n", key
.ObjectID(), key
.Type(), key
.Offset());
376 fsblock_t physicalBlock
= fRootBlock
;
377 Node
node(fVolume
, physicalBlock
);
379 status_t status
= B_OK
;
381 while (node
.Level() != 0) {
382 TRACE("BTree::Traverse() level %d count %d\n", node
.Level(),
384 status
= node
.SearchSlot(key
, &slot
, BTREE_BACKWARD
);
387 if (path
->SetNode(&node
, slot
) == NULL
)
390 TRACE("BTree::Traverse() getting index %" B_PRIu32
"\n", slot
);
392 status
= fVolume
->FindBlock(node
.Index(slot
)->LogicalAddress(),
394 if (status
!= B_OK
) {
395 ERROR("BTree::Traverse() unmapped block %" B_PRId64
"\n",
396 node
.Index(slot
)->LogicalAddress());
399 node
.SetTo(physicalBlock
);
402 TRACE("BTree::Traverse() dump count %" B_PRId32
"\n", node
.ItemCount());
403 status
= node
.SearchSlot(key
, &slot
, type
);
406 if (path
->SetNode(&node
, slot
) == NULL
)
409 TRACE("BTree::Traverse() found %" B_PRIu32
" %" B_PRIu32
"\n",
410 node
.Item(slot
)->Offset(), node
.Item(slot
)->Size());
415 /*! Searches the key in the tree, and stores the allocated found item in
416 _value, if successful.
417 Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND if not.
418 It can also return other errors to indicate that something went wrong.
421 BTree::_Find(Path
* path
, btrfs_key
& wanted
, void** _value
, uint32
* _size
,
422 uint32
* _offset
, btree_traversing type
) const
424 status_t status
= Traverse(type
, path
, wanted
);
429 status
= path
->GetCurrentEntry(&found
, _value
, _size
, _offset
);
433 if (found
.Type() != wanted
.Type() && wanted
.Type() != BTRFS_KEY_TYPE_ANY
)
434 return B_ENTRY_NOT_FOUND
;
442 BTree::FindNext(Path
* path
, btrfs_key
& key
, void** _value
, uint32
* _size
,
443 uint32
* _offset
) const
445 return _Find(path
, key
, _value
, _size
, _offset
, BTREE_FORWARD
);
450 BTree::FindPrevious(Path
* path
, btrfs_key
& key
, void** _value
, uint32
* _size
,
451 uint32
* _offset
) const
453 return _Find(path
, key
, _value
, _size
, _offset
, BTREE_BACKWARD
);
458 BTree::FindExact(Path
* path
, btrfs_key
& key
, void** _value
, uint32
* _size
,
459 uint32
* _offset
) const
461 return _Find(path
, key
, _value
, _size
, _offset
, BTREE_EXACT
);
466 BTree::PreviousLeaf(Path
* path
) const
468 // TODO: use Traverse() ???
472 // iterate to the root until satisfy the condition
474 node
= path
->GetNode(level
, &slot
);
475 if (node
== NULL
|| slot
!= 0)
480 // the current leaf is already the left most leaf or
481 // path was not initialized
483 return B_ENTRY_NOT_FOUND
;
485 path
->Move(level
, BTREE_BACKWARD
);
486 fsblock_t physicalBlock
;
487 // change all nodes below this level and slot to the ending
489 status_t status
= fVolume
->FindBlock(
490 node
->Index(slot
)->LogicalAddress(), physicalBlock
);
494 node
= path
->SetNode(physicalBlock
, -1);
497 slot
= node
->ItemCount() - 1;
506 BTree::NextLeaf(Path
* path
) const
511 // iterate to the root until satisfy the condition
513 node
= path
->GetNode(level
, &slot
);
514 if (node
== NULL
|| slot
< node
->ItemCount() - 1)
519 // the current leaf is already the right most leaf or
520 // path was not initialized
522 return B_ENTRY_NOT_FOUND
;
524 path
->Move(level
, BTREE_FORWARD
);
525 fsblock_t physicalBlock
;
526 // change all nodes below this level and slot to the beginning
528 status_t status
= fVolume
->FindBlock(
529 node
->Index(slot
)->LogicalAddress(), physicalBlock
);
533 node
= path
->SetNode(physicalBlock
, 0);
545 BTree::SetRoot(off_t logical
, fsblock_t
* block
)
549 //TODO: mapping physical block -> logical address
551 fLogicalRoot
= logical
;
552 if (fVolume
->FindBlock(logical
, fRootBlock
) != B_OK
) {
553 ERROR("Find() unmapped block %" B_PRId64
"\n", fRootBlock
);
562 BTree::_AddIterator(TreeIterator
* iterator
)
564 MutexLocker
_(fIteratorLock
);
565 fIterators
.Add(iterator
);
570 BTree::_RemoveIterator(TreeIterator
* iterator
)
572 MutexLocker
_(fIteratorLock
);
573 fIterators
.Remove(iterator
);
580 TreeIterator::TreeIterator(BTree
* tree
, const btrfs_key
& key
)
584 fIteratorStatus(B_NO_INIT
)
586 tree
->_AddIterator(this);
587 fPath
= new(std::nothrow
) BTree::Path(tree
);
589 fIteratorStatus
= B_NO_MEMORY
;
593 TreeIterator::~TreeIterator()
596 fTree
->_RemoveIterator(this);
604 TreeIterator::Rewind(bool inverse
)
607 fKey
.SetOffset(BTREE_END
);
609 fKey
.SetOffset(BTREE_BEGIN
);
610 fIteratorStatus
= B_NO_INIT
;
614 /*! Iterates through the tree in the specified direction.
617 TreeIterator::_Traverse(btree_traversing direction
)
619 status_t status
= fTree
->Traverse(direction
, fPath
, fKey
);
621 ERROR("TreeIterator::Traverse() Find failed\n");
625 return (fIteratorStatus
= B_OK
);
629 // Like GetEntry in BTree::Path but here we check the type and moving.
631 TreeIterator::_GetEntry(btree_traversing type
, void** _value
, uint32
* _size
,
635 if (fIteratorStatus
== B_NO_INIT
) {
636 status
= _Traverse(type
);
642 if (fIteratorStatus
!= B_OK
)
643 return fIteratorStatus
;
645 int move
= fPath
->Move(0, type
);
647 status
= fTree
->NextLeaf(fPath
);
649 status
= fTree
->PreviousLeaf(fPath
);
654 status
= fPath
->GetCurrentEntry(&found
, _value
, _size
, _offset
);
658 fKey
.SetObjectID(found
.ObjectID());
659 fKey
.SetOffset(found
.Offset());
660 if (fKey
.Type() != found
.Type() && fKey
.Type() != BTRFS_KEY_TYPE_ANY
)
661 return B_ENTRY_NOT_FOUND
;
667 /*! just sets the current key in the iterator.
670 TreeIterator::Find(const btrfs_key
& key
)
672 if (fIteratorStatus
== B_INTERRUPTED
)
673 return fIteratorStatus
;
676 fIteratorStatus
= B_NO_INIT
;
685 fIteratorStatus
= B_INTERRUPTED
;