xref: /haiku/src/add-ons/kernel/file_systems/btrfs/BTree.cpp (revision 299aba38f001689a58963cbf6053d2c809f3604f)
1*299aba38Shyche /*
2*299aba38Shyche  * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
3*299aba38Shyche  * Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
4*299aba38Shyche  * This file may be used under the terms of the MIT License.
5*299aba38Shyche  */
6*299aba38Shyche 
7*299aba38Shyche 
8*299aba38Shyche //! BTree implementation
9*299aba38Shyche 
10*299aba38Shyche 
11*299aba38Shyche #include "BTree.h"
12*299aba38Shyche #include "CachedBlock.h"
13*299aba38Shyche 
14*299aba38Shyche 
15*299aba38Shyche //#define TRACE_BTRFS
16*299aba38Shyche #ifdef TRACE_BTRFS
17*299aba38Shyche #	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
18*299aba38Shyche #else
19*299aba38Shyche #	define TRACE(x...) ;
20*299aba38Shyche #endif
21*299aba38Shyche #	define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
22*299aba38Shyche 
23*299aba38Shyche 
24*299aba38Shyche BTree::BTree(Volume* volume, btrfs_stream* stream)
25*299aba38Shyche 	:
26*299aba38Shyche 	fStream(stream),
27*299aba38Shyche 	fRootBlock(0),
28*299aba38Shyche 	fVolume(volume)
29*299aba38Shyche {
30*299aba38Shyche 	mutex_init(&fIteratorLock, "btrfs b+tree iterator");
31*299aba38Shyche }
32*299aba38Shyche 
33*299aba38Shyche 
34*299aba38Shyche BTree::BTree(Volume* volume, fsblock_t rootBlock)
35*299aba38Shyche 	:
36*299aba38Shyche 	fStream(NULL),
37*299aba38Shyche 	fRootBlock(rootBlock),
38*299aba38Shyche 	fVolume(volume)
39*299aba38Shyche {
40*299aba38Shyche 	mutex_init(&fIteratorLock, "btrfs b+tree iterator");
41*299aba38Shyche }
42*299aba38Shyche 
43*299aba38Shyche 
44*299aba38Shyche BTree::~BTree()
45*299aba38Shyche {
46*299aba38Shyche 	// if there are any TreeIterators left, we need to stop them
47*299aba38Shyche 	// (can happen when the tree's inode gets deleted while
48*299aba38Shyche 	// traversing the tree - a TreeIterator doesn't lock the inode)
49*299aba38Shyche 	mutex_lock(&fIteratorLock);
50*299aba38Shyche 
51*299aba38Shyche 	SinglyLinkedList<TreeIterator>::Iterator iterator
52*299aba38Shyche 		= fIterators.GetIterator();
53*299aba38Shyche 	while (iterator.HasNext())
54*299aba38Shyche 		iterator.Next()->Stop();
55*299aba38Shyche 	mutex_destroy(&fIteratorLock);
56*299aba38Shyche }
57*299aba38Shyche 
58*299aba38Shyche 
59*299aba38Shyche int32
60*299aba38Shyche BTree::_CompareKeys(btrfs_key& key1, btrfs_key& key2)
61*299aba38Shyche {
62*299aba38Shyche 	if (key1.ObjectID() > key2.ObjectID())
63*299aba38Shyche 		return 1;
64*299aba38Shyche 	if (key1.ObjectID() < key2.ObjectID())
65*299aba38Shyche 		return -1;
66*299aba38Shyche 	if (key1.Type() > key2.Type())
67*299aba38Shyche 		return 1;
68*299aba38Shyche 	if (key1.Type() < key2.Type())
69*299aba38Shyche 		return -1;
70*299aba38Shyche 	if (key1.Offset() > key2.Offset())
71*299aba38Shyche 		return 1;
72*299aba38Shyche 	if (key1.Offset() < key2.Offset())
73*299aba38Shyche 		return -1;
74*299aba38Shyche 	return 0;
75*299aba38Shyche }
76*299aba38Shyche 
77*299aba38Shyche 
78*299aba38Shyche /*!	Searches the key in the tree, and stores the allocated found item in
79*299aba38Shyche 	_value, if successful.
80*299aba38Shyche 	Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND if not.
81*299aba38Shyche 	It can also return other errors to indicate that something went wrong.
82*299aba38Shyche */
83*299aba38Shyche status_t
84*299aba38Shyche BTree::_Find(btrfs_key& key, void** _value, size_t* _size,
85*299aba38Shyche 	btree_traversing type)
86*299aba38Shyche {
87*299aba38Shyche 	TRACE("Find() objectid %" B_PRId64 " type %d offset %" B_PRId64 " \n",
88*299aba38Shyche 		key.ObjectID(),	key.Type(), key.Offset());
89*299aba38Shyche 	btrfs_stream* stream = fStream;
90*299aba38Shyche 	CachedBlock cached(fVolume);
91*299aba38Shyche 	fsblock_t physical;
92*299aba38Shyche 	if (stream == NULL) {
93*299aba38Shyche 		if (fVolume->FindBlock(fRootBlock, physical) != B_OK) {
94*299aba38Shyche 			ERROR("Find() unmapped block %" B_PRId64 "\n", fRootBlock);
95*299aba38Shyche 			return B_ERROR;
96*299aba38Shyche 		}
97*299aba38Shyche 		stream = (btrfs_stream*)cached.SetTo(physical);
98*299aba38Shyche 	}
99*299aba38Shyche 
100*299aba38Shyche 	while (stream->header.Level() != 0) {
101*299aba38Shyche 		TRACE("Find() level %d\n", stream->header.Level());
102*299aba38Shyche 		uint32 i = 1;
103*299aba38Shyche 		for (; i < stream->header.ItemCount(); i++) {
104*299aba38Shyche 			int32 comp = _CompareKeys(stream->index[i].key, key);
105*299aba38Shyche 			TRACE("Find() found index %" B_PRIu32 " at %" B_PRId64 " comp %"
106*299aba38Shyche 				B_PRId32 "\n", i, stream->index[i].BlockNum(), comp);
107*299aba38Shyche 			if (comp < 0)
108*299aba38Shyche 				continue;
109*299aba38Shyche 			if (comp > 0 || type == BTREE_BACKWARD)
110*299aba38Shyche 				break;
111*299aba38Shyche 		}
112*299aba38Shyche 		TRACE("Find() getting index %" B_PRIu32 " at %" B_PRId64 "\n", i - 1,
113*299aba38Shyche 			stream->index[i - 1].BlockNum());
114*299aba38Shyche 
115*299aba38Shyche 		if (fVolume->FindBlock(stream->index[i - 1].BlockNum(), physical)
116*299aba38Shyche 			!= B_OK) {
117*299aba38Shyche 			ERROR("Find() unmapped block %" B_PRId64 "\n",
118*299aba38Shyche 				stream->index[i - 1].BlockNum());
119*299aba38Shyche 			return B_ERROR;
120*299aba38Shyche 		}
121*299aba38Shyche 		stream = (btrfs_stream*)cached.SetTo(physical);
122*299aba38Shyche 	}
123*299aba38Shyche 
124*299aba38Shyche 	uint32 i;
125*299aba38Shyche #ifdef TRACE_BTRFS
126*299aba38Shyche 	TRACE("Find() dump count %" B_PRId32 "\n", stream->header.ItemCount());
127*299aba38Shyche 	for (i = 0; i < stream->header.ItemCount(); i++) {
128*299aba38Shyche 		int32 comp = _CompareKeys(key, stream->entries[i].key);
129*299aba38Shyche 		TRACE("Find() dump %" B_PRIu32 " %" B_PRIu32 " offset %" B_PRId64
130*299aba38Shyche 			" comp %" B_PRId32 "\n", stream->entries[i].Offset(),
131*299aba38Shyche 			stream->entries[i].Size(), stream->entries[i].key.Offset(), comp);
132*299aba38Shyche 	}
133*299aba38Shyche #endif
134*299aba38Shyche 
135*299aba38Shyche 	for (i = 0; i < stream->header.ItemCount(); i++) {
136*299aba38Shyche 		int32 comp = _CompareKeys(key, stream->entries[i].key);
137*299aba38Shyche 		TRACE("Find() found %" B_PRIu32 " %" B_PRIu32 " oid %" B_PRId64
138*299aba38Shyche 			" type %d offset %" B_PRId64 " comp %" B_PRId32 "\n",
139*299aba38Shyche 			stream->entries[i].Offset(), stream->entries[i].Size(),
140*299aba38Shyche 			stream->entries[i].key.ObjectID(), stream->entries[i].key.Type(),
141*299aba38Shyche 			stream->entries[i].key.Offset(), comp);
142*299aba38Shyche 		if (comp == 0)
143*299aba38Shyche 			break;
144*299aba38Shyche 		if (comp < 0 && i > 0) {
145*299aba38Shyche 			if (type == BTREE_EXACT)
146*299aba38Shyche 				return B_ENTRY_NOT_FOUND;
147*299aba38Shyche 			if (type == BTREE_BACKWARD)
148*299aba38Shyche 				i--;
149*299aba38Shyche 			break;
150*299aba38Shyche 		}
151*299aba38Shyche 	}
152*299aba38Shyche 
153*299aba38Shyche 	if (i == stream->header.ItemCount()) {
154*299aba38Shyche 		if (type == BTREE_BACKWARD)
155*299aba38Shyche 			i--;
156*299aba38Shyche 		else
157*299aba38Shyche 			return B_ENTRY_NOT_FOUND;
158*299aba38Shyche 	}
159*299aba38Shyche 
160*299aba38Shyche 	if (i < stream->header.ItemCount()
161*299aba38Shyche 		&& stream->entries[i].key.Type() == key.Type()) {
162*299aba38Shyche 		TRACE("Find() found %" B_PRIu32 " %" B_PRIu32 "\n",
163*299aba38Shyche 			stream->entries[i].Offset(), stream->entries[i].Size());
164*299aba38Shyche 
165*299aba38Shyche 		if (_value != NULL) {
166*299aba38Shyche 			*_value = malloc(stream->entries[i].Size());
167*299aba38Shyche 			uint32 totalOffset = stream->entries[i].Offset() + sizeof(btrfs_header);
168*299aba38Shyche 
169*299aba38Shyche 
170*299aba38Shyche 			if ((fVolume->BlockSize() - totalOffset % fVolume->BlockSize())
171*299aba38Shyche 				>= stream->entries[i].Size()) {
172*299aba38Shyche 				//If there is enough space for *_value
173*299aba38Shyche 				memcpy(*_value, ((uint8*)cached.SetTo(physical
174*299aba38Shyche 					+ totalOffset / fVolume->BlockSize())
175*299aba38Shyche 					+ totalOffset % fVolume->BlockSize()),
176*299aba38Shyche 					stream->entries[i].Size());
177*299aba38Shyche 			} else {
178*299aba38Shyche 				read_pos(fVolume->Device(), physical
179*299aba38Shyche 					* fVolume->BlockSize() + totalOffset,
180*299aba38Shyche 					*_value, stream->entries[i].Size());
181*299aba38Shyche 			}
182*299aba38Shyche 
183*299aba38Shyche 			key.SetOffset(stream->entries[i].key.Offset());
184*299aba38Shyche 			if (_size != NULL)
185*299aba38Shyche 				*_size = stream->entries[i].Size();
186*299aba38Shyche 		}
187*299aba38Shyche 		return B_OK;
188*299aba38Shyche 	}
189*299aba38Shyche 
190*299aba38Shyche 
191*299aba38Shyche 	TRACE("Find() not found %" B_PRId64 " %" B_PRId64 "\n", key.Offset(),
192*299aba38Shyche 		key.ObjectID());
193*299aba38Shyche 
194*299aba38Shyche 	return B_ENTRY_NOT_FOUND;
195*299aba38Shyche }
196*299aba38Shyche 
197*299aba38Shyche 
198*299aba38Shyche status_t
199*299aba38Shyche BTree::FindNext(btrfs_key& key, void** _value, size_t* _size)
200*299aba38Shyche {
201*299aba38Shyche 	return _Find(key, _value, _size, BTREE_FORWARD);
202*299aba38Shyche }
203*299aba38Shyche 
204*299aba38Shyche 
205*299aba38Shyche status_t
206*299aba38Shyche BTree::FindPrevious(btrfs_key& key, void** _value, size_t* _size)
207*299aba38Shyche {
208*299aba38Shyche 	return _Find(key, _value, _size, BTREE_BACKWARD);
209*299aba38Shyche }
210*299aba38Shyche 
211*299aba38Shyche 
212*299aba38Shyche status_t
213*299aba38Shyche BTree::FindExact(btrfs_key& key, void** _value, size_t* _size)
214*299aba38Shyche {
215*299aba38Shyche 	return _Find(key, _value, _size, BTREE_EXACT);
216*299aba38Shyche }
217*299aba38Shyche 
218*299aba38Shyche 
219*299aba38Shyche void
220*299aba38Shyche BTree::_AddIterator(TreeIterator* iterator)
221*299aba38Shyche {
222*299aba38Shyche 	MutexLocker _(fIteratorLock);
223*299aba38Shyche 	fIterators.Add(iterator);
224*299aba38Shyche }
225*299aba38Shyche 
226*299aba38Shyche 
227*299aba38Shyche void
228*299aba38Shyche BTree::_RemoveIterator(TreeIterator* iterator)
229*299aba38Shyche {
230*299aba38Shyche 	MutexLocker _(fIteratorLock);
231*299aba38Shyche 	fIterators.Remove(iterator);
232*299aba38Shyche }
233*299aba38Shyche 
234*299aba38Shyche 
235*299aba38Shyche //	#pragma mark -
236*299aba38Shyche 
237*299aba38Shyche 
238*299aba38Shyche TreeIterator::TreeIterator(BTree* tree, btrfs_key& key)
239*299aba38Shyche 	:
240*299aba38Shyche 	fTree(tree),
241*299aba38Shyche 	fCurrentKey(key)
242*299aba38Shyche {
243*299aba38Shyche 	Rewind();
244*299aba38Shyche 	tree->_AddIterator(this);
245*299aba38Shyche }
246*299aba38Shyche 
247*299aba38Shyche 
248*299aba38Shyche TreeIterator::~TreeIterator()
249*299aba38Shyche {
250*299aba38Shyche 	if (fTree)
251*299aba38Shyche 		fTree->_RemoveIterator(this);
252*299aba38Shyche }
253*299aba38Shyche 
254*299aba38Shyche 
255*299aba38Shyche /*!	Iterates through the tree in the specified direction.
256*299aba38Shyche */
257*299aba38Shyche status_t
258*299aba38Shyche TreeIterator::Traverse(btree_traversing direction, btrfs_key& key,
259*299aba38Shyche 	void** value, size_t* size)
260*299aba38Shyche {
261*299aba38Shyche 	if (fTree == NULL)
262*299aba38Shyche 		return B_INTERRUPTED;
263*299aba38Shyche 
264*299aba38Shyche 	fCurrentKey.SetOffset(fCurrentKey.Offset() + direction);
265*299aba38Shyche 	status_t status = fTree->_Find(fCurrentKey, value, size,
266*299aba38Shyche 		direction);
267*299aba38Shyche 	if (status != B_OK) {
268*299aba38Shyche 		TRACE("TreeIterator::Traverse() Find failed\n");
269*299aba38Shyche 		return B_ENTRY_NOT_FOUND;
270*299aba38Shyche 	}
271*299aba38Shyche 
272*299aba38Shyche 	return B_OK;
273*299aba38Shyche }
274*299aba38Shyche 
275*299aba38Shyche 
276*299aba38Shyche /*!	just sets the current key in the iterator.
277*299aba38Shyche */
278*299aba38Shyche status_t
279*299aba38Shyche TreeIterator::Find(btrfs_key& key)
280*299aba38Shyche {
281*299aba38Shyche 	if (fTree == NULL)
282*299aba38Shyche 		return B_INTERRUPTED;
283*299aba38Shyche 	fCurrentKey = key;
284*299aba38Shyche 	return B_OK;
285*299aba38Shyche }
286*299aba38Shyche 
287*299aba38Shyche 
288*299aba38Shyche void
289*299aba38Shyche TreeIterator::Stop()
290*299aba38Shyche {
291*299aba38Shyche 	fTree = NULL;
292*299aba38Shyche }
293