xref: /haiku/src/add-ons/kernel/file_systems/btrfs/BTree.cpp (revision 690d16c62820a1f4d5fb3dd83a5ed00bb571402a)
1299aba38Shyche /*
299768086Shyche  * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com.
3299aba38Shyche  * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
4299aba38Shyche  * Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
5299aba38Shyche  * This file may be used under the terms of the MIT License.
6299aba38Shyche  */
7299aba38Shyche 
8299aba38Shyche 
9299aba38Shyche //! BTree implementation
10299aba38Shyche 
11299aba38Shyche 
12299aba38Shyche #include "BTree.h"
13883b9bf2Shyche #include "Journal.h"
14299aba38Shyche 
1552850062SAugustin Cavalier #include <algorithm>
1652850062SAugustin Cavalier 
17299aba38Shyche 
18299aba38Shyche //#define TRACE_BTRFS
19299aba38Shyche #ifdef TRACE_BTRFS
20299aba38Shyche #	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
21299aba38Shyche #else
22299aba38Shyche #	define TRACE(x...) ;
23299aba38Shyche #endif
24299aba38Shyche #	define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
25299aba38Shyche 
26299aba38Shyche 
278160f31fShyche BTree::Node::Node(Volume* volume)
281481c49cShyche 	:
291481c49cShyche 	fNode(NULL),
308160f31fShyche 	fVolume(volume),
311481c49cShyche 	fBlockNumber(0),
321481c49cShyche 	fWritable(false)
331481c49cShyche {
341481c49cShyche }
351481c49cShyche 
361481c49cShyche 
378160f31fShyche BTree::Node::Node(Volume* volume, off_t block)
381481c49cShyche 	:
391481c49cShyche 	fNode(NULL),
408160f31fShyche 	fVolume(volume),
411481c49cShyche 	fBlockNumber(0),
421481c49cShyche 	fWritable(false)
431481c49cShyche {
441481c49cShyche 	SetTo(block);
451481c49cShyche }
461481c49cShyche 
471481c49cShyche 
48548a0d80Shyche BTree::Node::~Node()
491481c49cShyche {
501481c49cShyche 	Unset();
511481c49cShyche }
521481c49cShyche 
531481c49cShyche 
541481c49cShyche void
55548a0d80Shyche BTree::Node::Keep()
561481c49cShyche {
571481c49cShyche 	fNode = NULL;
581481c49cShyche }
591481c49cShyche 
601481c49cShyche void
61548a0d80Shyche BTree::Node::Unset()
621481c49cShyche {
631481c49cShyche 	if (fNode != NULL) {
648160f31fShyche 		block_cache_put(fVolume->BlockCache(), fBlockNumber);
651481c49cShyche 		fNode = NULL;
661481c49cShyche 	}
671481c49cShyche }
681481c49cShyche 
691481c49cShyche 
701481c49cShyche void
71548a0d80Shyche BTree::Node::SetTo(off_t block)
721481c49cShyche {
731481c49cShyche 	Unset();
741481c49cShyche 	fBlockNumber = block;
758160f31fShyche 	fNode = (btrfs_stream*)block_cache_get(fVolume->BlockCache(), block);
761481c49cShyche }
771481c49cShyche 
781481c49cShyche 
791481c49cShyche void
80548a0d80Shyche BTree::Node::SetToWritable(off_t block, int32 transactionId, bool empty)
811481c49cShyche {
821481c49cShyche 	Unset();
831481c49cShyche 	fBlockNumber = block;
841481c49cShyche 	fWritable = true;
851481c49cShyche 	if (empty) {
868160f31fShyche 		fNode = (btrfs_stream*)block_cache_get_empty(fVolume->BlockCache(),
878160f31fShyche 			block, transactionId);
881481c49cShyche 	} else {
898160f31fShyche 		fNode = (btrfs_stream*)block_cache_get_writable(fVolume->BlockCache(),
908160f31fShyche 			block, transactionId);
911481c49cShyche 	}
921481c49cShyche }
931481c49cShyche 
941481c49cShyche 
952ed88a48Shyche status_t
962ed88a48Shyche BTree::Node::SearchSlot(const btrfs_key& key, int* slot, btree_traversing type)
972ed88a48Shyche 	const
9891d7f850Shyche {
9991d7f850Shyche 	//binary search for item slot in a node
10091d7f850Shyche 	int entrySize = sizeof(btrfs_entry);
10191d7f850Shyche 	if (Level() != 0) {
10291d7f850Shyche 		// internal node
10391d7f850Shyche 		entrySize = sizeof(btrfs_index);
10491d7f850Shyche 	}
10591d7f850Shyche 
10691d7f850Shyche 	int high = ItemCount();
1072ed88a48Shyche 	int low = 0, mid = 0, comp = 0;
1082ed88a48Shyche 	uint8* base = (uint8*)fNode + sizeof(btrfs_header);
10991d7f850Shyche 	const btrfs_key* other;
11091d7f850Shyche 	while (low < high) {
11191d7f850Shyche 		mid = (low + high) / 2;
1122ed88a48Shyche 		other = (const btrfs_key*)(base + mid * entrySize);
1130d726c5cShyche 		comp = key.Compare(*other);
11491d7f850Shyche 		if (comp < 0)
11591d7f850Shyche 			high = mid;
11691d7f850Shyche 		else if (comp > 0)
11791d7f850Shyche 			low = mid + 1;
11891d7f850Shyche 		else {
11991d7f850Shyche 			*slot = mid;
1200d726c5cShyche 			return B_OK; 		//if key is in node
12191d7f850Shyche 		}
12291d7f850Shyche 	}
1230d726c5cShyche 
1242ed88a48Shyche 	// |--item1--|--item2--|--item3--|--etc--|
1252ed88a48Shyche 	//     m-1        m        m+1
1262ed88a48Shyche 	//           k          		: comp < 0
1272ed88a48Shyche 	//                     k		: comp > 0
128b568492fShyche 	if (type == BTREE_BACKWARD && comp < 0)
129b568492fShyche 		mid--;
130b568492fShyche 	else if (type == BTREE_FORWARD && comp > 0)
131b568492fShyche 		mid++;
1322ed88a48Shyche 
133b568492fShyche 	if (type == BTREE_EXACT || mid < 0)
1342ed88a48Shyche 		return B_ENTRY_NOT_FOUND;
1352ed88a48Shyche 
136b568492fShyche 	*slot = mid;
1370d726c5cShyche 	TRACE("SearchSlot() found slot %" B_PRId32 " comp %" B_PRId32 "\n",
1380d726c5cShyche 		*slot, comp);
1390d726c5cShyche 	return B_OK;
14091d7f850Shyche }
14191d7f850Shyche 
14291d7f850Shyche 
14327ffe058Shyche /*
14427ffe058Shyche  * calculate used space except the header.
14527ffe058Shyche  * type is only for leaf node
14627ffe058Shyche  * type 1: only item space
14727ffe058Shyche  * type 2: only item data space
14827ffe058Shyche  * type 3: both type 1 and 2
14927ffe058Shyche  */
15027ffe058Shyche int
15127ffe058Shyche BTree::Node::_CalculateSpace(uint32 from, uint32 to, uint8 type) const
15227ffe058Shyche {
15327ffe058Shyche 	if (to < from || to >= ItemCount())
15427ffe058Shyche 		return 0;
15527ffe058Shyche 
15627ffe058Shyche 	if (Level() != 0)
15727ffe058Shyche 		return sizeof(btrfs_index) * (to - from + 1);
15827ffe058Shyche 
15927ffe058Shyche 	uint32 result = 0;
16027ffe058Shyche 	if ((type & 1) == 1) {
16127ffe058Shyche 		result += sizeof(btrfs_entry) * (to - from + 1);
16227ffe058Shyche 	}
16327ffe058Shyche 	if ((type & 2) == 2) {
16427ffe058Shyche 		result += Item(from)->Offset() + Item(from)->Size()
16527ffe058Shyche 			- Item(to)->Offset();
16627ffe058Shyche 	}
16727ffe058Shyche 
16827ffe058Shyche 	return result;
16927ffe058Shyche }
17027ffe058Shyche 
17127ffe058Shyche 
17227ffe058Shyche int
17327ffe058Shyche BTree::Node::SpaceUsed() const
17427ffe058Shyche {
17527ffe058Shyche 	if (Level() == 0)
17627ffe058Shyche 		return _CalculateSpace(0, ItemCount() - 1, 3);
17727ffe058Shyche 	return _CalculateSpace(0, ItemCount() - 1, 0);
17827ffe058Shyche }
17927ffe058Shyche 
18027ffe058Shyche 
18127ffe058Shyche int
18227ffe058Shyche BTree::Node::SpaceLeft() const
18327ffe058Shyche {
18427ffe058Shyche 	return fVolume->BlockSize() - SpaceUsed() - sizeof(btrfs_header);
18527ffe058Shyche }
18627ffe058Shyche 
18727ffe058Shyche 
18889484dc0Shyche void
18989484dc0Shyche BTree::Node::_Copy(const Node* origin, uint32 at, uint32 from, uint32 to,
19089484dc0Shyche 	int length) const
19189484dc0Shyche {
19289484dc0Shyche 	TRACE("Node::_Copy() at: %d from: %d to: %d length: %d\n",
19389484dc0Shyche 		at, from, to, length);
19489484dc0Shyche 
19589484dc0Shyche 	if (Level() == 0) {
19689484dc0Shyche 		memcpy(Item(at), origin->Item(from), origin->_CalculateSpace(from, to));
19789484dc0Shyche 		// Item offset of copied node must be changed to get the
19889484dc0Shyche 		// item data offset correctly. length can be zero
19989484dc0Shyche 		// but let it there doesn't harm anything.
20089484dc0Shyche 		for (uint32 i = at; i - at <= to - from; ++i)
20189484dc0Shyche 			Item(i)->SetOffset(Item(i)->Offset() - length);
20289484dc0Shyche 
20389484dc0Shyche 		memcpy(ItemData(at + to - from), origin->ItemData(to),
20489484dc0Shyche 			origin->_CalculateSpace(from, to, 2));
20589484dc0Shyche 	} else {
20689484dc0Shyche 		memcpy(Index(at), origin->Index(from),
20789484dc0Shyche 			origin->_CalculateSpace(from, to));
20889484dc0Shyche 	}
20989484dc0Shyche }
21089484dc0Shyche 
21189484dc0Shyche 
21227ffe058Shyche status_t
21327ffe058Shyche BTree::Node::_SpaceCheck(int length) const
21427ffe058Shyche {
21527ffe058Shyche 	// this is a little bit weird here because we can't find
21627ffe058Shyche 	// any suitable error code
21727ffe058Shyche 	if (length < 0 && std::abs(length) >= SpaceUsed())
21827ffe058Shyche 		return B_DIRECTORY_NOT_EMPTY;	// not enough data to delete
21927ffe058Shyche 	if (length > 0 && length >= SpaceLeft())
22027ffe058Shyche 		return B_DEVICE_FULL;			// no spare space
22127ffe058Shyche 	return B_OK;
22227ffe058Shyche }
22327ffe058Shyche 
22427ffe058Shyche 
22589484dc0Shyche /*
22689484dc0Shyche  * copy node header, items and items data
22789484dc0Shyche  * length is size to insert/remove
22889484dc0Shyche  * if node is a internal node, length isnt used
22989484dc0Shyche  * length = 0: Copy a whole
23089484dc0Shyche  * length < 0: removing
23189484dc0Shyche  * length > 0: inserting
23289484dc0Shyche  */
23389484dc0Shyche status_t
23489484dc0Shyche BTree::Node::Copy(const Node* origin, uint32 start, uint32 end, int length)
23589484dc0Shyche 	const
23689484dc0Shyche {
23789484dc0Shyche 	status_t status = origin->_SpaceCheck(length);
23889484dc0Shyche 	if (status != B_OK)
23989484dc0Shyche 		return status;
24089484dc0Shyche 
24189484dc0Shyche 	memcpy(fNode, origin->fNode, sizeof(btrfs_header));
24289484dc0Shyche 	if (length == 0) {
24389484dc0Shyche 		// like removing [0, start - 1] and keeping [start, end]
24489484dc0Shyche 		length = -origin->_CalculateSpace(0, start - 1, 2);
24589484dc0Shyche 		_Copy(origin, 0, start, end, length);
24689484dc0Shyche 	} else if (length < 0) {
24789484dc0Shyche 		//removing all items in [start, end]
24889484dc0Shyche 		if (start > 0)
24989484dc0Shyche 			_Copy(origin, 0, 0, start - 1, 0);	// <-- [start,...
25089484dc0Shyche 		if (end + 1 < origin->ItemCount()) {
25189484dc0Shyche 			// ..., end] -->
25289484dc0Shyche 			// we only care data size
25389484dc0Shyche 			length += origin->_CalculateSpace(start, end);
25489484dc0Shyche 			_Copy(origin, start, end + 1, origin->ItemCount() - 1, length);
25589484dc0Shyche 		}
25689484dc0Shyche 	} else {
25789484dc0Shyche 		//inserting in [start, end] - make a hole for later
25889484dc0Shyche 		if (start > 0)
25989484dc0Shyche 			_Copy(origin, 0, 0, start - 1, 0);
26089484dc0Shyche 		if (start < origin->ItemCount()) {
26189484dc0Shyche 			length -= origin->_CalculateSpace(start, end);
26289484dc0Shyche 			_Copy(origin, end + 1, start, origin->ItemCount() - 1, length);
26389484dc0Shyche 		}
26489484dc0Shyche 	}
26589484dc0Shyche 
26689484dc0Shyche 	return B_OK;
26789484dc0Shyche }
26889484dc0Shyche 
26989484dc0Shyche 
27089484dc0Shyche /* Like copy but here we use memmove on current node.
27189484dc0Shyche  */
27289484dc0Shyche status_t
27389484dc0Shyche BTree::Node::MoveEntries(uint32 start, uint32 end, int length) const
27489484dc0Shyche {
27589484dc0Shyche 	status_t status = _SpaceCheck(length);
27689484dc0Shyche 	if (status != B_OK || length == 0/*B_OK*/)
27789484dc0Shyche 		return status;
27889484dc0Shyche 
27989484dc0Shyche 	int entrySize = sizeof(btrfs_entry);
28089484dc0Shyche 	if (Level() != 0)
28189484dc0Shyche 		entrySize = sizeof(btrfs_index);
28289484dc0Shyche 
28389484dc0Shyche 	uint8* base = (uint8*)fNode + sizeof(btrfs_header);
28489484dc0Shyche 	end++;
28589484dc0Shyche 	if (length < 0) {
28689484dc0Shyche 		// removing [start, end]
28789484dc0Shyche 		TRACE("Node::MoveEntries() removing ... start %" B_PRIu32 " end %"
28889484dc0Shyche 			B_PRIu32 " length %i\n", start, end, length);
28989484dc0Shyche 		length += _CalculateSpace(start, end - 1);
29089484dc0Shyche 	} else {
29189484dc0Shyche 		// length > 0
29289484dc0Shyche 		// inserting into [start, end] - make room for later
29389484dc0Shyche 		TRACE("Node::MoveEntries() inserting ... start %" B_PRIu32 " end %"
29489484dc0Shyche 			B_PRIu32 " length %i\n", start, end, length);
29589484dc0Shyche 		length -= _CalculateSpace(start, end - 1);
29689484dc0Shyche 		std::swap(start, end);
29789484dc0Shyche 	}
29889484dc0Shyche 
29989484dc0Shyche 	if (end >= ItemCount())
30089484dc0Shyche 		return B_OK;
30189484dc0Shyche 
30289484dc0Shyche 	int dataSize = _CalculateSpace(end, ItemCount() - 1, 2);
30389484dc0Shyche 	// moving items/block pointers
30489484dc0Shyche 	memmove(base + start * entrySize, base + end * entrySize,
30589484dc0Shyche 		_CalculateSpace(end, ItemCount() - 1));
30689484dc0Shyche 	if (Level() == 0) {
30789484dc0Shyche 		// moving item data
30889484dc0Shyche 		int num = start - end;
30989484dc0Shyche 		for(uint32 i = start; i < ItemCount() + num; ++i)
31089484dc0Shyche 			Item(i)->SetOffset(Item(i)->Offset() - length);
31189484dc0Shyche 
31289484dc0Shyche 		memmove(ItemData(ItemCount() - 1) - length, ItemData(ItemCount() - 1),
31389484dc0Shyche 			dataSize);
31489484dc0Shyche 	}
31589484dc0Shyche 
31689484dc0Shyche 	return B_OK;
31789484dc0Shyche }
31889484dc0Shyche 
31989484dc0Shyche 
3203216460dShyche //	#pragma mark - BTree::Path implementation
3213216460dShyche 
3223216460dShyche 
3233216460dShyche BTree::Path::Path(BTree* tree)
3243216460dShyche 	:
3253216460dShyche 	fTree(tree)
3263216460dShyche {
3273216460dShyche 	for (int i = 0; i < BTRFS_MAX_TREE_DEPTH; ++i) {
3283216460dShyche 		fNodes[i] = NULL;
3293216460dShyche 		fSlots[i] = 0;
3303216460dShyche 	}
3313216460dShyche }
3323216460dShyche 
3333216460dShyche 
3343216460dShyche BTree::Path::~Path()
3353216460dShyche {
3363216460dShyche 	for (int i = 0; i < BTRFS_MAX_TREE_DEPTH; ++i) {
3373216460dShyche 		delete fNodes[i];
3383216460dShyche 		fNodes[i] = NULL;
3393216460dShyche 		fSlots[i] = 0;
3403216460dShyche 	}
3413216460dShyche }
3423216460dShyche 
3433216460dShyche 
3443216460dShyche BTree::Node*
3453216460dShyche BTree::Path::GetNode(int level, int* _slot) const
3463216460dShyche {
3473216460dShyche 	if (_slot != NULL)
3483216460dShyche 		*_slot = fSlots[level];
3493216460dShyche 	return fNodes[level];
3503216460dShyche }
3513216460dShyche 
3523216460dShyche 
3533216460dShyche BTree::Node*
3543216460dShyche BTree::Path::SetNode(off_t block, int slot)
3553216460dShyche {
3563216460dShyche 	Node node(fTree->SystemVolume(), block);
3573216460dShyche 	return SetNode(&node, slot);
3583216460dShyche }
3593216460dShyche 
3603216460dShyche 
3613216460dShyche BTree::Node*
3623216460dShyche BTree::Path::SetNode(const Node* node, int slot)
3633216460dShyche {
3643216460dShyche 	uint8 level = node->Level();
3653216460dShyche 	if (fNodes[level] == NULL) {
3663216460dShyche 		fNodes[level] = new Node(fTree->SystemVolume(), node->BlockNum());
3673216460dShyche 		if (fNodes[level] == NULL)
3683216460dShyche 			return NULL;
3693216460dShyche 	} else
3703216460dShyche 		fNodes[level]->SetTo(node->BlockNum());
3713216460dShyche 
3723216460dShyche 	if (slot == -1)
3733216460dShyche 		fSlots[level] = fNodes[level]->ItemCount() - 1;
3743216460dShyche 	else
3753216460dShyche 		fSlots[level] = slot;
3763216460dShyche 	return fNodes[level];
3773216460dShyche }
3783216460dShyche 
3793216460dShyche 
3803216460dShyche int
3813216460dShyche BTree::Path::Move(int level, int step)
3823216460dShyche {
3833216460dShyche 	fSlots[level] += step;
3843216460dShyche 	if (fSlots[level] < 0)
3853216460dShyche 		return -1;
3863216460dShyche 	if (fSlots[level] >= fNodes[level]->ItemCount())
3873216460dShyche 		return 1;
3883216460dShyche 	return 0;
3893216460dShyche }
3903216460dShyche 
3913216460dShyche 
3923216460dShyche status_t
3933216460dShyche BTree::Path::GetEntry(int slot, btrfs_key* _key, void** _value, uint32* _size,
3943216460dShyche 	uint32* _offset)
3953216460dShyche {
3963216460dShyche 	BTree::Node* leaf = fNodes[0];
3973216460dShyche 	if (slot < 0 || slot >= leaf->ItemCount())
3983216460dShyche 		return B_ENTRY_NOT_FOUND;
3993216460dShyche 
4003216460dShyche 	if (_key != NULL)
4013216460dShyche 		*_key = leaf->Item(slot)->key;
4023216460dShyche 
4033216460dShyche 	uint32 itemSize = leaf->Item(slot)->Size();
4043216460dShyche 	if (_value != NULL) {
4053216460dShyche 		*_value = malloc(itemSize);
4063216460dShyche 		if (*_value == NULL)
4073216460dShyche 			return B_NO_MEMORY;
4083216460dShyche 
4093216460dShyche 		memcpy(*_value, leaf->ItemData(slot), itemSize);
4103216460dShyche 	}
4113216460dShyche 
4123216460dShyche 	if (_size != NULL)
4133216460dShyche 		*_size = itemSize;
4143216460dShyche 
4153216460dShyche 	if (_offset != NULL)
4163216460dShyche 		*_offset = leaf->Item(slot)->Offset();
4173216460dShyche 
4183216460dShyche 	return B_OK;
4193216460dShyche }
4203216460dShyche 
4213216460dShyche 
42284bc447cShyche status_t
42384bc447cShyche BTree::Path::SetEntry(int slot, const btrfs_entry& entry, void* value)
42484bc447cShyche {
42584bc447cShyche 	if (slot < 0)
42684bc447cShyche 		return B_ENTRY_NOT_FOUND;
42784bc447cShyche 
42884bc447cShyche 	memcpy(fNodes[0]->Item(slot), &entry, sizeof(btrfs_entry));
42984bc447cShyche 	memcpy(fNodes[0]->ItemData(slot), value, entry.Size());
43084bc447cShyche 	return B_OK;
43184bc447cShyche }
43284bc447cShyche 
43384bc447cShyche 
434883b9bf2Shyche /*
435883b9bf2Shyche  * Allocate and copy block and do all the changes that it can.
436883b9bf2Shyche  * for now, we only copy-on-write tree block,
437883b9bf2Shyche  * file data is "nocow" by default.
438883b9bf2Shyche  *
439883b9bf2Shyche  * 	o	parent	o
440883b9bf2Shyche  * 	|	 ===> 	 \
441883b9bf2Shyche  * 	o	      	x o
442883b9bf2Shyche  */
443883b9bf2Shyche status_t
444883b9bf2Shyche BTree::Path::CopyOnWrite(Transaction& transaction, int level, uint32 start,
445883b9bf2Shyche 	int num, int length)
446883b9bf2Shyche {
447883b9bf2Shyche 	Node* node = fNodes[level];
448883b9bf2Shyche 	if (node == NULL)
449883b9bf2Shyche 		return B_BAD_VALUE;
450883b9bf2Shyche 
451883b9bf2Shyche 	status_t status;
452883b9bf2Shyche 	if (transaction.HasBlock(node->BlockNum())) {
453883b9bf2Shyche 		// cow-ed block can not be cow-ed again
454883b9bf2Shyche 		status = node->MoveEntries(start, start + num - 1, length);
455883b9bf2Shyche 		if (status != B_OK)
456883b9bf2Shyche 			return status;
457883b9bf2Shyche 
458883b9bf2Shyche 		node->SetGeneration(transaction.SystemID());
459883b9bf2Shyche 		if (length < 0)
460883b9bf2Shyche 			node->SetItemCount(node->ItemCount() - num);
461883b9bf2Shyche 		else if (length > 0)
462883b9bf2Shyche 			node->SetItemCount(node->ItemCount() + num);
463883b9bf2Shyche 
464883b9bf2Shyche 		return B_OK;
465883b9bf2Shyche 	}
466883b9bf2Shyche 
467883b9bf2Shyche 	uint64 address;
468883b9bf2Shyche 	fsblock_t block;
469883b9bf2Shyche 	status = fTree->SystemVolume()->GetNewBlock(address, block);
470883b9bf2Shyche 	if (status != B_OK)
471883b9bf2Shyche 		return status;
472883b9bf2Shyche 
473883b9bf2Shyche 	fNodes[level] = new(std::nothrow) BTree::Node(fTree->SystemVolume());
474883b9bf2Shyche 	if (fNodes[level] == NULL)
475883b9bf2Shyche 		return B_NO_MEMORY;
476883b9bf2Shyche 
477883b9bf2Shyche 	fNodes[level]->SetToWritable(block, transaction.ID(), true);
478883b9bf2Shyche 
479883b9bf2Shyche 	status = fNodes[level]->Copy(node, start, start + num - 1, length);
480883b9bf2Shyche 	if (status != B_OK)
481883b9bf2Shyche 		return status;
482883b9bf2Shyche 
483883b9bf2Shyche 	fNodes[level]->SetGeneration(transaction.SystemID());
484883b9bf2Shyche 	fNodes[level]->SetLogicalAddress(address);
485883b9bf2Shyche 	if (length < 0)
486883b9bf2Shyche 		fNodes[level]->SetItemCount(node->ItemCount() - num);
487883b9bf2Shyche 	else if (length > 0)
488883b9bf2Shyche 		fNodes[level]->SetItemCount(node->ItemCount() + num);
489883b9bf2Shyche 	else
490883b9bf2Shyche 		fNodes[level]->SetItemCount(num);
491883b9bf2Shyche 
492883b9bf2Shyche 	// change pointer of this node in parent
493883b9bf2Shyche 	int parentSlot;
494883b9bf2Shyche 	Node* parentNode = GetNode(level + 1, &parentSlot);
495883b9bf2Shyche 	if (parentNode != NULL)
496883b9bf2Shyche 		parentNode->Index(parentSlot)->SetLogicalAddress(address);
497883b9bf2Shyche 
498883b9bf2Shyche 	if (level == fTree->RootLevel())
499883b9bf2Shyche 		fTree->SetRoot(fNodes[level]);
500883b9bf2Shyche 
501883b9bf2Shyche 	delete node;
502883b9bf2Shyche 	return B_OK;
503883b9bf2Shyche }
504883b9bf2Shyche 
505883b9bf2Shyche 
506883b9bf2Shyche /* Copy-On-Write all internal nodes start from a specific level.
507883b9bf2Shyche  * level > 0: to root
508883b9bf2Shyche  * level <= 0: to leaf
509883b9bf2Shyche  *
510883b9bf2Shyche  *		path	cow-path       path    cow-path
511883b9bf2Shyche  *	=================================================
512883b9bf2Shyche  *		root	cow-root       root	level < 0
513883b9bf2Shyche  *		 |  	|               |
514883b9bf2Shyche  *		 n1 	cow-n1         ...______
515883b9bf2Shyche  *		 |  	|               |       \
516883b9bf2Shyche  *		 n2 	cow-n2          n1     cow-n1
517883b9bf2Shyche  *		 |  	/               |        |
518883b9bf2Shyche  *		...____/                n2     cow-n2
519883b9bf2Shyche  *		 |  	                |        |
520883b9bf2Shyche  *		leaf	level > 0      leaf    cow-leaf
521883b9bf2Shyche  */
522883b9bf2Shyche status_t
523883b9bf2Shyche BTree::Path::InternalCopy(Transaction& transaction, int level)
524883b9bf2Shyche {
525883b9bf2Shyche 	if (std::abs(level) >= fTree->RootLevel())
526883b9bf2Shyche 		return B_OK;
527883b9bf2Shyche 
528883b9bf2Shyche 	TRACE("Path::InternalCopy() level %i\n", level);
529883b9bf2Shyche 	int from, to;
530883b9bf2Shyche 	if (level > 0) {
531883b9bf2Shyche 		from = level;
532883b9bf2Shyche 		to = fTree->RootLevel();
533883b9bf2Shyche 	} else {
534883b9bf2Shyche 		from = 0;
535883b9bf2Shyche 		to = std::abs(level);
536883b9bf2Shyche 	}
537883b9bf2Shyche 
538883b9bf2Shyche 	Node* node = NULL;
539883b9bf2Shyche 	status_t status;
540883b9bf2Shyche 	while (from <= to) {
541883b9bf2Shyche 		node = fNodes[from];
542883b9bf2Shyche 		status = CopyOnWrite(transaction, from, 0, node->ItemCount(), 0);
543883b9bf2Shyche 		from++;
544883b9bf2Shyche 		if (status != B_OK)
545883b9bf2Shyche 			return status;
546883b9bf2Shyche 	}
547883b9bf2Shyche 
548883b9bf2Shyche 	return B_OK;
549883b9bf2Shyche }
550883b9bf2Shyche 
551883b9bf2Shyche 
5523216460dShyche //	#pragma mark - BTree implementation
5531481c49cShyche 
5541481c49cShyche 
555fc4a1e78Shyche BTree::BTree(Volume* volume)
556fc4a1e78Shyche 	:
557fc4a1e78Shyche 	fRootBlock(0),
558883b9bf2Shyche 	fRootLevel(0),
559fc4a1e78Shyche 	fVolume(volume)
560fc4a1e78Shyche {
561fc4a1e78Shyche 	mutex_init(&fIteratorLock, "btrfs b+tree iterator");
562fc4a1e78Shyche }
563fc4a1e78Shyche 
564fc4a1e78Shyche 
565299aba38Shyche BTree::BTree(Volume* volume, btrfs_stream* stream)
566299aba38Shyche 	:
567299aba38Shyche 	fRootBlock(0),
568883b9bf2Shyche 	fRootLevel(0),
569299aba38Shyche 	fVolume(volume)
570299aba38Shyche {
571299aba38Shyche 	mutex_init(&fIteratorLock, "btrfs b+tree iterator");
572299aba38Shyche }
573299aba38Shyche 
574299aba38Shyche 
575299aba38Shyche BTree::BTree(Volume* volume, fsblock_t rootBlock)
576299aba38Shyche 	:
577299aba38Shyche 	fRootBlock(rootBlock),
578299aba38Shyche 	fVolume(volume)
579299aba38Shyche {
580299aba38Shyche 	mutex_init(&fIteratorLock, "btrfs b+tree iterator");
581299aba38Shyche }
582299aba38Shyche 
583299aba38Shyche 
584299aba38Shyche BTree::~BTree()
585299aba38Shyche {
586299aba38Shyche 	// if there are any TreeIterators left, we need to stop them
587299aba38Shyche 	// (can happen when the tree's inode gets deleted while
588299aba38Shyche 	// traversing the tree - a TreeIterator doesn't lock the inode)
589299aba38Shyche 	mutex_lock(&fIteratorLock);
590299aba38Shyche 
591299aba38Shyche 	SinglyLinkedList<TreeIterator>::Iterator iterator
592299aba38Shyche 		= fIterators.GetIterator();
593299aba38Shyche 	while (iterator.HasNext())
594299aba38Shyche 		iterator.Next()->Stop();
595299aba38Shyche 	mutex_destroy(&fIteratorLock);
596299aba38Shyche }
597299aba38Shyche 
598299aba38Shyche 
599299aba38Shyche int32
600875a0552Shyche btrfs_key::Compare(const btrfs_key& key) const
601299aba38Shyche {
602875a0552Shyche 	if (ObjectID() > key.ObjectID())
603299aba38Shyche 		return 1;
604875a0552Shyche 	if (ObjectID() < key.ObjectID())
605299aba38Shyche 		return -1;
606875a0552Shyche 	if (Type() > key.Type())
607299aba38Shyche 		return 1;
608875a0552Shyche 	if (Type() < key.Type())
609299aba38Shyche 		return -1;
610875a0552Shyche 	if (Offset() > key.Offset())
611299aba38Shyche 		return 1;
612875a0552Shyche 	if (Offset() < key.Offset())
613299aba38Shyche 		return -1;
614299aba38Shyche 	return 0;
615299aba38Shyche }
616299aba38Shyche 
617299aba38Shyche 
6183216460dShyche /* Traverse from root to fill in the path along way its finding.
6193216460dShyche  * Return current slot at leaf if successful.
6203216460dShyche  */
6213216460dShyche status_t
6223216460dShyche BTree::Traverse(btree_traversing type, Path* path, const btrfs_key& key)
6233216460dShyche 	const
6243216460dShyche {
6253216460dShyche 	TRACE("BTree::Traverse() objectid %" B_PRId64 " type %d offset %"
6263216460dShyche 		B_PRId64 " \n", key.ObjectID(),	key.Type(), key.Offset());
6273216460dShyche 	fsblock_t physicalBlock = fRootBlock;
6283216460dShyche 	Node node(fVolume, physicalBlock);
6293216460dShyche 	int slot;
6303216460dShyche 	status_t status = B_OK;
6313216460dShyche 
6323216460dShyche 	while (node.Level() != 0) {
6333216460dShyche 		TRACE("BTree::Traverse() level %d count %d\n", node.Level(),
6343216460dShyche 			node.ItemCount());
6353216460dShyche 		status = node.SearchSlot(key, &slot, BTREE_BACKWARD);
6363216460dShyche 		if (status != B_OK)
6373216460dShyche 			return status;
6383216460dShyche 		if (path->SetNode(&node, slot) == NULL)
6393216460dShyche 			return B_NO_MEMORY;
6403216460dShyche 
6413216460dShyche 		TRACE("BTree::Traverse() getting index %" B_PRIu32 "\n", slot);
6423216460dShyche 
6433216460dShyche 		status = fVolume->FindBlock(node.Index(slot)->LogicalAddress(),
6443216460dShyche 				physicalBlock);
6453216460dShyche 		if (status != B_OK) {
6463216460dShyche 			ERROR("BTree::Traverse() unmapped block %" B_PRId64 "\n",
6473216460dShyche 				node.Index(slot)->LogicalAddress());
6483216460dShyche 			return status;
6493216460dShyche 		}
6503216460dShyche 		node.SetTo(physicalBlock);
6513216460dShyche 	}
6523216460dShyche 
6533216460dShyche 	TRACE("BTree::Traverse() dump count %" B_PRId32 "\n", node.ItemCount());
6543216460dShyche 	status = node.SearchSlot(key, &slot, type);
6553216460dShyche 	if (status != B_OK)
6563216460dShyche 		return status;
6573216460dShyche 	if (path->SetNode(&node, slot) == NULL)
6583216460dShyche 		return B_NO_MEMORY;
6593216460dShyche 
6603216460dShyche 	TRACE("BTree::Traverse() found %" B_PRIu32 " %" B_PRIu32 "\n",
6613216460dShyche 		node.Item(slot)->Offset(), node.Item(slot)->Size());
6623216460dShyche 	return slot;
6633216460dShyche }
6643216460dShyche 
6653216460dShyche 
666299aba38Shyche /*!	Searches the key in the tree, and stores the allocated found item in
667299aba38Shyche 	_value, if successful.
668299aba38Shyche 	Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND if not.
669299aba38Shyche 	It can also return other errors to indicate that something went wrong.
670299aba38Shyche */
671299aba38Shyche status_t
6723216460dShyche BTree::_Find(Path* path, btrfs_key& wanted, void** _value, uint32* _size,
6733216460dShyche 	uint32* _offset, btree_traversing type) const
674299aba38Shyche {
6753216460dShyche 	status_t status = Traverse(type, path, wanted);
6763216460dShyche 	if (status < B_OK)
6773216460dShyche 		return status;
678299aba38Shyche 
6793216460dShyche 	btrfs_key found;
6803216460dShyche 	status = path->GetCurrentEntry(&found, _value, _size, _offset);
6813216460dShyche 	if (status != B_OK)
6823216460dShyche 		return status;
683299aba38Shyche 
684*690d16c6Shyche 	if (found.Type() != wanted.Type() && wanted.Type() != BTRFS_KEY_TYPE_ANY) {
685*690d16c6Shyche 		ERROR("Find() not found wanted: %" B_PRIu64 " %" B_PRIu8 " %"
686*690d16c6Shyche 			B_PRIu64 " found: %" B_PRIu64 " %" B_PRIu8 " %" B_PRIu64 "\n",
687*690d16c6Shyche 			wanted.ObjectID(), wanted.Type(), wanted.Offset(), found.ObjectID(),
688*690d16c6Shyche 			found.Type(), found.Offset());
68946eba5c0Shyche 		return B_ENTRY_NOT_FOUND;
690*690d16c6Shyche 	}
691299aba38Shyche 
6923216460dShyche 	wanted = found;
693299aba38Shyche 	return B_OK;
694299aba38Shyche }
695299aba38Shyche 
696299aba38Shyche 
69746eba5c0Shyche status_t
6983216460dShyche BTree::FindNext(Path* path, btrfs_key& key, void** _value, uint32* _size,
6993216460dShyche 	uint32* _offset) const
70046eba5c0Shyche {
7013216460dShyche 	return _Find(path, key, _value, _size, _offset, BTREE_FORWARD);
702299aba38Shyche }
703299aba38Shyche 
704299aba38Shyche 
705299aba38Shyche status_t
7063216460dShyche BTree::FindPrevious(Path* path, btrfs_key& key, void** _value, uint32* _size,
7073216460dShyche 	uint32* _offset) const
708299aba38Shyche {
7093216460dShyche 	return _Find(path, key, _value, _size, _offset, BTREE_BACKWARD);
710299aba38Shyche }
711299aba38Shyche 
712299aba38Shyche 
713299aba38Shyche status_t
7143216460dShyche BTree::FindExact(Path* path, btrfs_key& key, void** _value, uint32* _size,
7153216460dShyche 	uint32* _offset) const
716299aba38Shyche {
7173216460dShyche 	return _Find(path, key, _value, _size, _offset, BTREE_EXACT);
7183216460dShyche }
7193216460dShyche 
7203216460dShyche 
72184bc447cShyche /*
72284bc447cShyche  * Insert "num" of consecutive empty entries start with slot of "startKey"
72384bc447cShyche  * if successful return the starting slot, otherwise return error code.
72484bc447cShyche  */
72584bc447cShyche status_t
72684bc447cShyche BTree::MakeEntries(Transaction& transaction, Path* path,
72784bc447cShyche 	const btrfs_key& startKey, int num, int length)
72884bc447cShyche {
72984bc447cShyche 	TRACE("BTree::MakeEntries() num %i key (% " B_PRIu64 " %" B_PRIu8 " %"
73084bc447cShyche 		B_PRIu64 ")\n", num, startKey.ObjectID(), startKey.Type(),
73184bc447cShyche 		startKey.Offset());
73284bc447cShyche 
73384bc447cShyche 	status_t status = Traverse(BTREE_FORWARD, path, startKey);
73484bc447cShyche 	if (status < B_OK)
73584bc447cShyche 		return status;
73684bc447cShyche 
73784bc447cShyche 	int slot = status;
73884bc447cShyche 	status = path->InternalCopy(transaction, 1);
73984bc447cShyche 	if (status != B_OK)
74084bc447cShyche 		return status;
74184bc447cShyche 
74284bc447cShyche 	status = path->CopyOnWrite(transaction, 0, slot, num, length);
74384bc447cShyche 	if (status == B_DEVICE_FULL) {
74484bc447cShyche 		// TODO: push data or split node
74584bc447cShyche 		return status;
74684bc447cShyche 	}
74784bc447cShyche 
74884bc447cShyche 	if (status != B_OK)
74984bc447cShyche 		return status;
75084bc447cShyche 	return slot;
75184bc447cShyche }
75284bc447cShyche 
75384bc447cShyche 
75484bc447cShyche /* MakeEntries and then fill in them.
75584bc447cShyche  */
75684bc447cShyche status_t
75784bc447cShyche BTree::InsertEntries(Transaction& transaction, Path* path,
75884bc447cShyche 	btrfs_entry* entries, void** data, int num)
75984bc447cShyche {
76084bc447cShyche 	int totalLength = sizeof(btrfs_entry) * num;
76184bc447cShyche 	for (int i = 0; i < num; i++)
76284bc447cShyche 		totalLength += entries[i].Size();
76384bc447cShyche 
76484bc447cShyche 	status_t slot = MakeEntries(transaction, path, entries[0].key, num,
76584bc447cShyche 		totalLength);
76684bc447cShyche 	if (slot < B_OK)
76784bc447cShyche 		return slot;
76884bc447cShyche 
76984bc447cShyche 	uint32 upperLimit;
77084bc447cShyche 	if (slot > 0) {
77184bc447cShyche 		path->GetEntry(slot - 1, NULL, NULL, NULL, &upperLimit);
77284bc447cShyche 	} else
77384bc447cShyche 		upperLimit = fVolume->BlockSize() - sizeof(btrfs_header);
77484bc447cShyche 
77584bc447cShyche 	TRACE("BTree::InsertEntries() num: %i upper limit %" B_PRIu32 "\n", num,
77684bc447cShyche 		upperLimit);
77784bc447cShyche 	for (int i = 0; i < num; i++) {
77884bc447cShyche 		upperLimit -= entries[i].Size();
77984bc447cShyche 		entries[i].SetOffset(upperLimit);
78084bc447cShyche 		path->SetEntry(slot + i, entries[i], data[i]);
78184bc447cShyche 	}
78284bc447cShyche 
78384bc447cShyche 	return B_OK;
78484bc447cShyche }
78584bc447cShyche 
78684bc447cShyche 
78720185bb9Shyche /* Like MakeEntries, but here we remove entries instead.
78820185bb9Shyche  * Removed data stored in _data
78920185bb9Shyche  * May merge those functions into one.
79020185bb9Shyche  */
79120185bb9Shyche status_t
79220185bb9Shyche BTree::RemoveEntries(Transaction& transaction, Path* path,
79320185bb9Shyche 	const btrfs_key& startKey, void** _data, int num)
79420185bb9Shyche {
79520185bb9Shyche 	TRACE("BTree::RemoveEntries() num %i key (% " B_PRIu64 " %" B_PRIu8 " %"
79620185bb9Shyche 		B_PRIu64 ")\n", num, startKey.ObjectID(), startKey.Type(),
79720185bb9Shyche 		startKey.Offset());
79820185bb9Shyche 
79920185bb9Shyche 	status_t status = Traverse(BTREE_EXACT, path, startKey);
80020185bb9Shyche 	if (status < B_OK)
80120185bb9Shyche 		return status;
80220185bb9Shyche 
80320185bb9Shyche 	int slot = status;
80420185bb9Shyche 	int length = -sizeof(btrfs_entry) * num;
80520185bb9Shyche 	for (int i = 0; i < num; i++) {
80620185bb9Shyche 		uint32 itemSize;
80720185bb9Shyche 		path->GetEntry(slot + i, NULL, &_data[i], &itemSize);
80820185bb9Shyche 		length -= itemSize;
80920185bb9Shyche 	}
81020185bb9Shyche 
81120185bb9Shyche 	status = path->InternalCopy(transaction, 1);
81220185bb9Shyche 	if (status != B_OK)
81320185bb9Shyche 		return status;
81420185bb9Shyche 
81520185bb9Shyche 	status = path->CopyOnWrite(transaction, 0, slot, num, length);
81620185bb9Shyche 	if (status == B_DIRECTORY_NOT_EMPTY) {
81720185bb9Shyche 		// TODO: merge node or push data
81820185bb9Shyche 	}
81920185bb9Shyche 
82020185bb9Shyche 	return status;
82120185bb9Shyche }
82220185bb9Shyche 
82320185bb9Shyche 
8243216460dShyche status_t
8253216460dShyche BTree::PreviousLeaf(Path* path) const
8263216460dShyche {
8273216460dShyche 	// TODO: use Traverse() ???
8283216460dShyche 	int level = 0;
8293216460dShyche 	int slot;
8303216460dShyche 	Node* node = NULL;
8313216460dShyche 	// iterate to the root until satisfy the condition
8323216460dShyche 	while (true) {
8333216460dShyche 		node = path->GetNode(level, &slot);
8343216460dShyche 		if (node == NULL || slot != 0)
8353216460dShyche 			break;
8363216460dShyche 		level++;
8373216460dShyche 	}
8383216460dShyche 
8393216460dShyche 	// the current leaf is already the left most leaf or
8403216460dShyche 	// path was not initialized
8413216460dShyche 	if (node == NULL)
8423216460dShyche 		return B_ENTRY_NOT_FOUND;
8433216460dShyche 
8443216460dShyche 	path->Move(level, BTREE_BACKWARD);
8453216460dShyche 	fsblock_t physicalBlock;
8463216460dShyche 	// change all nodes below this level and slot to the ending
8473216460dShyche 	do {
8483216460dShyche 		status_t status = fVolume->FindBlock(
8493216460dShyche 			node->Index(slot)->LogicalAddress(), physicalBlock);
8503216460dShyche 		if (status != B_OK)
8513216460dShyche 			return status;
8523216460dShyche 
8533216460dShyche 		node = path->SetNode(physicalBlock, -1);
8543216460dShyche 		if (node == NULL)
8553216460dShyche 			return B_NO_MEMORY;
8563216460dShyche 		slot = node->ItemCount() - 1;
8573216460dShyche 		level--;
8583216460dShyche 	} while(level != 0);
8593216460dShyche 
8603216460dShyche 	return B_OK;
8613216460dShyche }
8623216460dShyche 
8633216460dShyche 
8643216460dShyche status_t
8653216460dShyche BTree::NextLeaf(Path* path) const
8663216460dShyche {
8673216460dShyche 	int level = 0;
8683216460dShyche 	int slot;
8693216460dShyche 	Node* node = NULL;
8703216460dShyche 	// iterate to the root until satisfy the condition
8713216460dShyche 	while (true) {
8723216460dShyche 		node = path->GetNode(level, &slot);
8733216460dShyche 		if (node == NULL || slot < node->ItemCount() - 1)
8743216460dShyche 			break;
8753216460dShyche 		level++;
8763216460dShyche 	}
8773216460dShyche 
8783216460dShyche 	// the current leaf is already the right most leaf or
8793216460dShyche 	// path was not initialized
8803216460dShyche 	if (node == NULL)
8813216460dShyche 		return B_ENTRY_NOT_FOUND;
8823216460dShyche 
8833216460dShyche 	path->Move(level, BTREE_FORWARD);
8843216460dShyche 	fsblock_t physicalBlock;
8853216460dShyche 	// change all nodes below this level and slot to the beginning
8863216460dShyche 	do {
8873216460dShyche 		status_t status = fVolume->FindBlock(
8883216460dShyche 			node->Index(slot)->LogicalAddress(), physicalBlock);
8893216460dShyche 		if (status != B_OK)
8903216460dShyche 			return status;
8913216460dShyche 
8923216460dShyche 		node = path->SetNode(physicalBlock, 0);
8933216460dShyche 		if (node == NULL)
8943216460dShyche 			return B_NO_MEMORY;
8953216460dShyche 		slot = 0;
8963216460dShyche 		level--;
8973216460dShyche 	} while(level != 0);
8983216460dShyche 
8993216460dShyche 	return B_OK;
900299aba38Shyche }
901299aba38Shyche 
902299aba38Shyche 
903883b9bf2Shyche /* Set root infomation, to use this function root must be valid and
904883b9bf2Shyche  * exists on disk.
905883b9bf2Shyche  */
906fc4a1e78Shyche status_t
907fc4a1e78Shyche BTree::SetRoot(off_t logical, fsblock_t* block)
908fc4a1e78Shyche {
909fc4a1e78Shyche 	if (block != NULL) {
910fc4a1e78Shyche 		fRootBlock = *block;
911fc4a1e78Shyche 	} else {
912fc4a1e78Shyche 		if (fVolume->FindBlock(logical, fRootBlock) != B_OK) {
913ce029df2Shyche 			ERROR("SetRoot() unmapped block %" B_PRId64 " %" B_PRId64 "\n",
914ce029df2Shyche 				logical, fRootBlock);
915fc4a1e78Shyche 			return B_ERROR;
916fc4a1e78Shyche 		}
917fc4a1e78Shyche 	}
918883b9bf2Shyche 
919883b9bf2Shyche 	btrfs_header header;
920883b9bf2Shyche 	read_pos(fVolume->Device(), fRootBlock * fVolume->BlockSize(), &header,
921883b9bf2Shyche 		sizeof(btrfs_header));
922883b9bf2Shyche 	fRootLevel = header.Level();
923883b9bf2Shyche 	fLogicalRoot = header.LogicalAddress();
924fc4a1e78Shyche 	return B_OK;
925fc4a1e78Shyche }
926fc4a1e78Shyche 
927fc4a1e78Shyche 
928299aba38Shyche void
929883b9bf2Shyche BTree::SetRoot(Node* root)
930883b9bf2Shyche {
931883b9bf2Shyche 	fRootBlock = root->BlockNum();
932883b9bf2Shyche 	fLogicalRoot = root->LogicalAddress();
933883b9bf2Shyche 	fRootLevel = root->Level();
934883b9bf2Shyche }
935883b9bf2Shyche 
936883b9bf2Shyche 
937883b9bf2Shyche void
938299aba38Shyche BTree::_AddIterator(TreeIterator* iterator)
939299aba38Shyche {
940299aba38Shyche 	MutexLocker _(fIteratorLock);
941299aba38Shyche 	fIterators.Add(iterator);
942299aba38Shyche }
943299aba38Shyche 
944299aba38Shyche 
945299aba38Shyche void
946299aba38Shyche BTree::_RemoveIterator(TreeIterator* iterator)
947299aba38Shyche {
948299aba38Shyche 	MutexLocker _(fIteratorLock);
949299aba38Shyche 	fIterators.Remove(iterator);
950299aba38Shyche }
951299aba38Shyche 
952299aba38Shyche 
953299aba38Shyche //	#pragma mark -
954299aba38Shyche 
955299aba38Shyche 
956bfd7a4fbShyche TreeIterator::TreeIterator(BTree* tree, const btrfs_key& key)
957299aba38Shyche 	:
958299aba38Shyche 	fTree(tree),
959bfd7a4fbShyche 	fKey(key),
960bfd7a4fbShyche 	fIteratorStatus(B_NO_INIT)
961299aba38Shyche {
962299aba38Shyche 	tree->_AddIterator(this);
963bfd7a4fbShyche 	fPath = new(std::nothrow) BTree::Path(tree);
964bfd7a4fbShyche 	if (fPath == NULL)
965bfd7a4fbShyche 		fIteratorStatus = B_NO_MEMORY;
966299aba38Shyche }
967299aba38Shyche 
968299aba38Shyche 
969299aba38Shyche TreeIterator::~TreeIterator()
970299aba38Shyche {
971299aba38Shyche 	if (fTree)
972299aba38Shyche 		fTree->_RemoveIterator(this);
973bfd7a4fbShyche 
974bfd7a4fbShyche 	delete fPath;
975bfd7a4fbShyche 	fPath = NULL;
976bfd7a4fbShyche }
977bfd7a4fbShyche 
978bfd7a4fbShyche 
979bfd7a4fbShyche void
980bfd7a4fbShyche TreeIterator::Rewind(bool inverse)
981bfd7a4fbShyche {
982bfd7a4fbShyche 	if (inverse)
983bfd7a4fbShyche 		fKey.SetOffset(BTREE_END);
984bfd7a4fbShyche 	else
985bfd7a4fbShyche 		fKey.SetOffset(BTREE_BEGIN);
986bfd7a4fbShyche 	fIteratorStatus = B_NO_INIT;
987299aba38Shyche }
988299aba38Shyche 
989299aba38Shyche 
990299aba38Shyche /*!	Iterates through the tree in the specified direction.
991299aba38Shyche */
992299aba38Shyche status_t
993bfd7a4fbShyche TreeIterator::_Traverse(btree_traversing direction)
994299aba38Shyche {
995bfd7a4fbShyche 	status_t status = fTree->Traverse(direction, fPath, fKey);
996bfd7a4fbShyche 	if (status < B_OK) {
997bfd7a4fbShyche 		ERROR("TreeIterator::Traverse() Find failed\n");
998bfd7a4fbShyche 		return status;
999299aba38Shyche 	}
1000299aba38Shyche 
1001bfd7a4fbShyche 	return (fIteratorStatus = B_OK);
1002bfd7a4fbShyche }
1003bfd7a4fbShyche 
1004bfd7a4fbShyche 
1005bfd7a4fbShyche // Like GetEntry in BTree::Path but here we check the type and moving.
1006bfd7a4fbShyche status_t
1007bfd7a4fbShyche TreeIterator::_GetEntry(btree_traversing type, void** _value, uint32* _size,
1008bfd7a4fbShyche 	uint32* _offset)
1009bfd7a4fbShyche {
10105686d4e1Shyche 	status_t status = B_OK;
1011bfd7a4fbShyche 	if (fIteratorStatus == B_NO_INIT) {
1012bfd7a4fbShyche 		status = _Traverse(type);
1013bfd7a4fbShyche 		if (status != B_OK)
1014bfd7a4fbShyche 			return status;
1015bfd7a4fbShyche 		type = BTREE_EXACT;
1016bfd7a4fbShyche 	}
1017bfd7a4fbShyche 
1018bfd7a4fbShyche 	if (fIteratorStatus != B_OK)
1019bfd7a4fbShyche 		return fIteratorStatus;
1020bfd7a4fbShyche 
1021bfd7a4fbShyche 	int move = fPath->Move(0, type);
1022bfd7a4fbShyche 	if (move > 0)
1023bfd7a4fbShyche 		status = fTree->NextLeaf(fPath);
1024bfd7a4fbShyche 	else if (move < 0)
1025bfd7a4fbShyche 		status = fTree->PreviousLeaf(fPath);
1026bfd7a4fbShyche 	if (status != B_OK)
1027bfd7a4fbShyche 		return status;
1028bfd7a4fbShyche 
1029bfd7a4fbShyche 	btrfs_key found;
1030bfd7a4fbShyche 	status = fPath->GetCurrentEntry(&found, _value, _size, _offset);
1031bfd7a4fbShyche 	if (status != B_OK)
1032bfd7a4fbShyche 		return status;
1033bfd7a4fbShyche 
1034bfd7a4fbShyche 	fKey.SetObjectID(found.ObjectID());
1035bfd7a4fbShyche 	fKey.SetOffset(found.Offset());
1036bfd7a4fbShyche 	if (fKey.Type() != found.Type() && fKey.Type() != BTRFS_KEY_TYPE_ANY)
1037bfd7a4fbShyche 		return B_ENTRY_NOT_FOUND;
1038bfd7a4fbShyche 
1039299aba38Shyche 	return B_OK;
1040299aba38Shyche }
1041299aba38Shyche 
1042299aba38Shyche 
1043299aba38Shyche /*!	just sets the current key in the iterator.
1044299aba38Shyche */
1045299aba38Shyche status_t
1046bfd7a4fbShyche TreeIterator::Find(const btrfs_key& key)
1047299aba38Shyche {
1048bfd7a4fbShyche 	if (fIteratorStatus == B_INTERRUPTED)
1049bfd7a4fbShyche 		return fIteratorStatus;
1050bfd7a4fbShyche 
1051bfd7a4fbShyche 	fKey = key;
1052bfd7a4fbShyche 	fIteratorStatus = B_NO_INIT;
1053299aba38Shyche 	return B_OK;
1054299aba38Shyche }
1055299aba38Shyche 
1056299aba38Shyche 
1057299aba38Shyche void
1058299aba38Shyche TreeIterator::Stop()
1059299aba38Shyche {
1060299aba38Shyche 	fTree = NULL;
1061bfd7a4fbShyche 	fIteratorStatus = B_INTERRUPTED;
1062299aba38Shyche }
1063