1/*
2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "Directory.h"
8
9#include <algorithm>
10#include <new>
11
12#include <AutoDeleter.h>
13
14#include "Block.h"
15#include "BlockAllocator.h"
16#include "DebugSupport.h"
17#include "Transaction.h"
18#include "Volume.h"
19
20
21class DirEntryBlock {
22public:
23								DirEntryBlock();
24								DirEntryBlock(
25									checksumfs_dir_entry_block* entryBlock,
26									size_t entryBlockSize);
27
28			void				SetTo(checksumfs_dir_entry_block* entryBlock,
29									size_t entryBlockSize);
30
31	inline	int32				EntryCount() const;
32	inline	size_t				BytesUsedFor(int32 entryCount) const;
33	inline	size_t				BytesUsed() const;
34	inline	size_t				FreeSpace() const;
35
36	inline	uint64				BlockIndexAt(int32 index) const;
37			const char*			NameAt(int32 index, size_t& _nameLength) const;
38
39			int32				FindInsertionIndex(const char* name,
40									size_t nameLength, bool& _exactMatch) const;
41
42			int32				FindSplitIndex(int32 index,
43									size_t bytesNeeded) const;
44
45			void				InsertEntry(int32 index, const char* name,
46									size_t nameLength, uint64 blockIndex);
47			void				ReplaceEntryName(int32 index, const char* name,
48									size_t nameLength);
49			void				RemoveEntry(int32 index);
50
51			void				SplitBlock(int32 splitIndex,
52									DirEntryBlock& other);
53
54			bool				Check() const;
55
56private:
57			checksumfs_dir_entry_block* fBlock;
58			size_t				fBlockSize;
59};
60
61
62class DirEntryTree {
63public:
64								DirEntryTree(Directory* directory);
65
66			status_t			LookupEntry(const char* name,
67									uint64& _blockIndex);
68			status_t			LookupNextEntry(const char* name,
69									char* foundName, size_t& _foundNameLength,
70									uint64& _blockIndex);
71
72			status_t			InsertEntry(const char* name, uint64 blockIndex,
73									Transaction& transaction);
74			status_t			RemoveEntry(const char* name,
75									Transaction& transaction);
76
77			status_t			FreeTree(Transaction& transaction);
78
79			bool				IsEmpty() const;
80
81			bool				Check();
82
83private:
84			struct LevelInfo {
85				Block			block;
86				DirEntryBlock	entryBlock;
87				int32			index;
88				bool			exactMatch;
89			};
90
91private:
92			status_t			_InitReadOnly();
93			status_t			_InitWritable(Transaction& transaction);
94			status_t			_InitCommon();
95
96			status_t			_UpdateOrInsertKey(LevelInfo* infos,
97									int32 level, const char* name,
98									size_t nameLength, uint64 blockIndex,
99									bool insertKey, Transaction& transaction);
100
101			status_t			_InsertEntryIncrementDepth(LevelInfo* infos,
102									Transaction& transaction);
103			status_t			_InsertEntrySplitBlock(int32 level,
104									LevelInfo& info, size_t needed,
105									Transaction& transaction, Block& newBlock,
106									int32& _splitIndex);
107
108			bool				_Check(int32 level, uint64 blockIndex,
109									const char* key, size_t keyLength,
110									DirEntryBlock& entryBlock);
111
112	inline	uint16				_Depth() const	{ return fTree->depth; }
113
114private:
115			Directory*			fDirectory;
116			Block				fRootBlock;
117			checksumfs_dir_entry_tree* fTree;
118			checksumfs_dir_entry_block* fRootEntryBlock;
119			size_t				fRootEntryBlockSize;
120};
121
122
123// #pragma mark -
124
125
126static int
127compare_names(const char* a, size_t lengthA, const char* b, size_t lengthB)
128{
129	int cmp = strncmp(a, b, std::min(lengthA, lengthB));
130	if (cmp != 0)
131		return cmp;
132
133	return (int)lengthA - (int)lengthB;
134		// assumes we don't overflow 31 bits
135}
136
137
138// #pragma mark - DirEntryBlock
139
140
141DirEntryBlock::DirEntryBlock()
142	:
143	fBlock(NULL),
144	fBlockSize(0)
145{
146}
147
148
149DirEntryBlock::DirEntryBlock(checksumfs_dir_entry_block* entryBlock,
150	size_t entryBlockSize)
151	:
152	fBlock(entryBlock),
153	fBlockSize(entryBlockSize)
154{
155}
156
157
158void
159DirEntryBlock::SetTo(checksumfs_dir_entry_block* entryBlock,
160	size_t entryBlockSize)
161{
162	fBlock = entryBlock;
163	fBlockSize = entryBlockSize;
164}
165
166
167int32
168DirEntryBlock::EntryCount() const
169{
170	return fBlock->entryCount;
171}
172
173
174size_t
175DirEntryBlock::BytesUsedFor(int32 entryCount) const
176{
177	if (entryCount == 0)
178		return sizeof(*fBlock);
179	return sizeof(*fBlock) + 10 * entryCount
180		+ fBlock->nameEnds[entryCount - 1];
181}
182
183
184size_t
185DirEntryBlock::BytesUsed() const
186{
187	return BytesUsedFor(EntryCount());
188}
189
190
191size_t
192DirEntryBlock::FreeSpace() const
193{
194	return fBlockSize - BytesUsed();
195}
196
197
198uint64
199DirEntryBlock::BlockIndexAt(int32 index) const
200{
201	uint64* blockIndices
202		= (uint64*)((uint8*)fBlock + fBlockSize) - 1;
203	return blockIndices[-index];
204}
205
206
207const char*
208DirEntryBlock::NameAt(int32 index, size_t& _nameLength) const
209{
210	int32 entryCount = EntryCount();
211	if (index < 0 || index >= entryCount)
212		return NULL;
213
214	uint32 nameOffset = index > 0 ? fBlock->nameEnds[index - 1] : 0;
215	_nameLength = fBlock->nameEnds[index] - nameOffset;
216	return (const char*)(fBlock->nameEnds + entryCount) + nameOffset;
217}
218
219
220int32
221DirEntryBlock::FindInsertionIndex(const char* name, size_t nameLength,
222	bool& _exactMatch) const
223{
224	int32 entryCount = EntryCount();
225	if (entryCount == 0) {
226		_exactMatch = false;
227		return 0;
228	}
229
230	const char* entryNames = (char*)(fBlock->nameEnds + entryCount);
231	uint32 nameOffset = 0;
232
233	int32 index = 0;
234	int cmp = -1;
235
236	// TODO: Binary search!
237	for (; index < entryCount; index++) {
238		const char* entryName = entryNames + nameOffset;
239		size_t entryNameLength = fBlock->nameEnds[index] - nameOffset;
240
241		cmp = compare_names(entryName, entryNameLength, name, nameLength);
242		if (cmp >= 0)
243			break;
244
245		nameOffset += entryNameLength;
246	}
247
248	_exactMatch = cmp == 0;
249	return index;
250}
251
252
253/*!	Finds a good split index for an insertion of \a bytesNeeded bytes at
254	index \a index.
255*/
256int32
257DirEntryBlock::FindSplitIndex(int32 index, size_t bytesNeeded) const
258{
259	size_t splitSize = (BytesUsed() + bytesNeeded) / 2;
260
261	int32 entryCount = EntryCount();
262	for (int32 i = 0; i < entryCount; i++) {
263		size_t bytesUsed = BytesUsedFor(i + 1);
264		if (i == index)
265			bytesUsed += bytesNeeded;
266		if (bytesUsed > splitSize)
267			return i;
268	}
269
270	// This should never happen.
271	return entryCount;
272}
273
274
275void
276DirEntryBlock::InsertEntry(int32 index, const char* name, size_t nameLength,
277	uint64 blockIndex)
278{
279	uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize) - 1;
280	int32 entryCount = fBlock->entryCount;
281	char* entryNames = (char*)(fBlock->nameEnds + entryCount);
282
283	uint32 nameOffset = index == 0 ? 0 : fBlock->nameEnds[index - 1];
284	uint32 lastNameEnd = entryCount == 0
285		? 0 : fBlock->nameEnds[entryCount - 1];
286
287	if (index < entryCount) {
288		// make room in the block indices array
289		memmove(&blockIndices[-entryCount], &blockIndices[1 - entryCount],
290			8 * (entryCount - index));
291
292		// make room in the name array -- we also move 2 bytes more for the
293		// new entry in the nameEnds array
294		memmove(entryNames + nameOffset + nameLength + 2,
295			entryNames + nameOffset, lastNameEnd - nameOffset);
296
297		// move the names < index by 2 bytes
298		if (index > 0)
299			memmove(entryNames + 2, entryNames, nameOffset);
300
301		// move and update the nameEnds entries > index
302		for (int32 i = entryCount; i > index; i--)
303			fBlock->nameEnds[i] = fBlock->nameEnds[i - 1] + nameLength;
304	} else if (entryCount > 0) {
305		// the nameEnds array grows -- move all names
306		memmove(entryNames + 2, entryNames, lastNameEnd);
307	}
308
309	// we have made room -- insert the entry
310	entryNames += 2;
311	memcpy(entryNames + nameOffset, name, nameLength);
312	fBlock->nameEnds[index] = nameOffset + nameLength;
313	blockIndices[-index] = blockIndex;
314	fBlock->entryCount++;
315ASSERT(Check());
316}
317
318
319void
320DirEntryBlock::ReplaceEntryName(int32 index, const char* name,
321	size_t nameLength)
322{
323	int32 entryCount = fBlock->entryCount;
324	char* entryNames = (char*)(fBlock->nameEnds + entryCount);
325
326	ASSERT(index >= 0 && index < entryCount);
327
328	uint32 nameOffset = index == 0 ? 0 : fBlock->nameEnds[index - 1];
329	uint32 oldNameLength = fBlock->nameEnds[index] - nameOffset;
330	uint32 lastNameEnd = fBlock->nameEnds[entryCount - 1];
331
332	if (oldNameLength != nameLength) {
333		int32 lengthDiff = (int32)nameLength - (int32)oldNameLength;
334
335		ASSERT(lengthDiff <= (int32)FreeSpace());
336
337		// move names after the changing name
338		if (index + 1 < entryCount) {
339			memmove(entryNames + nameOffset + nameLength,
340				entryNames + nameOffset + oldNameLength,
341				lastNameEnd - nameOffset - oldNameLength);
342		}
343
344		// update the name ends
345		for (int32 i = index; i < entryCount; i++)
346			fBlock->nameEnds[i] = (int32)fBlock->nameEnds[i] + lengthDiff;
347	}
348
349	// copy the name
350	memcpy(entryNames + nameOffset, name, nameLength);
351ASSERT(Check());
352}
353
354
355void
356DirEntryBlock::RemoveEntry(int32 index)
357{
358	ASSERT(index >= 0 && index < EntryCount());
359
360	int32 entryCount = EntryCount();
361	if (entryCount == 1) {
362		// simple case -- removing the last entry
363		fBlock->entryCount = 0;
364		return;
365	}
366
367	uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize) - 1;
368	char* entryNames = (char*)(fBlock->nameEnds + entryCount);
369
370	uint32 nameOffset = index == 0 ? 0 : fBlock->nameEnds[index - 1];
371	uint32 nameEnd = fBlock->nameEnds[index];
372	uint32 lastNameEnd = entryCount == 0
373		? 0 : fBlock->nameEnds[entryCount - 1];
374
375	if (index < entryCount - 1) {
376		uint32 nameLength = nameEnd - nameOffset;
377
378		// remove the element from the block indices array
379		memmove(&blockIndices[-entryCount + 2], &blockIndices[-entryCount + 1],
380			8 * (entryCount - index - 1));
381
382		// move and update the nameEnds entries > index
383		for (int32 i = index + 1; i < entryCount; i++)
384			fBlock->nameEnds[i - 1] = fBlock->nameEnds[i] - nameLength;
385
386		// move the names < index by 2 bytes
387		if (index > 0)
388			memmove(entryNames - 2, entryNames, nameOffset);
389
390		// move the names > index
391		memmove(entryNames - 2 + nameOffset, entryNames + nameEnd,
392			lastNameEnd - nameEnd);
393	} else {
394		// the nameEnds array shrinks -- move all names
395		memmove(entryNames - 2, entryNames, nameOffset);
396	}
397
398	// we have removed the entry
399	fBlock->entryCount--;
400ASSERT(Check());
401}
402
403
404/*!	Moves all entries beyond \a splitIndex (inclusively) to the empty block
405	\a other.
406*/
407void
408DirEntryBlock::SplitBlock(int32 splitIndex, DirEntryBlock& other)
409{
410	ASSERT(other.EntryCount() == 0);
411	ASSERT(splitIndex <= EntryCount());
412
413	int32 entryCount = EntryCount();
414	if (splitIndex == entryCount)
415		return;
416	int32 otherEntryCount = entryCount - splitIndex;
417
418	// copy block indices
419	uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize);
420	uint64* otherBlockIndices
421		= (uint64*)((uint8*)other.fBlock + other.fBlockSize);
422		// note: both point after the last entry, unlike in other methods
423	memcpy(otherBlockIndices - otherEntryCount, blockIndices - entryCount,
424		8 * otherEntryCount);
425
426	// copy the name end offsets
427	uint32 namesOffset = splitIndex > 0
428		? fBlock->nameEnds[splitIndex - 1] : 0;
429	for (int32 i = splitIndex; i < entryCount; i++) {
430		other.fBlock->nameEnds[i - splitIndex] = fBlock->nameEnds[i]
431			- namesOffset;
432	}
433
434	// copy the names
435	char* entryNames = (char*)(fBlock->nameEnds + entryCount);
436	char* otherEntryNames
437		= (char*)(other.fBlock->nameEnds + otherEntryCount);
438	memcpy(otherEntryNames, entryNames + namesOffset,
439		fBlock->nameEnds[entryCount - 1] - namesOffset);
440
441	// the name ends array shrinks -- move the names
442	if (splitIndex > 0) {
443		char* newEntryNames = (char*)(fBlock->nameEnds + splitIndex);
444		memmove(newEntryNames, entryNames, namesOffset);
445	}
446
447	// update the entry counts
448	fBlock->entryCount = splitIndex;
449	other.fBlock->entryCount = otherEntryCount;
450ASSERT(Check());
451ASSERT(other.Check());
452}
453
454
455bool
456DirEntryBlock::Check() const
457{
458	int32 entryCount = EntryCount();
459	if (entryCount == 0)
460		return true;
461
462	// Check size: Both name ends and block index arrays must fit and we need
463	// at least one byte per name.
464	size_t size = sizeof(*fBlock) + entryCount * 10;
465	if (size + entryCount > fBlockSize) {
466		ERROR("Invalid dir entry block: entry count %d requires minimum size "
467			"of %" B_PRIuSIZE " + %d bytes, but block size is %" B_PRIuSIZE
468			"\n", (int)entryCount, size, (int)entryCount, fBlockSize);
469		return false;
470	}
471
472	// check the name ends and block indices arrays and the names
473	const char* entryNames = (char*)(fBlock->nameEnds + entryCount);
474	const uint64* blockIndices = (uint64*)((uint8*)fBlock + fBlockSize) - 1;
475	const char* previousName = NULL;
476	uint16 previousNameLength = 0;
477	uint16 previousEnd = 0;
478
479	for (int32 i = 0; i < entryCount; i++) {
480		// check name end
481		uint16 nameEnd = fBlock->nameEnds[i];
482		if (nameEnd <= previousEnd || nameEnd > fBlockSize - size) {
483			ERROR("Invalid dir entry block: name end offset of entry %" B_PRId32
484				": %u, previous: %u\n", i, nameEnd, previousEnd);
485			return false;
486		}
487
488		// check name length
489		uint16 nameLength = nameEnd - previousEnd;
490		if (nameLength > kCheckSumFSNameLength) {
491			ERROR("Invalid dir entry block: name of entry %" B_PRId32 " too "
492				"long: %u\n", i, nameLength);
493			return false;
494		}
495
496		// verify that the name doesn't contain a null char
497		const char* name = entryNames + previousEnd;
498		if (strnlen(name, nameLength) != nameLength) {
499			ERROR("Invalid dir entry block: name of entry %" B_PRId32
500				" contains a null char\n", i);
501			return false;
502		}
503
504		// compare the name with the previous name
505		if (i > 0) {
506			int cmp = compare_names(previousName, previousNameLength, name,
507				nameLength);
508			if (cmp == 0) {
509				ERROR("Invalid dir entry block: entries %" B_PRId32 "/%"
510					B_PRId32 " have the same name: \"%.*s\"\n", i - 1, i,
511					(int)nameLength, name);
512				return false;
513			} else if (cmp > 0) {
514				ERROR("Invalid dir entry block: entries %" B_PRId32 "/%"
515					B_PRId32 " out of order: \"%.*s\" > \"%.*s\"\n", i - 1, i,
516					(int)previousNameLength, previousName, (int)nameLength,
517					name);
518				return false;
519			}
520		}
521
522		// check the block index
523		if (blockIndices[-i] < kCheckSumFSSuperBlockOffset / B_PAGE_SIZE) {
524			ERROR("Invalid dir entry block: entry %" B_PRId32
525				" has invalid block index: %" B_PRIu64, i, blockIndices[-i]);
526			return false;
527		}
528
529		previousName = name;
530		previousNameLength = nameLength;
531		previousEnd = nameEnd;
532	}
533
534	return true;
535}
536
537
538// #pragma mark - DirEntryTree
539
540
541DirEntryTree::DirEntryTree(Directory* directory)
542	:
543	fDirectory(directory)
544{
545}
546
547
548status_t
549DirEntryTree::LookupEntry(const char* name, uint64& _blockIndex)
550{
551	FUNCTION("name: \"%s\"\n", name);
552
553	status_t error = _InitReadOnly();
554	if (error != B_OK)
555		RETURN_ERROR(error);
556
557	size_t nameLength = strlen(name);
558	if (nameLength > kCheckSumFSNameLength)
559		RETURN_ERROR(B_ENTRY_NOT_FOUND);
560
561	uint32 depth = _Depth();
562
563	DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize);
564ASSERT(entryBlock.Check());
565
566	Block block;
567
568	for (uint32 level = 0; level <= depth; level++) {
569		if (entryBlock.EntryCount() == 0)
570			RETURN_ERROR(level == 0 ? B_ENTRY_NOT_FOUND : B_BAD_DATA);
571
572		bool exactMatch;
573		int32 index = entryBlock.FindInsertionIndex(name, nameLength,
574			exactMatch);
575
576		// If we haven't found an exact match, the index points to the first
577		// entry that is greater or after the last entry.
578		if (!exactMatch) {
579			if (index == 0) {
580				// The first entry is already greater, so the branch doesn't
581				// contain the entry we're looking for.
582				RETURN_ERROR(B_ENTRY_NOT_FOUND);
583			}
584
585			index--;
586		}
587
588		PRINT("  level %" B_PRId32 " -> index: %" B_PRId32 " %sexact\n", level,
589			index, exactMatch ? "" : " not ");
590
591		uint64 blockIndex = entryBlock.BlockIndexAt(index);
592
593		if (level == depth) {
594			// final level -- here we should have an exact match
595			if (!exactMatch)
596				RETURN_ERROR(B_ENTRY_NOT_FOUND);
597
598			_blockIndex = blockIndex;
599			return B_OK;
600		}
601
602		// not the final level -- load the block and descend to the next
603		// level
604		if (!block.GetReadable(fDirectory->GetVolume(), blockIndex))
605			RETURN_ERROR(B_ERROR);
606
607		entryBlock.SetTo((checksumfs_dir_entry_block*)block.Data(),
608			B_PAGE_SIZE);
609ASSERT(entryBlock.Check());
610	}
611
612	// cannot get here, but keep the compiler happy
613	RETURN_ERROR(B_ENTRY_NOT_FOUND);
614}
615
616
617status_t
618DirEntryTree::LookupNextEntry(const char* name, char* foundName,
619	size_t& _foundNameLength, uint64& _blockIndex)
620{
621	FUNCTION("name: \"%s\"\n", name);
622
623	status_t error = _InitReadOnly();
624	if (error != B_OK)
625		RETURN_ERROR(error);
626
627	size_t nameLength = strlen(name);
628	if (nameLength > kCheckSumFSNameLength)
629		RETURN_ERROR(B_ENTRY_NOT_FOUND);
630
631	int32 depth = _Depth();
632
633	LevelInfo* infos = new(std::nothrow) LevelInfo[
634		kCheckSumFSMaxDirEntryTreeDepth + 1];
635	if (infos == NULL)
636		RETURN_ERROR(B_NO_MEMORY);
637	ArrayDeleter<LevelInfo> infosDeleter(infos);
638
639	infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize);
640ASSERT(infos[0].entryBlock.Check());
641
642	// descend the tree
643	for (int32 level = 0; level <= depth; level++) {
644		LevelInfo& info = infos[level];
645
646		if (info.entryBlock.EntryCount() == 0) {
647			if (level == 0) {
648				// directory is empty
649				return B_ENTRY_NOT_FOUND;
650			}
651
652			RETURN_ERROR(B_BAD_DATA);
653		}
654
655		info.index = info.entryBlock.FindInsertionIndex(name, nameLength,
656			info.exactMatch);
657
658		PRINT("  level %" B_PRId32 " -> index: %" B_PRId32 " %sexact\n", level,
659			info.index, info.exactMatch ? "" : " not ");
660
661		if (level == depth)
662			break;
663
664		// If we haven't found an exact match, the index points to the first
665		// entry that is greater or after the last entry.
666		if (!info.exactMatch && info.index > 0)
667			info.index--;
668
669		uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index);
670
671		// not the final level -- load the block and descend to the next
672		// level
673		LevelInfo& nextInfo = infos[level + 1];
674		if (!nextInfo.block.GetReadable(fDirectory->GetVolume(),
675				nextBlockIndex)) {
676			RETURN_ERROR(B_ERROR);
677		}
678
679		nextInfo.entryBlock.SetTo(
680			(checksumfs_dir_entry_block*)nextInfo.block.Data(),
681			B_PAGE_SIZE);
682ASSERT(nextInfo.entryBlock.Check());
683	}
684
685	if (infos[depth].exactMatch)
686		infos[depth].index++;
687
688	if (infos[depth].index >= infos[depth].entryBlock.EntryCount()) {
689		// We're at the end of the last block -- we need to track back to find a
690		// greater branch.
691		PRINT("  searching for greater branch\n");
692
693		int32 level;
694		for (level = depth - 1; level >= 0; level--) {
695			LevelInfo& info = infos[level];
696			if (++info.index < info.entryBlock.EntryCount()) {
697				PRINT("  found greater branch: level: %" B_PRId32 " -> index: %"
698					B_PRId32 "\n", level, info.index);
699				break;
700			}
701		}
702
703		if (level < 0)
704			return B_ENTRY_NOT_FOUND;
705
706		// We've found a greater branch -- get the first entry in that branch.
707		for (level++; level <= depth; level++) {
708			LevelInfo& previousInfo = infos[level - 1];
709			LevelInfo& info = infos[level];
710
711			uint64 nextBlockIndex = previousInfo.entryBlock.BlockIndexAt(
712				previousInfo.index);
713
714			// load the block
715			if (!info.block.GetReadable(fDirectory->GetVolume(),
716					nextBlockIndex)) {
717				RETURN_ERROR(B_ERROR);
718			}
719
720			info.entryBlock.SetTo(
721				(checksumfs_dir_entry_block*)info.block.Data(), B_PAGE_SIZE);
722ASSERT(info.entryBlock.Check());
723
724			info.index = 0;
725			if (info.entryBlock.EntryCount() == 0)
726				RETURN_ERROR(B_BAD_DATA);
727		}
728	}
729
730	// get and check the name
731	LevelInfo& info = infos[depth];
732
733	name = info.entryBlock.NameAt(info.index, nameLength);
734	if (nameLength > kCheckSumFSNameLength
735		|| strnlen(name, nameLength) != nameLength) {
736		RETURN_ERROR(B_BAD_DATA);
737	}
738
739	// set the return values
740	memcpy(foundName, name, nameLength);
741	foundName[nameLength] = '\0';
742	_foundNameLength = nameLength;
743	_blockIndex = info.entryBlock.BlockIndexAt(info.index);
744
745	PRINT("  found entry: \"%s\" -> %" B_PRIu64 "\n", foundName, _blockIndex);
746
747	return B_OK;
748}
749
750
751status_t
752DirEntryTree::InsertEntry(const char* name, uint64 blockIndex,
753	Transaction& transaction)
754{
755	FUNCTION("name: \"%s\", blockIndex: %" B_PRIu64 "\n", name, blockIndex);
756
757	status_t error = _InitWritable(transaction);
758	if (error != B_OK)
759		RETURN_ERROR(error);
760
761	size_t nameLength = strlen(name);
762	if (nameLength == 0)
763		RETURN_ERROR(B_BAD_VALUE);
764	if (nameLength > kCheckSumFSNameLength)
765		RETURN_ERROR(B_NAME_TOO_LONG);
766
767	int32 depth = _Depth();
768
769	LevelInfo* infos = new(std::nothrow) LevelInfo[
770		kCheckSumFSMaxDirEntryTreeDepth + 1];
771	if (infos == NULL)
772		RETURN_ERROR(B_NO_MEMORY);
773	ArrayDeleter<LevelInfo> infosDeleter(infos);
774
775	infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize);
776
777	for (int32 level = 0; level <= depth; level++) {
778		LevelInfo& info = infos[level];
779
780		if (info.entryBlock.EntryCount() == 0) {
781			if (level == 0) {
782				PRINT("  directory is empty\n");
783				// directory is empty
784				info.index = 0;
785				break;
786			}
787
788			RETURN_ERROR(B_BAD_DATA);
789		}
790
791		info.index = info.entryBlock.FindInsertionIndex(name, nameLength,
792			info.exactMatch);
793
794		PRINT("  level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32
795			" %sexact\n", level,
796			level == 0 ? fDirectory->BlockIndex() : info.block.Index(),
797			info.index, info.exactMatch ? "" : " not ");
798
799		// Finding an exact match -- even in the non-final level -- means
800		// that there's an entry with that name.
801		if (info.exactMatch)
802			RETURN_ERROR(B_FILE_EXISTS);
803
804		if (level == depth) {
805			// final level -- here we need to insert the entry
806			break;
807		}
808
809		// Since we haven't found an exact match, the index points to the
810		// first entry that is greater or after the last entry.
811		info.index--;
812
813		uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index);
814
815		// not the final level -- load the block and descend to the next
816		// level
817		LevelInfo& nextInfo = infos[level + 1];
818		if (!nextInfo.block.GetReadable(fDirectory->GetVolume(),
819				nextBlockIndex)) {
820			RETURN_ERROR(B_ERROR);
821		}
822
823		nextInfo.entryBlock.SetTo(
824			(checksumfs_dir_entry_block*)nextInfo.block.Data(),
825			B_PAGE_SIZE);
826ASSERT(nextInfo.entryBlock.Check());
827	}
828
829	// We've found the insertion point. Insert the key and iterate backwards
830	// to perform the potentially necessary updates. Insertion at index 0 of
831	// the block changes the block's key, requiring an update in the parent
832	// block. Insertion or key update can cause the block to be split (if
833	// there's not enough space left in it), requiring an insertion in the
834	// parent block. So we start with a pending insertion in the leaf block
835	// and work our way upwards, performing key updates and insertions as
836	// necessary.
837
838	return _UpdateOrInsertKey(infos, depth, name, nameLength, blockIndex, true,
839		transaction);
840}
841
842
843status_t
844DirEntryTree::RemoveEntry(const char* name, Transaction& transaction)
845{
846	FUNCTION("name: \"%s\"\n", name);
847
848	status_t error = _InitWritable(transaction);
849	if (error != B_OK)
850		RETURN_ERROR(error);
851
852	size_t nameLength = strlen(name);
853	if (nameLength == 0)
854		RETURN_ERROR(B_BAD_VALUE);
855	if (nameLength > kCheckSumFSNameLength)
856		RETURN_ERROR(B_ENTRY_NOT_FOUND);
857
858	int32 depth = _Depth();
859
860	LevelInfo* infos = new(std::nothrow) LevelInfo[
861		kCheckSumFSMaxDirEntryTreeDepth + 1];
862	if (infos == NULL)
863		RETURN_ERROR(B_NO_MEMORY);
864	ArrayDeleter<LevelInfo> infosDeleter(infos);
865
866	infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize);
867
868	for (int32 level = 0; level <= depth; level++) {
869		LevelInfo& info = infos[level];
870
871		if (info.entryBlock.EntryCount() == 0) {
872			if (level == 0) {
873				// directory is empty
874				PRINT("  directory is empty\n");
875				RETURN_ERROR(B_ENTRY_NOT_FOUND);
876			}
877
878			RETURN_ERROR(B_BAD_DATA);
879		}
880
881		info.index = info.entryBlock.FindInsertionIndex(name, nameLength,
882			info.exactMatch);
883
884		PRINT("  level %" B_PRId32 ", block %" B_PRIu64 " -> index: %" B_PRId32
885			" %sexact\n", level,
886			level == 0 ? fDirectory->BlockIndex() : info.block.Index(),
887			info.index, info.exactMatch ? "" : " not ");
888
889		if (level == depth) {
890			// final level -- here the entry should be found
891			if (!info.exactMatch)
892				RETURN_ERROR(B_ENTRY_NOT_FOUND);
893			break;
894		}
895
896		// If we haven't found an exact match, the index points to the first
897		// entry that is greater or after the last entry.
898		if (!info.exactMatch) {
899			if (info.index == 0) {
900				// The first entry is already greater, so the branch doesn't
901				// contain the entry we're looking for.
902				RETURN_ERROR(B_ENTRY_NOT_FOUND);
903			}
904
905			info.index--;
906		}
907
908		uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index);
909
910		// not the final level -- load the block and descend to the next
911		// level
912		LevelInfo& nextInfo = infos[level + 1];
913		if (!nextInfo.block.GetReadable(fDirectory->GetVolume(),
914				nextBlockIndex)) {
915			RETURN_ERROR(B_ERROR);
916		}
917
918		nextInfo.entryBlock.SetTo(
919			(checksumfs_dir_entry_block*)nextInfo.block.Data(),
920			B_PAGE_SIZE);
921ASSERT(nextInfo.entryBlock.Check());
922	}
923
924	// We've found the entry. Insert the key and iterate backwards to perform
925	// the potentially necessary updates. Removal at index 0 of the block
926	// changes the block's key, requiring an update in the parent block.
927	// Removal of the last entry will require removal of the block from its
928	// parent. Key update can cause the block to be split (if there's not
929	// enough space left in it), requiring an insertion in the parent block.
930	// We start with a pending removal in the leaf block and work our way
931	// upwards as long as the blocks become empty. As soon as a key update is
932	// required, we delegate the remaining to the update/insert backwards loop.
933
934	for (int32 level = depth; level >= 0; level--) {
935		LevelInfo& info = infos[level];
936
937		// make the block writable
938		if (level > 0) {
939			error = info.block.MakeWritable(transaction);
940			if (error != B_OK)
941				RETURN_ERROR(error);
942		}
943
944		PRINT("  level: %" B_PRId32 ", index: %" B_PRId32 ": removing key "
945			"\"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index, (int)nameLength,
946			name, nameLength);
947
948		if (info.entryBlock.EntryCount() == 1) {
949			// That's the last key in the block. Unless that's the root level,
950			// we remove the block completely.
951			PRINT("  -> block is empty\n");
952			if (level == 0) {
953				info.entryBlock.RemoveEntry(info.index);
954				return B_OK;
955			}
956
957			error = fDirectory->GetVolume()->GetBlockAllocator()->Free(
958				info.block.Index(), 1, transaction);
959			if (error != B_OK)
960				RETURN_ERROR(error);
961			fDirectory->SetSize(fDirectory->Size() - B_PAGE_SIZE);
962
963			// remove the key (the same one) from the parent block
964			continue;
965		}
966
967		// There are more entries, so just remove the entry in question. If it
968		// is not the first one, we're done, otherwise we have to update the
969		// block's key in the parent block.
970		info.entryBlock.RemoveEntry(info.index);
971
972		if (info.index > 0 || level == 0)
973			return B_OK;
974
975		name = info.entryBlock.NameAt(0, nameLength);
976		return _UpdateOrInsertKey(infos, level - 1, name, nameLength, 0, false,
977			transaction);
978	}
979
980	return B_OK;
981}
982
983
984status_t
985DirEntryTree::FreeTree(Transaction& transaction)
986{
987	status_t error = _InitReadOnly();
988	if (error != B_OK)
989		RETURN_ERROR(error);
990
991	int32 depth = _Depth();
992	if (depth == 0)
993		return B_OK;
994
995	LevelInfo* infos = new(std::nothrow) LevelInfo[
996		kCheckSumFSMaxDirEntryTreeDepth + 1];
997	if (infos == NULL)
998		RETURN_ERROR(B_NO_MEMORY);
999	ArrayDeleter<LevelInfo> infosDeleter(infos);
1000
1001	infos[0].entryBlock.SetTo(fRootEntryBlock, fRootEntryBlockSize);
1002	infos[0].index = 0;
1003
1004	// Iterate through the tree in post order. We don't touch the content of
1005	// any block, we only free the blocks.
1006	int32 level = 0;
1007	while (true) {
1008		LevelInfo& info = infos[level];
1009
1010		if (level == depth || info.index >= info.entryBlock.EntryCount()) {
1011			// we're through with the block
1012			if (level == 0)
1013				break;
1014
1015			// free it
1016			error = fDirectory->GetVolume()->GetBlockAllocator()->Free(
1017				info.block.Index(), 1, transaction);
1018
1019			// continue with the next sibling branch
1020			infos[--level].index++;
1021		}
1022
1023		// descend to next level
1024		uint64 nextBlockIndex = info.entryBlock.BlockIndexAt(info.index);
1025
1026		LevelInfo& nextInfo = infos[++level];
1027		if (!nextInfo.block.GetReadable(fDirectory->GetVolume(),
1028				nextBlockIndex)) {
1029			RETURN_ERROR(B_ERROR);
1030		}
1031
1032		nextInfo.entryBlock.SetTo(
1033			(checksumfs_dir_entry_block*)nextInfo.block.Data(),
1034			B_PAGE_SIZE);
1035	}
1036
1037	return B_OK;
1038}
1039
1040
1041bool
1042DirEntryTree::IsEmpty() const
1043{
1044	DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize);
1045	return entryBlock.EntryCount() == 0;
1046}
1047
1048
1049bool
1050DirEntryTree::Check()
1051{
1052	if (_InitReadOnly() != B_OK) {
1053		ERROR("DirEntryTree::Check(): Init failed!\n");
1054		return false;
1055	}
1056
1057	DirEntryBlock entryBlock(fRootEntryBlock, fRootEntryBlockSize);
1058	return _Check(0, fDirectory->BlockIndex(), NULL, 0, entryBlock);
1059}
1060
1061
1062status_t
1063DirEntryTree::_InitReadOnly()
1064{
1065	if (!fRootBlock.GetReadable(fDirectory->GetVolume(),
1066			fDirectory->BlockIndex())) {
1067		RETURN_ERROR(B_ERROR);
1068	}
1069
1070	return _InitCommon();
1071}
1072
1073
1074status_t
1075DirEntryTree::_InitWritable(Transaction& transaction)
1076{
1077	if (!fRootBlock.GetWritable(fDirectory->GetVolume(),
1078			fDirectory->BlockIndex(), transaction)) {
1079		RETURN_ERROR(B_ERROR);
1080	}
1081
1082	return _InitCommon();
1083}
1084
1085
1086status_t
1087DirEntryTree::_InitCommon()
1088{
1089	fTree = (checksumfs_dir_entry_tree*)
1090		((uint8*)fRootBlock.Data() + sizeof(checksumfs_node));
1091
1092	fRootEntryBlock = (checksumfs_dir_entry_block*)(fTree + 1);
1093	fRootEntryBlockSize = B_PAGE_SIZE
1094		- ((addr_t)fRootEntryBlock - (addr_t)fRootBlock.Data());
1095
1096	if (_Depth() > kCheckSumFSMaxDirEntryTreeDepth)
1097		RETURN_ERROR(B_BAD_DATA);
1098
1099	return B_OK;
1100}
1101
1102
1103status_t
1104DirEntryTree::_UpdateOrInsertKey(LevelInfo* infos, int32 level,
1105	const char* name, size_t nameLength, uint64 blockIndex, bool insertKey,
1106	Transaction& transaction)
1107{
1108	FUNCTION("level: %" B_PRId32 ": %s name: \"%.*s\" (%" B_PRIuSIZE "), "
1109		"blockIndex: %" B_PRIu64 "\n", level, insertKey ? "insert" : "update",
1110		(int)nameLength, name, nameLength, blockIndex);
1111
1112	// Some temporary blocks: newBlock is used when a block is split. The
1113	// other three are used when a key update respectively insertion in the
1114	// parent block becomes necessary. We only need them, since the name
1115	// we update/insert is potentially from a block and instead of cloning
1116	// the name, we simple postpone putting the block until we don't need
1117	// the name anymore.
1118	Block newBlock;
1119	Block tempBlockUpdate;
1120	Block tempBlockUpdateInsert;
1121	Block tempBlockInsert;
1122
1123	int32 depth = _Depth();
1124	status_t error;
1125
1126	bool updateNextKey = !insertKey;
1127	bool insertNextKey = insertKey;
1128	const char* nameToUpdate = name;
1129	size_t nameToUpdateLength = nameLength;
1130	const char* nextNameToInsert = name;
1131	size_t nextNameToInsertLength = nameLength;
1132	uint64 nextBlockIndexToInsert = blockIndex;
1133
1134	for (; level >= 0; level--) {
1135		LevelInfo& info = infos[level];
1136
1137		bool updateThisKey = updateNextKey;
1138		bool insertThisKey = insertNextKey;
1139
1140		if (!updateThisKey && !insertThisKey)
1141			return B_OK;
1142
1143		updateNextKey = false;
1144		insertNextKey = false;
1145
1146		blockIndex = nextBlockIndexToInsert;
1147		name = nextNameToInsert;
1148		nameLength = nextNameToInsertLength;
1149
1150		// make the block writable
1151		if (level > 0) {
1152			error = info.block.MakeWritable(transaction);
1153			if (error != B_OK)
1154				RETURN_ERROR(error);
1155		}
1156
1157		if (updateThisKey) {
1158			PRINT("  level: %" B_PRId32 ", index: %" B_PRId32 ": updating key "
1159				"to \"%.*s\" (%" B_PRIuSIZE ")\n", level, info.index,
1160				(int)nameToUpdateLength, nameToUpdate, nameToUpdateLength);
1161
1162			size_t oldNameLength;
1163			info.entryBlock.NameAt(info.index, oldNameLength);
1164			size_t spaceNeeded = oldNameLength < nameToUpdateLength
1165				? nameToUpdateLength - oldNameLength : 0;
1166
1167			if (spaceNeeded <= info.entryBlock.FreeSpace()) {
1168				info.entryBlock.ReplaceEntryName(info.index, nameToUpdate,
1169					nameToUpdateLength);
1170				if (info.index == 0) {
1171					// we updated at index 0, so we need to update this
1172					// block's key in the parent block
1173					updateNextKey = true;
1174					nameToUpdate = info.entryBlock.NameAt(0,
1175						nameToUpdateLength);
1176
1177					// make sure the new block is kept until we no longer
1178					// use the name in the next iteration
1179					tempBlockUpdate.TransferFrom(info.block);
1180				}
1181			} else if (level == 0) {
1182				// We need to split the root block -- clone it first.
1183				error = _InsertEntryIncrementDepth(infos, transaction);
1184				if (error != B_OK)
1185					RETURN_ERROR(error);
1186
1187				level = 2;
1188					// _InsertEntryIncrementDepth() moved the root block
1189					// content to level 1, where we want to continue.
1190				updateNextKey = true;
1191				insertNextKey = insertThisKey;
1192				continue;
1193			} else {
1194				// We need to split this non-root block.
1195				int32 splitIndex;
1196				error = _InsertEntrySplitBlock(level, info, spaceNeeded,
1197					transaction, newBlock, splitIndex);
1198				if (error != B_OK)
1199					RETURN_ERROR(error);
1200
1201				nextBlockIndexToInsert = newBlock.Index();
1202
1203				DirEntryBlock newEntryBlock(
1204					(checksumfs_dir_entry_block*)newBlock.Data(),
1205					B_PAGE_SIZE);
1206ASSERT(newEntryBlock.Check());
1207
1208				if (info.index < splitIndex) {
1209					ASSERT(info.entryBlock.FreeSpace() >= spaceNeeded);
1210
1211					info.entryBlock.ReplaceEntryName(info.index,
1212						nameToUpdate, nameToUpdateLength);
1213					if (info.index == 0) {
1214						// we updated at index 0, so we need to update this
1215						// block's key in the parent block
1216						updateNextKey = true;
1217						nameToUpdate = info.entryBlock.NameAt(0,
1218							nameToUpdateLength);
1219
1220						// make sure the new block is kept until we no
1221						// longer use the name in the next iteration
1222						tempBlockUpdate.TransferFrom(info.block);
1223					}
1224				} else {
1225					ASSERT(newEntryBlock.FreeSpace() >= spaceNeeded);
1226
1227					// we need to transfer the block to the info, in case we
1228					// also need to insert a key below
1229					info.block.TransferFrom(newBlock);
1230					info.entryBlock.SetTo(
1231						(checksumfs_dir_entry_block*)info.block.Data(),
1232						B_PAGE_SIZE);
1233ASSERT(info.entryBlock.Check());
1234
1235					info.index -= splitIndex;
1236
1237					info.entryBlock.ReplaceEntryName(info.index, nameToUpdate,
1238						nameToUpdateLength);
1239				}
1240
1241				// the newly created block needs to be inserted in the
1242				// parent
1243				insertNextKey = true;
1244				nextNameToInsert = newEntryBlock.NameAt(0,
1245					nextNameToInsertLength);
1246
1247				// make sure the new block is kept until we no longer use
1248				// the name in the next iteration (might already have been
1249				// transferred to entry.block)
1250				tempBlockUpdateInsert.TransferFrom(newBlock);
1251			}
1252		}
1253
1254		if (insertThisKey) {
1255			// insert after the block we descended
1256			if (level < depth)
1257				info.index++;
1258
1259			PRINT("  level: %" B_PRId32 ", index: %" B_PRId32 ": inserting key "
1260				"\"%.*s\" (%" B_PRIuSIZE "), blockIndex: %" B_PRIu64 "\n",
1261				level, info.index, (int)nameLength, name, nameLength,
1262				blockIndex);
1263
1264			if (info.entryBlock.FreeSpace() >= nameLength + 10) {
1265				info.entryBlock.InsertEntry(info.index, name,
1266					nameLength, blockIndex);
1267				if (info.index == 0) {
1268					// we inserted at index 0, so we need to update this
1269					// block's key in the parent block
1270					updateNextKey = true;
1271					nameToUpdate = info.entryBlock.NameAt(0,
1272						nameToUpdateLength);
1273
1274					// make sure the new block is kept until we no longer
1275					// use the name in the next iteration
1276					tempBlockUpdate.TransferFrom(info.block);
1277				}
1278				continue;
1279			}
1280
1281			// Not enough space left in the block -- we need to split it.
1282			ASSERT(!insertNextKey);
1283
1284			// for level == 0 we need to clone the block first
1285			if (level == 0) {
1286				error = _InsertEntryIncrementDepth(infos, transaction);
1287				if (error != B_OK)
1288					RETURN_ERROR(error);
1289
1290				level = 2;
1291					// _InsertEntryIncrementDepth() moved the root block
1292					// content to level 1, where we want to continue.
1293				updateNextKey = false;
1294				insertNextKey = true;
1295				continue;
1296			}
1297
1298			int32 splitIndex;
1299			error = _InsertEntrySplitBlock(level, info, nameLength + 10,
1300				transaction, newBlock, splitIndex);
1301			if (error != B_OK)
1302				RETURN_ERROR(error);
1303
1304			DirEntryBlock newEntryBlock(
1305				(checksumfs_dir_entry_block*)newBlock.Data(),
1306				B_PAGE_SIZE);
1307ASSERT(newEntryBlock.Check());
1308
1309			if (info.index < splitIndex) {
1310				ASSERT(info.entryBlock.FreeSpace() >= nameLength + 10);
1311
1312				info.entryBlock.InsertEntry(info.index, name,
1313					nameLength, blockIndex);
1314				if (info.index == 0) {
1315					// we inserted at index 0, so we need to update this
1316					// block's key in the parent block
1317					updateNextKey = true;
1318					nameToUpdate = info.entryBlock.NameAt(0,
1319						nameToUpdateLength);
1320
1321					// make sure the new block is kept until we no longer
1322					// use the name in the next iteration
1323					tempBlockUpdate.TransferFrom(info.block);
1324				}
1325			} else {
1326				ASSERT(newEntryBlock.FreeSpace() >= nameLength + 10);
1327
1328				info.index -= splitIndex;
1329
1330				newEntryBlock.InsertEntry(info.index, name, nameLength,
1331					blockIndex);
1332			}
1333
1334			// the newly created block needs to be inserted in the parent
1335			insertNextKey = true;
1336			nextNameToInsert = newEntryBlock.NameAt(0, nextNameToInsertLength);
1337			nextBlockIndexToInsert = newBlock.Index();
1338
1339			// make sure the new block is kept until we no longer use
1340			// the name in the next iteration
1341			tempBlockInsert.TransferFrom(newBlock);
1342		}
1343	}
1344
1345	return B_OK;
1346}
1347
1348
1349status_t
1350DirEntryTree::_InsertEntryIncrementDepth(LevelInfo* infos,
1351	Transaction& transaction)
1352{
1353	FUNCTION("depth: %u -> %u\n", _Depth(), _Depth() + 1);
1354
1355	if (_Depth() >= kCheckSumFSMaxDirEntryTreeDepth)
1356		RETURN_ERROR(B_DEVICE_FULL);
1357
1358	// allocate a new block
1359	AllocatedBlock allocatedBlock(
1360		fDirectory->GetVolume()->GetBlockAllocator(), transaction);
1361
1362	status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex());
1363	if (error != B_OK)
1364		RETURN_ERROR(error);
1365	fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE);
1366
1367	LevelInfo& newInfo = infos[1];
1368	if (!newInfo.block.GetZero(fDirectory->GetVolume(),
1369			allocatedBlock.Index(), transaction)) {
1370		RETURN_ERROR(B_ERROR);
1371	}
1372
1373	allocatedBlock.Detach();
1374
1375	newInfo.entryBlock.SetTo(
1376		(checksumfs_dir_entry_block*)newInfo.block.Data(), B_PAGE_SIZE);
1377ASSERT(newInfo.entryBlock.Check());
1378
1379	// move the old root block contents to the new block
1380	LevelInfo& rootInfo = infos[0];
1381	rootInfo.entryBlock.SplitBlock(0, newInfo.entryBlock);
1382
1383	// add an entry for the new block to the root block
1384	size_t nameLength;
1385	const char* name = newInfo.entryBlock.NameAt(0, nameLength);
1386	rootInfo.entryBlock.InsertEntry(0, name, nameLength,
1387		newInfo.block.Index());
1388
1389	PRINT("  -> new block: %" B_PRIu64 "\n", newInfo.block.Index());
1390
1391	newInfo.index = rootInfo.index;
1392	rootInfo.index = 0;
1393	fTree->depth++;
1394
1395	return B_OK;
1396}
1397
1398
1399status_t
1400DirEntryTree::_InsertEntrySplitBlock(int32 level, LevelInfo& info,
1401	size_t needed, Transaction& transaction, Block& newBlock,
1402	int32& _splitIndex)
1403{
1404	int32 splitIndex = info.entryBlock.FindSplitIndex(info.index,
1405		needed);
1406
1407	FUNCTION("level: %" B_PRId32 ", size needed: %" B_PRIuSIZE ", split index: "
1408		"%" B_PRId32 "/%" B_PRId32 "\n", level, needed, splitIndex,
1409		info.entryBlock.EntryCount());
1410
1411	// allocate a new block
1412	AllocatedBlock allocatedBlock(
1413		fDirectory->GetVolume()->GetBlockAllocator(), transaction);
1414
1415	status_t error = allocatedBlock.Allocate(fDirectory->BlockIndex());
1416	if (error != B_OK)
1417		RETURN_ERROR(error);
1418	fDirectory->SetSize(fDirectory->Size() + B_PAGE_SIZE);
1419
1420	if (!newBlock.GetZero(fDirectory->GetVolume(), allocatedBlock.Index(),
1421			transaction)) {
1422		RETURN_ERROR(B_ERROR);
1423	}
1424
1425	allocatedBlock.Detach();
1426
1427	// split the old block
1428	DirEntryBlock newEntryBlock(
1429		(checksumfs_dir_entry_block*)newBlock.Data(), B_PAGE_SIZE);
1430ASSERT(newEntryBlock.Check());
1431	info.entryBlock.SplitBlock(splitIndex, newEntryBlock);
1432
1433	PRINT("  -> new block: %" B_PRIu64 "\n", newBlock.Index());
1434
1435	_splitIndex = splitIndex;
1436	return B_OK;
1437}
1438
1439
1440bool
1441DirEntryTree::_Check(int32 level, uint64 blockIndex, const char* key,
1442	size_t keyLength, DirEntryBlock& entryBlock)
1443{
1444	// check block for validity
1445	if (!entryBlock.Check()) {
1446		ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %"
1447			B_PRIu64 " not valid!\n", level, blockIndex);
1448		return false;
1449	}
1450
1451	// The root block is allowed to be empty. For all other blocks that is an
1452	// error.
1453	uint32 entryCount = entryBlock.EntryCount();
1454	if (entryCount == 0) {
1455		if (level == 0)
1456			return true;
1457
1458		ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %"
1459			B_PRIu64 " empty!\n", level, blockIndex);
1460		return false;
1461	}
1462
1463	// Verify that the block's first entry matches the key with which the
1464	// parent block refers to it.
1465	if (level > 0) {
1466		size_t nameLength;
1467		const char* name = entryBlock.NameAt(0, nameLength);
1468		if (nameLength != keyLength || strncmp(name, key, keyLength) != 0) {
1469			ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %"
1470				B_PRIu64 " key mismatch: is \"%.*s\", should be \"%.*s\"\n",
1471				level, blockIndex, (int)keyLength, key, (int)nameLength, name);
1472			return false;
1473		}
1474	}
1475
1476	if (level == _Depth())
1477		return true;
1478
1479	// not the final level -- recurse
1480	for (uint32 i = 0; i < entryCount; i++) {
1481		size_t nameLength;
1482		const char* name = entryBlock.NameAt(i, nameLength);
1483		uint64 childBlockIndex = entryBlock.BlockIndexAt(i);
1484
1485		Block childBlock;
1486		if (!childBlock.GetReadable(fDirectory->GetVolume(), childBlockIndex)) {
1487			ERROR("DirEntryTree::Check(): level %" B_PRIu32 ": block %"
1488				B_PRIu64 " failed to get child block %" B_PRIu64 " (entry %"
1489				B_PRIu32 ")\n", level, blockIndex, childBlockIndex, i);
1490		}
1491
1492		DirEntryBlock childEntryBlock(
1493			(checksumfs_dir_entry_block*)childBlock.Data(), B_PAGE_SIZE);
1494
1495		if (!_Check(level + 1, childBlockIndex, name, nameLength,
1496				childEntryBlock)) {
1497			return false;
1498		}
1499	}
1500
1501	return true;
1502}
1503
1504
1505// #pragma mark - Directory
1506
1507
1508Directory::Directory(Volume* volume, uint64 blockIndex,
1509	const checksumfs_node& nodeData)
1510	:
1511	Node(volume, blockIndex, nodeData)
1512{
1513}
1514
1515
1516Directory::Directory(Volume* volume, mode_t mode)
1517	:
1518	Node(volume, mode)
1519{
1520}
1521
1522
1523Directory::~Directory()
1524{
1525}
1526
1527
1528void
1529Directory::DeletingNode()
1530{
1531	Node::DeletingNode();
1532
1533	// iterate through the directory and remove references to all entries' nodes
1534	char* name = (char*)malloc(kCheckSumFSNameLength + 1);
1535	if (name != NULL) {
1536		name[0] = '\0';
1537
1538		DirEntryTree entryTree(this);
1539		size_t nameLength;
1540		uint64 blockIndex;
1541		while (entryTree.LookupNextEntry(name, name, nameLength,
1542				blockIndex) == B_OK) {
1543			Node* node;
1544			if (GetVolume()->GetNode(blockIndex, node) == B_OK) {
1545				Transaction transaction(GetVolume());
1546				if (transaction.StartAndAddNode(node) == B_OK) {
1547					node->SetHardLinks(node->HardLinks() - 1);
1548					if (node->HardLinks() == 0)
1549						GetVolume()->RemoveNode(node);
1550
1551					if (transaction.Commit() != B_OK) {
1552						ERROR("Failed to commit transaction for dereferencing "
1553							"entry node of deleted directory at %" B_PRIu64
1554							"\n", BlockIndex());
1555					}
1556				} else {
1557					ERROR("Failed to start transaction for dereferencing "
1558						"entry node of deleted directory at %" B_PRIu64 "\n",
1559						BlockIndex());
1560				}
1561
1562				GetVolume()->PutNode(node);
1563			} else {
1564				ERROR("Failed to get node %" B_PRIu64 " referenced by deleted "
1565					"directory at %" B_PRIu64 "\n", blockIndex, BlockIndex());
1566			}
1567		}
1568
1569		free(name);
1570	}
1571
1572	// free the directory entry block tree
1573	Transaction transaction(GetVolume());
1574	if (transaction.Start() != B_OK) {
1575		ERROR("Failed to start transaction for freeing entry tree of deleted "
1576			"directory at %" B_PRIu64 "\n", BlockIndex());
1577		return;
1578	}
1579
1580	DirEntryTree entryTree(this);
1581	if (entryTree.FreeTree(transaction) != B_OK) {
1582		ERROR("Failed to freeing entry tree of deleted directory at %" B_PRIu64
1583			"\n", BlockIndex());
1584		return;
1585	}
1586
1587	if (transaction.Commit() != B_OK) {
1588		ERROR("Failed to commit transaction for freeing entry tree of deleted "
1589			"directory at %" B_PRIu64 "\n", BlockIndex());
1590	}
1591}
1592
1593
1594status_t
1595Directory::LookupEntry(const char* name, uint64& _blockIndex)
1596{
1597	DirEntryTree entryTree(this);
1598	return entryTree.LookupEntry(name, _blockIndex);
1599}
1600
1601
1602status_t
1603Directory::LookupNextEntry(const char* name, char* foundName,
1604	size_t& _foundNameLength, uint64& _blockIndex)
1605{
1606	DirEntryTree entryTree(this);
1607	return entryTree.LookupNextEntry(name, foundName, _foundNameLength,
1608		_blockIndex);
1609}
1610
1611
1612status_t
1613Directory::InsertEntry(const char* name, uint64 blockIndex,
1614	Transaction& transaction)
1615{
1616	DirEntryTree entryTree(this);
1617
1618	status_t error = entryTree.InsertEntry(name, blockIndex, transaction);
1619	if (error == B_OK)
1620		ASSERT(entryTree.Check());
1621	return error;
1622}
1623
1624
1625status_t
1626Directory::RemoveEntry(const char* name, Transaction& transaction,
1627	bool* _lastEntryRemoved)
1628{
1629	DirEntryTree entryTree(this);
1630
1631	status_t error = entryTree.RemoveEntry(name, transaction);
1632	if (error != B_OK)
1633		return error;
1634
1635	ASSERT(entryTree.Check());
1636
1637	if (_lastEntryRemoved != NULL)
1638		*_lastEntryRemoved = entryTree.IsEmpty();
1639
1640	return B_OK;
1641}
1642