xref: /haiku/src/add-ons/kernel/file_systems/bfs/CheckVisitor.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2002-2012, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2012, Andreas Henriksson, sausageboy@gmail.com
4  * This file may be used under the terms of the MIT License.
5  */
6 
7 
8 //! File system error checking
9 
10 
11 #include "CheckVisitor.h"
12 
13 #include "BlockAllocator.h"
14 #include "BPlusTree.h"
15 #include "Inode.h"
16 #include "Volume.h"
17 
18 
19 struct check_index {
20 	check_index()
21 		:
22 		inode(NULL)
23 	{
24 	}
25 
26 	char				name[B_FILE_NAME_LENGTH];
27 	block_run			run;
28 	Inode*				inode;
29 };
30 
31 
32 CheckVisitor::CheckVisitor(Volume* volume)
33 	:
34 	FileSystemVisitor(volume),
35 	fCheckBitmap(NULL)
36 {
37 }
38 
39 
40 CheckVisitor::~CheckVisitor()
41 {
42 	free(fCheckBitmap);
43 }
44 
45 
46 status_t
47 CheckVisitor::StartBitmapPass()
48 {
49 	if (!_ControlValid())
50 		return B_BAD_VALUE;
51 
52 	// Lock the volume's journal and block allocator
53 	GetVolume()->GetJournal(0)->Lock(NULL, true);
54 	recursive_lock_lock(&GetVolume()->Allocator().Lock());
55 
56 	size_t size = _BitmapSize();
57 	fCheckBitmap = (uint32*)malloc(size);
58 	if (fCheckBitmap == NULL) {
59 		recursive_lock_unlock(&GetVolume()->Allocator().Lock());
60 		GetVolume()->GetJournal(0)->Unlock(NULL, true);
61 		return B_NO_MEMORY;
62 	}
63 
64 	memset(&Control().stats, 0, sizeof(check_control::stats));
65 
66 	// initialize bitmap
67 	memset(fCheckBitmap, 0, size);
68 	for (int32 block = GetVolume()->Log().Start() + GetVolume()->Log().Length();
69 			block-- > 0;) {
70 		_SetCheckBitmapAt(block);
71 	}
72 
73 	Control().pass = BFS_CHECK_PASS_BITMAP;
74 	Control().stats.block_size = GetVolume()->BlockSize();
75 
76 	// TODO: check reserved area in bitmap!
77 
78 	Start(VISIT_REGULAR | VISIT_INDICES | VISIT_REMOVED
79 		| VISIT_ATTRIBUTE_DIRECTORIES);
80 
81 	return B_OK;
82 }
83 
84 
85 status_t
86 CheckVisitor::WriteBackCheckBitmap()
87 {
88 	if (GetVolume()->IsReadOnly())
89 		return B_OK;
90 
91 	// calculate the number of used blocks in the check bitmap
92 	size_t size = _BitmapSize();
93 	off_t usedBlocks = 0LL;
94 
95 	// TODO: update the allocation groups used blocks info
96 	for (uint32 i = size >> 2; i-- > 0;) {
97 		uint32 compare = 1;
98 		// Count the number of bits set
99 		for (int16 j = 0; j < 32; j++, compare <<= 1) {
100 			if ((compare & fCheckBitmap[i]) != 0)
101 				usedBlocks++;
102 		}
103 	}
104 
105 	Control().stats.freed = GetVolume()->UsedBlocks() - usedBlocks
106 		+ Control().stats.missing;
107 	if (Control().stats.freed < 0)
108 		Control().stats.freed = 0;
109 
110 	// Should we fix errors? Were there any errors we can fix?
111 	if ((Control().flags & BFS_FIX_BITMAP_ERRORS) != 0
112 		&& (Control().stats.freed != 0 || Control().stats.missing != 0)) {
113 		// If so, write the check bitmap back over the original one,
114 		// and use transactions here to play safe - we even use several
115 		// transactions, so that we don't blow the maximum log size
116 		// on large disks, since we don't need to make this atomic.
117 #if 0
118 		// prints the blocks that differ
119 		off_t block = 0;
120 		for (int32 i = 0; i < fNumGroups; i++) {
121 			AllocationBlock cached(fVolume);
122 			for (uint32 j = 0; j < fGroups[i].NumBlocks(); j++) {
123 				cached.SetTo(fGroups[i], j);
124 				for (uint32 k = 0; k < cached.NumBlockBits(); k++) {
125 					if (cached.IsUsed(k) != _CheckBitmapIsUsedAt(block)) {
126 						dprintf("differ block %lld (should be %d)\n", block,
127 							_CheckBitmapIsUsedAt(block));
128 					}
129 					block++;
130 				}
131 			}
132 		}
133 #endif
134 
135 		GetVolume()->SuperBlock().used_blocks
136 			= HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
137 
138 		size_t blockSize = GetVolume()->BlockSize();
139 		off_t numBitmapBlocks = GetVolume()->NumBitmapBlocks();
140 
141 		for (uint32 i = 0; i < numBitmapBlocks; i += 512) {
142 			Transaction transaction(GetVolume(), 1 + i);
143 
144 			uint32 blocksToWrite = 512;
145 			if (blocksToWrite + i > numBitmapBlocks)
146 				blocksToWrite = numBitmapBlocks - i;
147 
148 			status_t status = transaction.WriteBlocks(1 + i,
149 				(uint8*)fCheckBitmap + i * blockSize, blocksToWrite);
150 			if (status < B_OK) {
151 				FATAL(("error writing bitmap: %s\n", strerror(status)));
152 				return status;
153 			}
154 			transaction.Done();
155 		}
156 	}
157 
158 	return B_OK;
159 }
160 
161 
162 status_t
163 CheckVisitor::StartIndexPass()
164 {
165 	// if we don't have indices to rebuild, this pass is done
166 	if (indices.IsEmpty())
167 		return B_ENTRY_NOT_FOUND;
168 
169 	Control().pass = BFS_CHECK_PASS_INDEX;
170 
171 	status_t status = _PrepareIndices();
172 	if (status != B_OK) {
173 		Control().status = status;
174 		return status;
175 	}
176 
177 	Start(VISIT_REGULAR);
178 	return Next();
179 }
180 
181 
182 status_t
183 CheckVisitor::StopChecking()
184 {
185 	if (GetVolume()->IsReadOnly()) {
186 		// We can't fix errors on this volume
187 		Control().flags = 0;
188 	}
189 
190 	if (Control().status != B_ENTRY_NOT_FOUND)
191 		FATAL(("CheckVisitor didn't run through\n"));
192 
193 	_FreeIndices();
194 
195 	recursive_lock_unlock(&GetVolume()->Allocator().Lock());
196 	GetVolume()->GetJournal(0)->Unlock(NULL, true);
197 	return B_OK;
198 }
199 
200 
201 status_t
202 CheckVisitor::VisitDirectoryEntry(Inode* inode, Inode* parent,
203 	const char* treeName)
204 {
205 	Control().inode = inode->ID();
206 	Control().mode = inode->Mode();
207 
208 	if (Pass() != BFS_CHECK_PASS_BITMAP)
209 		return B_OK;
210 
211 	// check if the inode's name is the same as in the b+tree
212 	if (inode->IsRegularNode()) {
213 		RecursiveLocker locker(inode->SmallDataLock());
214 		NodeGetter node(GetVolume(), inode);
215 		if (node.Node() == NULL) {
216 			Control().errors |= BFS_COULD_NOT_OPEN;
217 			Control().status = B_IO_ERROR;
218 			return B_OK;
219 		}
220 
221 		const char* localName = inode->Name(node.Node());
222 		if (localName == NULL || strcmp(localName, treeName)) {
223 			Control().errors |= BFS_NAMES_DONT_MATCH;
224 			FATAL(("Names differ: tree \"%s\", inode \"%s\"\n", treeName,
225 				localName));
226 
227 			if ((Control().flags & BFS_FIX_NAME_MISMATCHES) != 0) {
228 				// Rename the inode
229 				Transaction transaction(GetVolume(), inode->BlockNumber());
230 
231 				// Note, this may need extra blocks, but the inode will
232 				// only be checked afterwards, so that it won't be lost
233 				status_t status = inode->SetName(transaction, treeName);
234 				if (status == B_OK)
235 					status = inode->WriteBack(transaction);
236 				if (status == B_OK)
237 					status = transaction.Done();
238 				if (status != B_OK) {
239 					Control().status = status;
240 					return B_OK;
241 				}
242 			}
243 		}
244 	}
245 
246 	// Check for the correct mode of the node (if the mode of the
247 	// file don't fit to its parent, there is a serious problem)
248 	if (((parent->Mode() & S_ATTR_DIR) != 0
249 			&& !inode->IsAttribute())
250 		|| ((parent->Mode() & S_INDEX_DIR) != 0
251 			&& !inode->IsIndex())
252 		|| (is_directory(parent->Mode())
253 			&& !inode->IsRegularNode())) {
254 		FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent "
255 			"%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(),
256 			inode->Mode(), parent->Mode(), parent->BlockNumber()));
257 
258 		// if we are allowed to fix errors, we should remove the file
259 		if ((Control().flags & BFS_REMOVE_WRONG_TYPES) != 0
260 			&& (Control().flags & BFS_FIX_BITMAP_ERRORS) != 0) {
261 			Control().status = _RemoveInvalidNode(parent, NULL, inode,
262 				treeName);
263 		} else
264 			Control().status = B_ERROR;
265 
266 		Control().errors |= BFS_WRONG_TYPE;
267 	}
268 	return B_OK;
269 }
270 
271 
272 status_t
273 CheckVisitor::VisitInode(Inode* inode, const char* treeName)
274 {
275 	Control().inode = inode->ID();
276 	Control().mode = inode->Mode();
277 		// (we might have set these in VisitDirectoryEntry already)
278 
279 	// set name
280 	if (treeName == NULL) {
281 		if (inode->GetName(Control().name) < B_OK) {
282 			if (inode->IsContainer())
283 				strcpy(Control().name, "(dir has no name)");
284 			else
285 				strcpy(Control().name, "(node has no name)");
286 		}
287 	} else
288 		strcpy(Control().name, treeName);
289 
290 	status_t status = B_OK;
291 
292 	switch (Pass()) {
293 		case BFS_CHECK_PASS_BITMAP:
294 		{
295 			status = _CheckInodeBlocks(inode, NULL);
296 			if (status != B_OK)
297 				return status;
298 
299 			// Check the B+tree as well
300 			if (inode->IsContainer()) {
301 				bool repairErrors = (Control().flags & BFS_FIX_BPLUSTREES) != 0;
302 				bool errorsFound = false;
303 
304 				status = inode->Tree()->Validate(repairErrors, errorsFound);
305 
306 				if (errorsFound) {
307 					Control().errors |= BFS_INVALID_BPLUSTREE;
308 					if (inode->IsIndex() && treeName != NULL && repairErrors) {
309 						// We completely rebuild corrupt indices
310 						check_index* index = new(std::nothrow) check_index;
311 						if (index == NULL)
312 							return B_NO_MEMORY;
313 
314 						strlcpy(index->name, treeName, sizeof(index->name));
315 						index->run = inode->BlockRun();
316 						Indices().Push(index);
317 					}
318 				}
319 			}
320 
321 			break;
322 		}
323 
324 		case BFS_CHECK_PASS_INDEX:
325 			status = _AddInodeToIndex(inode);
326 			break;
327 	}
328 
329 	Control().status = status;
330 	return B_OK;
331 }
332 
333 
334 status_t
335 CheckVisitor::OpenInodeFailed(status_t reason, ino_t id, Inode* parent,
336 		char* treeName, TreeIterator* iterator)
337 {
338 	FATAL(("Could not open inode at %" B_PRIdOFF "\n", id));
339 
340 	if (treeName != NULL)
341 		strlcpy(Control().name, treeName, B_FILE_NAME_LENGTH);
342 	else
343 		strcpy(Control().name, "(node has no name)");
344 
345 	Control().inode = id;
346 	Control().errors = BFS_COULD_NOT_OPEN;
347 
348 	// remove inode from the tree if we can
349 	if (parent != NULL && iterator != NULL
350 		&& (Control().flags & BFS_REMOVE_INVALID) != 0) {
351 		Control().status = _RemoveInvalidNode(parent, iterator->Tree(), NULL,
352 			treeName);
353 	} else
354 		Control().status = B_ERROR;
355 
356 	return B_OK;
357 }
358 
359 
360 status_t
361 CheckVisitor::OpenBPlusTreeFailed(Inode* inode)
362 {
363 	FATAL(("Could not open b+tree from inode at %" B_PRIdOFF "\n",
364 		inode->ID()));
365 	return B_OK;
366 }
367 
368 
369 status_t
370 CheckVisitor::TreeIterationFailed(status_t reason, Inode* parent)
371 {
372 	// Iterating over the B+tree failed - we let the checkfs run
373 	// fail completely, as we would delete all files we cannot
374 	// access.
375 	// TODO: maybe have a force parameter that actually does that.
376 	// TODO: we also need to be able to repair broken B+trees!
377 	return reason;
378 }
379 
380 
381 status_t
382 CheckVisitor::_RemoveInvalidNode(Inode* parent, BPlusTree* tree,
383 	Inode* inode, const char* name)
384 {
385 	// It's safe to start a transaction, because Inode::Remove()
386 	// won't touch the block bitmap (which we hold the lock for)
387 	// if we set the INODE_DONT_FREE_SPACE flag - since we fix
388 	// the bitmap anyway.
389 	Transaction transaction(GetVolume(), parent->BlockNumber());
390 	status_t status;
391 
392 	if (inode != NULL) {
393 		inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DONT_FREE_SPACE);
394 
395 		status = parent->Remove(transaction, name, NULL, false, true);
396 	} else {
397 		parent->WriteLockInTransaction(transaction);
398 
399 		// does the file even exist?
400 		off_t id;
401 		status = tree->Find((uint8*)name, (uint16)strlen(name), &id);
402 		if (status == B_OK)
403 			status = tree->Remove(transaction, name, id);
404 	}
405 
406 	if (status == B_OK) {
407 		entry_cache_remove(GetVolume()->ID(), parent->ID(), name);
408 		transaction.Done();
409 	}
410 
411 	return status;
412 }
413 
414 
415 bool
416 CheckVisitor::_ControlValid()
417 {
418 	if (Control().magic != BFS_IOCTL_CHECK_MAGIC) {
419 		FATAL(("invalid check_control!\n"));
420 		return false;
421 	}
422 
423 	return true;
424 }
425 
426 
427 bool
428 CheckVisitor::_CheckBitmapIsUsedAt(off_t block) const
429 {
430 	size_t size = _BitmapSize();
431 	uint32 index = block / 32;	// 32bit resolution
432 	if (index > size / 4)
433 		return false;
434 
435 	return BFS_ENDIAN_TO_HOST_INT32(fCheckBitmap[index])
436 		& (1UL << (block & 0x1f));
437 }
438 
439 
440 void
441 CheckVisitor::_SetCheckBitmapAt(off_t block)
442 {
443 	size_t size = _BitmapSize();
444 	uint32 index = block / 32;	// 32bit resolution
445 	if (index > size / 4)
446 		return;
447 
448 	fCheckBitmap[index] |= HOST_ENDIAN_TO_BFS_INT32(1UL << (block & 0x1f));
449 }
450 
451 
452 size_t
453 CheckVisitor::_BitmapSize() const
454 {
455 	return GetVolume()->BlockSize() * GetVolume()->NumBitmapBlocks();
456 }
457 
458 
459 status_t
460 CheckVisitor::_CheckInodeBlocks(Inode* inode, const char* name)
461 {
462 	status_t status = _CheckAllocated(inode->BlockRun(), "inode");
463 	if (status != B_OK)
464 		return status;
465 
466 	if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) {
467 		// symlinks may not have a valid data stream
468 		if (strlen(inode->Node().short_symlink) >= SHORT_SYMLINK_NAME_LENGTH)
469 			return B_BAD_DATA;
470 
471 		return B_OK;
472 	}
473 
474 	data_stream* data = &inode->Node().data;
475 
476 	// check the direct range
477 
478 	if (data->max_direct_range) {
479 		for (int32 i = 0; i < NUM_DIRECT_BLOCKS; i++) {
480 			if (data->direct[i].IsZero())
481 				break;
482 
483 			status = _CheckAllocated(data->direct[i], "direct");
484 			if (status < B_OK)
485 				return status;
486 
487 			Control().stats.direct_block_runs++;
488 			Control().stats.blocks_in_direct
489 				+= data->direct[i].Length();
490 		}
491 	}
492 
493 	CachedBlock cached(GetVolume());
494 
495 	// check the indirect range
496 
497 	if (data->max_indirect_range) {
498 		status = _CheckAllocated(data->indirect, "indirect");
499 		if (status < B_OK)
500 			return status;
501 
502 		off_t block = GetVolume()->ToBlock(data->indirect);
503 
504 		for (int32 i = 0; i < data->indirect.Length(); i++) {
505 			block_run* runs = (block_run*)cached.SetTo(block + i);
506 			if (runs == NULL)
507 				RETURN_ERROR(B_IO_ERROR);
508 
509 			int32 runsPerBlock = GetVolume()->BlockSize() / sizeof(block_run);
510 			int32 index = 0;
511 			for (; index < runsPerBlock; index++) {
512 				if (runs[index].IsZero())
513 					break;
514 
515 				status = _CheckAllocated(runs[index], "indirect->run");
516 				if (status < B_OK)
517 					return status;
518 
519 				Control().stats.indirect_block_runs++;
520 				Control().stats.blocks_in_indirect
521 					+= runs[index].Length();
522 			}
523 			Control().stats.indirect_array_blocks++;
524 
525 			if (index < runsPerBlock)
526 				break;
527 		}
528 	}
529 
530 	// check the double indirect range
531 
532 	if (data->max_double_indirect_range) {
533 		status = _CheckAllocated(data->double_indirect, "double indirect");
534 		if (status != B_OK)
535 			return status;
536 
537 		int32 runsPerBlock = runs_per_block(GetVolume()->BlockSize());
538 		int32 runsPerArray = runsPerBlock * data->double_indirect.Length();
539 
540 		CachedBlock cachedDirect(GetVolume());
541 
542 		for (int32 indirectIndex = 0; indirectIndex < runsPerArray;
543 				indirectIndex++) {
544 			// get the indirect array block
545 			block_run* array = (block_run*)cached.SetTo(
546 				GetVolume()->ToBlock(data->double_indirect)
547 					+ indirectIndex / runsPerBlock);
548 			if (array == NULL)
549 				return B_IO_ERROR;
550 
551 			block_run indirect = array[indirectIndex % runsPerBlock];
552 			// are we finished yet?
553 			if (indirect.IsZero())
554 				return B_OK;
555 
556 			status = _CheckAllocated(indirect, "double indirect->runs");
557 			if (status != B_OK)
558 				return status;
559 
560 			int32 maxIndex
561 				= ((uint32)indirect.Length() << GetVolume()->BlockShift())
562 					/ sizeof(block_run);
563 
564 			for (int32 index = 0; index < maxIndex; ) {
565 				block_run* runs = (block_run*)cachedDirect.SetTo(
566 					GetVolume()->ToBlock(indirect) + index / runsPerBlock);
567 				if (runs == NULL)
568 					return B_IO_ERROR;
569 
570 				do {
571 					// are we finished yet?
572 					if (runs[index % runsPerBlock].IsZero())
573 						return B_OK;
574 
575 					status = _CheckAllocated(runs[index % runsPerBlock],
576 						"double indirect->runs->run");
577 					if (status != B_OK)
578 						return status;
579 
580 					Control().stats.double_indirect_block_runs++;
581 					Control().stats.blocks_in_double_indirect
582 						+= runs[index % runsPerBlock].Length();
583 				} while ((++index % runsPerBlock) != 0);
584 			}
585 
586 			Control().stats.double_indirect_array_blocks++;
587 		}
588 	}
589 
590 	return B_OK;
591 }
592 
593 
594 status_t
595 CheckVisitor::_CheckAllocated(block_run run, const char* type)
596 {
597 	BlockAllocator& allocator = GetVolume()->Allocator();
598 
599 	// make sure the block run is valid
600 	if (!allocator.IsValidBlockRun(run)) {
601 		Control().errors |= BFS_INVALID_BLOCK_RUN;
602 		return B_OK;
603 	}
604 
605 	status_t status;
606 
607 	off_t start = GetVolume()->ToBlock(run);
608 	off_t end = start + run.Length();
609 
610 	// check if the run is allocated in the block bitmap on disk
611 	off_t block = start;
612 
613 	while (block < end) {
614 		off_t firstMissing;
615 		status = allocator.CheckBlocks(block, end - block, true, &firstMissing);
616 		if (status == B_OK)
617 			break;
618 		else if (status != B_BAD_DATA)
619 			return status;
620 
621 		off_t afterLastMissing;
622 		status = allocator.CheckBlocks(firstMissing, end - firstMissing, false,
623 			&afterLastMissing);
624 		if (status == B_OK)
625 			afterLastMissing = end;
626 		else if (status != B_BAD_DATA)
627 			return status;
628 
629 		PRINT(("%s: block_run(%ld, %u, %u): blocks %Ld - %Ld are "
630 			"not allocated!\n", type, run.AllocationGroup(), run.Start(),
631 			run.Length(), firstMissing, afterLastMissing - 1));
632 
633 		Control().stats.missing += afterLastMissing - firstMissing;
634 
635 		block = afterLastMissing;
636 	}
637 
638 	// set bits in check bitmap, while checking if they're already set
639 	off_t firstSet = -1;
640 
641 	for (block = start; block < end; block++) {
642 		if (_CheckBitmapIsUsedAt(block)) {
643 			if (firstSet == -1) {
644 				firstSet = block;
645 				Control().errors |= BFS_BLOCKS_ALREADY_SET;
646 			}
647 			Control().stats.already_set++;
648 		} else {
649 			if (firstSet != -1) {
650 				FATAL(("%s: block_run(%d, %u, %u): blocks %" B_PRIdOFF
651 					" - %" B_PRIdOFF " are already set!\n", type,
652 					(int)run.AllocationGroup(), run.Start(), run.Length(),
653 					firstSet, block - 1));
654 				firstSet = -1;
655 			}
656 			_SetCheckBitmapAt(block);
657 		}
658 	}
659 
660 	return B_OK;
661 }
662 
663 
664 status_t
665 CheckVisitor::_PrepareIndices()
666 {
667 	int32 count = 0;
668 
669 	for (int32 i = 0; i < Indices().CountItems(); i++) {
670 		check_index* index = Indices().Array()[i];
671 		Vnode vnode(GetVolume(), index->run);
672 		Inode* inode;
673 		status_t status = vnode.Get(&inode);
674 		if (status != B_OK) {
675 			FATAL(("check: Could not open index at %" B_PRIdOFF "\n",
676 				GetVolume()->ToBlock(index->run)));
677 			return status;
678 		}
679 
680 		BPlusTree* tree = inode->Tree();
681 		if (tree == NULL) {
682 			// TODO: We can't yet repair those
683 			continue;
684 		}
685 
686 		status = tree->MakeEmpty();
687 		if (status != B_OK)
688 			return status;
689 
690 		index->inode = inode;
691 		vnode.Keep();
692 		count++;
693 	}
694 
695 	return count == 0 ? B_ENTRY_NOT_FOUND : B_OK;
696 }
697 
698 
699 void
700 CheckVisitor::_FreeIndices()
701 {
702 	for (int32 i = 0; i < Indices().CountItems(); i++) {
703 		check_index* index = Indices().Array()[i];
704 		if (index->inode != NULL) {
705 			put_vnode(GetVolume()->FSVolume(),
706 				GetVolume()->ToVnode(index->inode->BlockRun()));
707 		}
708 		delete index;
709 	}
710 	Indices().MakeEmpty();
711 }
712 
713 
714 status_t
715 CheckVisitor::_AddInodeToIndex(Inode* inode)
716 {
717 	Transaction transaction(GetVolume(), inode->BlockNumber());
718 
719 	for (int32 i = 0; i < Indices().CountItems(); i++) {
720 		check_index* index = Indices().Array()[i];
721 		if (index->inode == NULL)
722 			continue;
723 
724 		index->inode->WriteLockInTransaction(transaction);
725 
726 		BPlusTree* tree = index->inode->Tree();
727 		if (tree == NULL)
728 			return B_ERROR;
729 
730 		status_t status = B_OK;
731 
732 		if (!strcmp(index->name, "name")) {
733 			if (inode->InNameIndex()) {
734 				char name[B_FILE_NAME_LENGTH];
735 				if (inode->GetName(name, B_FILE_NAME_LENGTH) != B_OK)
736 					return B_ERROR;
737 
738 				status = tree->Insert(transaction, name, inode->ID());
739 			}
740 		} else if (!strcmp(index->name, "last_modified")) {
741 			if (inode->InLastModifiedIndex()) {
742 				status = tree->Insert(transaction, inode->OldLastModified(),
743 					inode->ID());
744 			}
745 		} else if (!strcmp(index->name, "size")) {
746 			if (inode->InSizeIndex())
747 				status = tree->Insert(transaction, inode->Size(), inode->ID());
748 		} else {
749 			uint8 key[MAX_INDEX_KEY_LENGTH];
750 			size_t keyLength = sizeof(key);
751 			if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, key,
752 					&keyLength) == B_OK) {
753 				status = tree->Insert(transaction, key, keyLength, inode->ID());
754 			}
755 		}
756 
757 		if (status != B_OK)
758 			return status;
759 	}
760 
761 	return transaction.Done();
762 }
763