xref: /haiku/src/bin/bfs_tools/recover.cpp (revision 6d2f2ec177bf615a117a7428d71be4330545b320)
1 /*
2  * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
3  */
4 
5 //!	recovers corrupt BFS disks
6 
7 
8 #include <set>
9 
10 #include "Disk.h"
11 #include "Inode.h"
12 #include "Hashtable.h"
13 #include "BPlusTree.h"
14 #include "dump.h"
15 
16 #include <String.h>
17 #include <fs_info.h>
18 
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 
25 
26 extern const char *__progname;
27 static const char *kProgramName = __progname;
28 
29 bool gCreateIndices = false;
30 bool gDumpMissingInodes = false;
31 bool gRawMode = false;
32 bool gVerbose = false;
33 
34 
35 // TODO: add a cache for all inodes
36 typedef std::set<block_run> RunSet;
37 
38 
39 class InodeHashtable {
40 	public:
41 		InodeHashtable(int capacity)
42 			:
43 			fHashtable(capacity),
44 			fLastChecked(0)
45 		{
46 			fHashtable.SetHashFunction((uint32 (*)(const void *))BlockRunHash);
47 			fHashtable.SetCompareFunction((bool (*)(const void *, const void *))
48 				BlockRunCompare);
49 		}
50 
51 		Inode* Acquire(Inode* inode)
52 		{
53 			if (inode == NULL)
54 				return NULL;
55 
56 			status_t status = inode->AcquireBuffer();
57 			if (status != B_OK) {
58 				fprintf(stderr, "Could not retrieve buffer for inode %"
59 					B_PRIdOFF ": %s\n", inode->Offset(), strerror(status));
60 				return NULL;
61 			}
62 			return inode;
63 		}
64 
65 		void Release(Inode* inode)
66 		{
67 			inode->ReleaseBuffer();
68 		}
69 
70 		Inode* Get(block_run run)
71 		{
72 			return Acquire((Inode *)fHashtable.GetValue(&run));
73 		}
74 
75 		bool Insert(Inode* inode)
76 		{
77 			bool success = fHashtable.Put(&inode->BlockRun(), inode);
78 			if (success)
79 				inode->ReleaseBuffer();
80 
81 			return success;
82 		}
83 
84 		bool Contains(block_run *key)
85 		{
86 			return fHashtable.ContainsKey(key);
87 		}
88 
89 		Inode* Remove(block_run *key)
90 		{
91 			return Acquire((Inode*)fHashtable.Remove(key));
92 		}
93 
94 		status_t GetNextEntry(Inode **_inode)
95 		{
96 			status_t status = fHashtable.GetNextEntry((void**)_inode);
97 			if (status == B_OK) {
98 				if (Acquire(*_inode) == NULL)
99 					return B_NO_MEMORY;
100 			}
101 
102 			return status;
103 		}
104 
105 		void Rewind()
106 		{
107 			fHashtable.Rewind();
108 		}
109 
110 		bool IsEmpty() const
111 		{
112 			return fHashtable.IsEmpty();
113 		}
114 
115 		void MakeEmpty()
116 		{
117 			fHashtable.MakeEmpty(HASH_EMPTY_NONE, HASH_EMPTY_DELETE);
118 		}
119 
120 		static uint32 BlockRunHash(const block_run *run)
121 		{
122 			return (run->allocation_group << 16) | run->start;
123 		}
124 
125 		static bool BlockRunCompare(const block_run *runA, const block_run *runB)
126 		{
127 			return *runA == *runB;
128 		}
129 
130 	private:
131 		Hashtable	fHashtable;
132 		bigtime_t	fLastChecked;
133 		uint32		fPercentUsed;
134 };
135 
136 
137 class InodeGetter {
138 	public:
139 		InodeGetter(Disk& disk, block_run run)
140 		{
141 			fInode = Inode::Factory(&disk, run);
142 			if (fInode != NULL)
143 				fInode->AcquireBuffer();
144 		}
145 
146 		~InodeGetter()
147 		{
148 			if (fInode != NULL)
149 				fInode->ReleaseBuffer();
150 		}
151 
152 		Inode* Node() const
153 		{
154 			return fInode;
155 		}
156 
157 		void Detach()
158 		{
159 			fInode = NULL;
160 		}
161 
162 	private:
163 		Inode*	fInode;
164 };
165 
166 
167 RunSet gMainInodes;
168 	// contains all inodes found on disk in the general data area
169 InodeHashtable gLogged(50);
170 	// contains all inodes found in the log area
171 InodeHashtable gMissing(50);
172 InodeHashtable gMissingEmpty(25);
173 
174 
175 class HashtableInodeSource : public Inode::Source {
176 public:
177 	HashtableInodeSource(Disk& disk)
178 		:
179 		fDisk(disk)
180 	{
181 	}
182 
183 	virtual Inode *InodeAt(block_run run)
184 	{
185 		Inode *inode;
186 		if ((inode = gLogged.Get(run)) != NULL)
187 			return inode;
188 
189 		if ((inode = gMissing.Get(run)) != NULL)
190 			return inode;
191 
192 		if (gMainInodes.find(run) == gMainInodes.end())
193 			return NULL;
194 
195 		return Inode::Factory(&fDisk, run);
196 	}
197 
198 private:
199 	Disk&	fDisk;
200 };
201 
202 
203 bool
204 operator<(const block_run& a, const block_run& b)
205 {
206 	return a.allocation_group < b.allocation_group
207 		|| (a.allocation_group == b.allocation_group && a.start < b.start);
208 }
209 
210 
211 void
212 collectInodes(Disk& disk, RunSet* set, InodeHashtable* hashTable, off_t start,
213 	off_t end)
214 {
215 	char buffer[8192];
216 	Inode inode(&disk, (bfs_inode *)buffer, false);
217 
218 	off_t directories = 0LL;
219 	off_t directorySize = 0LL;
220 	off_t files = 0LL;
221 	off_t fileSize = 0LL;
222 	off_t symlinks = 0LL;
223 	off_t count = 0LL;
224 
225 	off_t position = start;
226 	bigtime_t lastUpdate = system_time();
227 
228 	for (off_t offset = start; offset < end; offset += sizeof(buffer)) {
229 		if (disk.ReadAt(offset, buffer, sizeof(buffer)) < B_OK) {
230 			fprintf(stderr, "could not read from device!\n");
231 			break;
232 		}
233 
234 		//if ((offset % (disk.BlockSize() << disk.SuperBlock()->ag_shift)) == 0)
235 		//	printf("reading block %Ld, allocation group %Ld, %Ld inodes...\33[1A\n", offset / disk.BlockSize(),offset / (disk.BlockSize() << disk.SuperBlock()->ag_shift), count);
236 
237 		for (uint32 i = 0; i < sizeof(buffer); i += disk.BlockSize()) {
238 			inode.SetTo((bfs_inode *)(buffer + i));
239 			if (inode.InitCheck() == B_OK) {
240 				if (inode.Flags() & INODE_DELETED)
241 					continue;
242 
243 				Inode *node = Inode::Factory(&disk, &inode);
244 				if (node != NULL) {
245 					if (gVerbose)
246 						printf("  node: %" B_PRIdOFF " \"%s\"\n", position,
247 							node->Name());
248 
249 					if (set != NULL)
250 						set->insert(node->BlockRun());
251 					else
252 						hashTable->Insert(node);
253 
254 					if (node->IsDirectory()) {
255 						directories++;
256 						directorySize += node->Size();
257 					} else if (node->IsFile()) {
258 						files++;
259 						fileSize += node->Size();
260 					} else if (node->IsSymlink()) {
261 						symlinks++;
262 					}
263 					count++;
264 				} else if (gVerbose) {
265 					printf("\nunrecognized inode:");
266 					dump_inode(&inode, inode.InodeBuffer());
267 				}
268 			}
269 			position += disk.BlockSize();
270 		}
271 		if (system_time() - lastUpdate > 500000) {
272 			printf("  block %" B_PRIdOFF " (%" B_PRIdOFF "%%), %" B_PRIdOFF
273 				" inodes\33[1A\n", offset,
274 				100 * (offset - start) / (end - start), count);
275 			lastUpdate = system_time();
276 		}
277 	}
278 	printf("\n%" B_PRIdOFF " inodes found.\n", count);
279 
280 	printf("\n%20" B_PRIdOFF " directories found (total of %" B_PRIdOFF
281 		" bytes)\n%20" B_PRIdOFF " files found (total of %" B_PRIdOFF
282 		" bytes)\n%20" B_PRIdOFF " symlinks found\n"
283 		"--------------------\n"
284 		"%20" B_PRIdOFF " inodes total found.\n",
285 		directories, directorySize, files, fileSize, symlinks, count);
286 }
287 
288 
289 void
290 collectLogInodes(Disk &disk)
291 {
292 	// scan log area
293 	off_t offset = disk.ToOffset(disk.Log());
294 	off_t end = offset + (disk.Log().length << disk.BlockShift());
295 
296 	printf("\nsearching from %" B_PRIdOFF " to %" B_PRIdOFF " (log area)\n",
297 		offset, end);
298 
299 	collectInodes(disk, NULL, &gLogged, offset, end);
300 }
301 
302 
303 void
304 collectRealInodes(Disk &disk)
305 {
306 	// first block after bootblock, bitmap, and log
307 	off_t offset = disk.ToOffset(disk.Log()) + (disk.Log().length
308 		<< disk.BlockShift());
309 	off_t end = (off_t)disk.NumBlocks() << disk.BlockShift();
310 
311 	printf("\nsearching from %" B_PRIdOFF " to %" B_PRIdOFF " (main area)\n",
312 		offset, end);
313 
314 	collectInodes(disk, &gMainInodes, NULL, offset, end);
315 }
316 
317 
318 Directory *
319 getNameIndex(Disk &disk)
320 {
321 	InodeGetter getter(disk, disk.Indices());
322 	Directory *indices = dynamic_cast<Directory *>(getter.Node());
323 
324 	block_run run;
325 	if (indices != NULL && indices->FindEntry("name", &run) == B_OK) {
326 		InodeGetter getter(disk, run);
327 		Inode* node = getter.Node();
328 		getter.Detach();
329 		return dynamic_cast<Directory *>(node);
330 	}
331 
332 	// search name index
333 
334 	RunSet::iterator iterator = gMainInodes.begin();
335 	for (; iterator != gMainInodes.end(); iterator++) {
336 		InodeGetter getter(disk, *iterator);
337 		Inode* node = getter.Node();
338 
339 		if (!node || !node->IsIndex() || node->Name() == NULL)
340 			continue;
341 		if (!strcmp(node->Name(), "name") && node->Mode() & S_STR_INDEX)
342 			return dynamic_cast<Directory *>(node);
343 	}
344 
345 	return NULL;
346 }
347 
348 
349 void
350 checkDirectoryContents(Disk& disk, Directory *dir)
351 {
352 	dir->Rewind();
353 
354 	char name[B_FILE_NAME_LENGTH];
355 	block_run run;
356 
357 	while (dir->GetNextEntry(name, &run) == B_OK) {
358 		if (run == dir->BlockRun() || run == dir->Parent()
359 			|| gMainInodes.find(run) != gMainInodes.end())
360 			continue;
361 
362 		Inode *missing = gMissing.Get(run);
363 		if (missing != NULL) {
364 			if (missing->SetName(name) < B_OK) {
365 				fprintf(stderr, "setting name of missing node to "
366 					"\"%s\" failed!", name);
367 			}
368 			if (gVerbose) {
369 				printf("Set name of missing node (%" B_PRId32
370 					", %d) to \"%s\" (%s)\n",
371 					run.allocation_group, run.start, missing->Name(), name);
372 			}
373 
374 			missing->SetParent(dir->BlockRun());
375 		}
376 //		if (node->Mode() & S_INDEX_DIR)
377 //		{
378 //			if (node->Mode() & S_STR_INDEX)
379 //				printf("index directory (%ld, %d): \"%s\" is missing (%ld, %d, %d)\n",node->BlockRun().allocation_group,node->BlockRun().start,name,run.allocation_group,run.start,run.length);
380 //			else
381 //				printf("index directory (%ld, %d): key is missing (%ld, %d, %d)\n",node->BlockRun().allocation_group,node->BlockRun().start,run.allocation_group,run.start,run.length);
382 //		}
383 		else {
384 			// missing inode has not been found
385 			if (gVerbose) {
386 				printf("directory \"%s\" (%" B_PRId32 ", %d): node \"%s\" is "
387 					"missing (%" B_PRId32 ", %d, %d)\n", dir->Name(),
388 					dir->BlockRun().allocation_group,
389 					dir->BlockRun().start, name,
390 					run.allocation_group, run.start, run.length);
391 			}
392 
393 			if ((missing = (Inode *)gLogged.Remove(&run)) != NULL) {
394 				// missing inode is in the log
395 				if (gVerbose)
396 					printf("found missing entry in log!\n");
397 				if (missing->InodeBuffer()->parent != dir->BlockRun()) {
398 					if (gVerbose)
399 						puts("\tparent directories differ (may be an old "
400 							"version of it), reseting parent.");
401 					missing->SetParent(dir->BlockRun());
402 				}
403 				if (!gMissing.Insert(missing))
404 					delete missing;
405 			} else if (!gMissingEmpty.Contains(&run)) {
406 				// not known at all yet
407 				Inode *empty = Inode::EmptyInode(&disk, name, 0);
408 				if (empty) {
409 					empty->SetBlockRun(run);
410 					empty->SetParent(dir->BlockRun());
411 					if (gVerbose)
412 						printf("\tname = %s\n", empty->Name());
413 
414 					if (!gMissingEmpty.Insert(empty))
415 						delete empty;
416 				}
417 			}
418 		}
419 	}
420 }
421 
422 
423 void
424 checkStructure(Disk &disk)
425 {
426 	off_t count = 0;
427 	Inode* node;
428 
429 	RunSet::iterator iterator = gMainInodes.begin();
430 	for (; iterator != gMainInodes.end(); iterator++) {
431 		InodeGetter getter(disk, *iterator);
432 		node = getter.Node();
433 
434 		count++;
435 		if ((count % 50) == 0)
436 			fprintf(stderr, "%" B_PRIdOFF " inodes processed...\33[1A\n", count);
437 
438 		if (node == NULL)
439 			continue;
440 
441 		if (node->IsDirectory() && !node->IsIndex()) {
442 			// check if all entries are in the hashtable
443 			Directory* directory = dynamic_cast<Directory*>(node);
444 			if (directory != NULL)
445 				checkDirectoryContents(disk, directory);
446 			else {
447 				printf("Node \"%s\" at %" B_PRId32
448 					",%d looks like a directory, but isn't.\n",
449 					node->Name(), node->BlockRun().allocation_group,
450 					node->BlockRun().start);
451 			}
452 		}
453 
454 		// check for the parent directory
455 
456 		block_run run = node->Parent();
457 		InodeGetter parentGetter(disk, run);
458 		Inode *parentNode = parentGetter.Node();
459 
460 		Directory *dir = dynamic_cast<Directory *>(parentNode);
461 		if (dir || (parentNode && (node->Mode() & S_ATTR_DIR))) {
462 			// entry has a valid parent directory, so it's assumed to be a valid entry
463 			disk.BlockBitmap()->BackupSet(node, true);
464 		} else if (node->Mode() & S_ATTR) {
465 			if (gVerbose) {
466 				printf("attribute \"%s\" at %" B_PRId32 ",%d misses its parent.\n",
467 					node->Name(), node->BlockRun().allocation_group,
468 					node->BlockRun().start);
469 				puts("\thandling not yet implemented...");
470 			}
471 		} else /*if ((node->Flags() & INODE_DELETED) == 0)*/ {
472 			Inode* missing = gMissing.Get(run);
473 			dir = dynamic_cast<Directory *>(missing);
474 
475 			if (missing == NULL) {
476 				if (gVerbose) {
477 					printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32
478 						"): parent directory is missing (%" B_PRId32
479 						", %d, %d), may be deleted!\n",
480 						node->IsFile() ? "file" : "node", node->Name(),
481 						node->BlockRun().allocation_group,
482 						node->BlockRun().start,
483 						node->Mode(), run.allocation_group, run.start,
484 						run.length);
485 				}
486 
487 				if ((dir = dynamic_cast<Directory *>((Inode *)gLogged.Remove(
488 						&run))) != NULL) {
489 					if (gVerbose) {
490 						printf("found directory \"%s\" in log:\n", dir->Name());
491 						if (dir->Size() > 0)
492 							dump_inode(dir, dir->InodeBuffer());
493 						else
494 							puts("\tempty inode.");
495 					}
496 				} else {
497 					if (gVerbose)
498 						puts("\tcreate parent missing entry");
499 
500 					Inode *nameNode = (Inode *)gMissingEmpty.Remove(&run);
501 					if (nameNode != NULL) {
502 						nameNode->SetMode(S_IFDIR);
503 						if (gVerbose)
504 							printf("found missing name!\n");
505 					} else {
506 						BString parentName;
507 						parentName << "__directory " << run.allocation_group
508 							<< ":" << (int32)run.start;
509 
510 						nameNode = Inode::EmptyInode(&disk, parentName.String(),
511 							S_IFDIR);
512 						if (nameNode) {
513 							nameNode->SetBlockRun(run);
514 							nameNode->SetParent(disk.Root());
515 						}
516 					}
517 
518 					if (nameNode) {
519 						dir = new Directory(*nameNode);
520 						if (dir->CopyBuffer() < B_OK)
521 							puts("could not copy buffer!");
522 						else
523 							delete nameNode;
524 					}
525 				}
526 				if (dir) {
527 					dir->AcquireBuffer();
528 
529 					if (!gMissing.Insert(dir)) {
530 						printf("could not add dir!!\n");
531 						delete dir;
532 						dir = NULL;
533 					}
534 				}
535 			} else if (missing != NULL && dir == NULL && gVerbose) {
536 				printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32
537 					"): parent directory found in missing list (%" B_PRId32
538 					", %d, %d), but it's not a dir!\n",
539 					node->IsFile() ? "file" : "node", node->Name(),
540 					node->BlockRun().allocation_group, node->BlockRun().start,
541 					node->Mode(), run.allocation_group, run.start, run.length);
542 			} else if (gVerbose) {
543 				printf("%s \"%s\" (%" B_PRId32 ", %d, mode = %10" B_PRIo32
544 					"): parent directory found in missing list (%" B_PRId32
545 					", %d, %d)!\n",
546 					node->IsFile() ? "file" : "node", node->Name(),
547 					node->BlockRun().allocation_group, node->BlockRun().start,
548 					node->Mode(), run.allocation_group, run.start, run.length);
549 			}
550 
551 			if (dir) {
552 				dir->AddEntry(node);
553 				dir->ReleaseBuffer();
554 			}
555 		}
556 //			else
557 //			{
558 //				printf("node %s\n", node->Name());
559 //				status_t status = dir->Contains(node);
560 //				if (status == B_ENTRY_NOT_FOUND)
561 //					printf("node \"%s\": parent directory \"%s\" contains no link to this node!\n",node->Name(),dir->Name());
562 //				else if (status != B_OK)
563 //					printf("node \"%s\": parent directory \"%s\" error: %s\n",node->Name(),dir->Name(),strerror(status));
564 //			}
565 
566 		// check for attributes
567 
568 		run = node->Attributes();
569 		if (!run.IsZero()) {
570 			//printf("node \"%s\" (%ld, %d, mode = %010lo): has attribute dir!\n",node->Name(),node->BlockRun().allocation_group,node->BlockRun().start,node->Mode());
571 
572 			if (gMainInodes.find(run) == gMainInodes.end()) {
573 				if (gVerbose) {
574 					printf("node \"%s\": attributes are missing (%" B_PRId32
575 						", %d, %d)\n", node->Name(), run.allocation_group,
576 						run.start, run.length);
577 				}
578 
579 				if ((dir = (Directory *)gMissing.Get(run)) != NULL) {
580 					if (gVerbose)
581 						puts("\tfound in missing");
582 					dir->SetMode(dir->Mode() | S_ATTR_DIR);
583 					dir->SetParent(node->BlockRun());
584 				} else {
585 					if (gVerbose)
586 						puts("\tcreate new!");
587 
588 					Inode *empty = Inode::EmptyInode(&disk, NULL,
589 						S_IFDIR | S_ATTR_DIR);
590 					if (empty) {
591 						empty->SetBlockRun(run);
592 						empty->SetParent(node->BlockRun());
593 
594 						dir = new Directory(*empty);
595 						if (dir->CopyBuffer() < B_OK)
596 							puts("could not copy buffer!");
597 						else
598 							delete empty;
599 
600 						if (!gMissing.Insert(dir)) {
601 							puts("\tcould not add attribute dir");
602 							delete dir;
603 						}
604 					}
605 				}
606 			}
607 		}
608 	}
609 	printf("%" B_PRIdOFF " inodes processed.\n", count);
610 
611 	Directory *directory = getNameIndex(disk);
612 	if (directory != NULL) {
613 		puts("\n*** Search names of missing inodes in the name index");
614 
615 		BPlusTree *tree;
616 		if (directory->GetTree(&tree) == B_OK && tree->Validate(gVerbose) == B_OK) {
617 			char name[B_FILE_NAME_LENGTH];
618 			block_run run;
619 			directory->Rewind();
620 			while (directory->GetNextEntry(name, &run) >= B_OK) {
621 				if ((node = gMissing.Get(run)) == NULL)
622 					continue;
623 
624 				if (gVerbose) {
625 					printf("  Node found: %" B_PRId32 ":%d\n",
626 						run.allocation_group, run.start);
627 				}
628 				if (node->Name() == NULL || strcmp(node->Name(), name)) {
629 					if (gVerbose) {
630 						printf("\tnames differ: %s -> %s (indices)\n",
631 							node->Name(), name);
632 					}
633 					node->SetName(name);
634 				}
635 			}
636 		} else
637 			printf("\tname index is corrupt!\n");
638 
639 		directory->ReleaseBuffer();
640 	} else
641 		printf("*** Name index corrupt or not existent!\n");
642 
643 	if (!gVerbose)
644 		return;
645 
646 	if (!gMissing.IsEmpty())
647 		puts("\n*** Missing inodes:");
648 
649 	gMissing.Rewind();
650 	while (gMissing.GetNextEntry(&node) == B_OK) {
651 		if (gDumpMissingInodes)
652 			dump_inode(node, node->InodeBuffer());
653 
654 		Directory *dir = dynamic_cast<Directory *>(node);
655 		if (dir) {
656 			printf("\ndirectory \"%s\" (%" B_PRId32 ", %d) contents:\n",
657 				node->Name(), node->BlockRun().allocation_group,
658 				node->BlockRun().start);
659 
660 			dir->Rewind();
661 
662 			char name[B_FILE_NAME_LENGTH];
663 			block_run run;
664 			while (dir->GetNextEntry(name, &run) == B_OK) {
665 				printf("\t\"%s\" (%" B_PRId32 ", %d, %d)\n", name,
666 					run.allocation_group, run.start, run.length);
667 			}
668 
669 			BPlusTree *tree;
670 			if (dir->GetTree(&tree) < B_OK)
671 				continue;
672 
673 			uint16 length;
674 			off_t offset;
675 
676 			while (tree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH,
677 					&offset) == B_OK) {
678 				name[length] = 0;
679 
680 				run = disk.ToBlockRun(offset);
681 				printf("%s: block_run == (%5" B_PRId32 ",%5d,%5d), \"%s\"\n",
682 					dir->Name(), run.allocation_group, run.start, run.length,
683 					name);
684 			}
685 
686 			//tree->WriteTo(dir);
687 			//disk.WriteAt(dir->Offset(),dir->InodeBuffer(),disk.BlockSize());
688 		}
689 
690 		gMissing.Release(node);
691 	}
692 }
693 
694 
695 void
696 copyInodes(Disk& disk, const char* copyTo)
697 {
698 	if (copyTo == NULL)
699 		return;
700 
701 	HashtableInodeSource source(disk);
702 	Inode *node;
703 
704 	int32 count = 0;
705 
706 	RunSet::iterator iterator = gMainInodes.begin();
707 	for (; iterator != gMainInodes.end(); iterator++) {
708 		InodeGetter getter(disk, *iterator);
709 		Inode* node = getter.Node();
710 
711 		if (node && !node->IsIndex() && !node->IsAttributeDirectory())
712 			node->CopyTo(copyTo, true, &source);
713 
714 		if ((++count % 500) == 0)
715 			fprintf(stderr, "copied %" B_PRId32 " files...\n", count);
716 	}
717 
718 	gMissing.Rewind();
719 	while (gMissing.GetNextEntry(&node) == B_OK) {
720 		if (!node->IsIndex() && !node->IsAttributeDirectory())
721 			node->CopyTo(copyTo, true, &source);
722 
723 		gMissing.Release(node);
724 	}
725 }
726 
727 
728 void
729 usage(char *name)
730 {
731 	fprintf(stderr,"usage: %s [-idv] [-r [start-offset] [end-offset]] <device> [recover-to-path]\n"
732 		"\t-i\trecreate indices on target disk\n"
733 		"\t-d\tdump missing and recreated i-nodes\n"
734 		"\t-r\tdisk access in raw mode (use only if the partition table is invalid)\n"
735 		"\t-s\trecreate superblock and exit (for experts only, don't use this\n"
736 		"\t\tif you don't know what you're doing)\n"
737 		"\t-v\tverbose output\n", name);
738 	exit(-1);
739 }
740 
741 
742 int
743 main(int argc, char **argv)
744 {
745 	char *fileName = strrchr(argv[0], '/');
746 	fileName = fileName ? fileName + 1 : argv[0];
747 	bool recreateSuperBlockOnly = false;
748 
749 	off_t startOffset = 0, endOffset = -1;
750 
751 	puts("Copyright (c) 2001-2008 pinc Software.");
752 
753 	if (argc < 2 || !strcmp(argv[1], "--help"))
754 		usage(fileName);
755 
756 	while (*++argv) {
757 		char *arg = *argv;
758 		if (*arg == '-') {
759 			while (*++arg && isalpha(*arg)) {
760 				switch (arg[0]) {
761 					case 'r':
762 					{
763 						gRawMode = true;
764 
765 						if (argv[1] && isdigit((argv[1])[0])) {
766 							argv++;
767 							arg = *argv;
768 
769 							startOffset = atoll(arg);
770 						}
771 						if (argv[1] && isdigit((argv[1])[0])) {
772 							argv++;
773 							arg = *argv;
774 
775 							endOffset = atoll(arg);
776 						}
777 						if (endOffset != -1 && endOffset < startOffset)
778 							usage(fileName);
779 						break;
780 					}
781 					case 'v':
782 						gVerbose = true;
783 						break;
784 					case 'i':
785 						gCreateIndices = true;
786 						break;
787 					case 'd':
788 						gDumpMissingInodes = true;
789 						break;
790 					case 's':
791 						recreateSuperBlockOnly = true;
792 						break;
793 				}
794 			}
795 		} else
796 			break;
797 	}
798 
799 	Disk disk(argv[0], gRawMode, startOffset, endOffset);
800 	if (disk.InitCheck() < B_OK) {
801 		fprintf(stderr,"Could not open device or file: %s\n",
802 			strerror(disk.InitCheck()));
803 		return -1;
804 	}
805 
806 	if (argv[1] != NULL) {
807 		dev_t device = dev_for_path(argv[1]);
808 		fs_info info;
809 		if (fs_stat_dev(device, &info) == B_OK) {
810 			if (!strcmp(info.device_name, disk.Path().Path())) {
811 				fprintf(stderr,"The source and target device are identical, "
812 					"you currently need\n"
813 					"to have another disk to recover to, sorry!\n");
814 				return -1;
815 			}
816 			if ((info.flags & (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR))
817 					!= (B_FS_IS_PERSISTENT | B_FS_HAS_ATTR)) {
818 				fprintf(stderr, "%s: The target file system (%s) does not have "
819 					"the required\n"
820 					"\tfunctionality in order to restore all information.\n",
821 					kProgramName, info.fsh_name);
822 				return -1;
823 			}
824 		}
825 	}
826 
827 	bool recreatedSuperBlock = false;
828 
829 	if (disk.ValidateSuperBlock() < B_OK) {
830 		fprintf(stderr, "The disk's superblock is corrupt!\n");
831 		if (disk.RecreateSuperBlock() < B_OK) {
832 			fprintf(stderr, "Can't recreate the disk's superblock, sorry!\n");
833 			return -1;
834 		}
835 		recreatedSuperBlock = true;
836 	}
837 	if (gVerbose) {
838 		puts("\n*** The superblock:\n");
839 		dump_super_block(disk.SuperBlock());
840 	}
841 
842 	if (recreateSuperBlockOnly) {
843 		if (!recreatedSuperBlock) {
844 			printf("Superblock was valid, no changes made.\n");
845 			return 0;
846 		}
847 
848 		status_t status = disk.WriteAt(512, disk.SuperBlock(),
849 			sizeof(disk_super_block));
850 		if (status < B_OK) {
851 			fprintf(stderr, "Could not write superblock: %s!\n",
852 				strerror(status));
853 			return 1;
854 		}
855 		return 0;
856 	}
857 
858 	puts("\n*** Collecting inodes...");
859 
860 	collectLogInodes(disk);
861 	collectRealInodes(disk);
862 
863 	puts("\n*** Checking Disk Structure Integrity...");
864 
865 	checkStructure(disk);
866 
867 	if (argv[1])
868 		copyInodes(disk, argv[1]);
869 
870 	//disk.WriteBootBlock();
871 	//disk.BlockBitmap()->CompareWithBackup();
872 
873 	gMissing.MakeEmpty();
874 	gLogged.MakeEmpty();
875 
876 	return 0;
877 }
878 
879