2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
5 //! sanity and completeness check for indices
10 #include "BPlusTree.h"
23 char gEscape
[3] = {0x1b, '[', 0};
25 bool gDoNotCheckForFiles
= false;
26 bool gDoNotCheckIndex
= false;
27 bool gCheckAll
= false;
30 // we just want to know which inodes exist, so don't remember anything
31 // along the position of the inode.
33 class BlockRunHashtable
: public Hashtable
36 BlockRunHashtable(int capacity
)
39 SetHashFunction((uint32 (*)(const void *))BlockRunHash
);
40 SetCompareFunction((bool (*)(const void *, const void *))BlockRunCompare
);
43 bool Contains(block_run
*run
)
45 return ContainsKey((void *)run
);
48 bool Put(block_run
&run
)
50 block_run
*value
= (block_run
*)malloc(sizeof(block_run
));
54 memcpy(value
,&run
,sizeof(block_run
));
56 if (Hashtable::Put(value
,value
))
63 static uint32
BlockRunHash(const block_run
*run
)
65 return run
->allocation_group
<< 16 | run
->start
;
68 static bool BlockRunCompare(const block_run
*runA
, const block_run
*runB
)
70 return *runA
== *runB
;
75 BlockRunHashtable
gHashtable(1000);
79 compareBlockRuns(const block_run
*a
, const block_run
*b
)
81 int cmp
= (int)a
->allocation_group
- (int)b
->allocation_group
;
83 cmp
= (int)a
->start
- (int)b
->start
;
89 collectFiles(Disk
&disk
,Directory
*directory
)
91 if (directory
== NULL
)
95 char name
[B_FILE_NAME_LENGTH
];
97 while (directory
->GetNextEntry(name
,&run
) >= B_OK
)
99 if (!strcmp(name
,".") || !strcmp(name
,".."))
104 if (++gCount
% 50 == 0)
105 printf(" %7Ld%s1A\n",gCount
,gEscape
);
107 Inode
*inode
= Inode::Factory(&disk
,run
);
110 if (inode
->IsDirectory())
111 collectFiles(disk
,static_cast<Directory
*>(inode
));
116 printf(" Directory \"%s\" (%ld, %d) points to corrupt inode \"%s\" (%ld, %d)\n",
117 directory
->Name(),directory
->BlockRun().allocation_group
,directory
->BlockRun().start
,
118 name
,run
.allocation_group
,run
.start
);
124 collectFiles(Disk
&disk
)
126 Directory
*root
= (Directory
*)Inode::Factory(&disk
,disk
.Root());
128 puts("Collecting files (this will take some time)...");
130 if (root
== NULL
|| root
->InitCheck() < B_OK
)
132 fprintf(stderr
," Could not open root directory!\n");
135 collectFiles(disk
,root
);
137 printf(" %7Ld files found.\n",gCount
);
144 checkIndexForNonExistingFiles(Disk
&disk
,BPlusTree
&tree
)
146 char name
[B_FILE_NAME_LENGTH
];
150 while (tree
.GetNextEntry(name
,&length
,B_FILE_NAME_LENGTH
,&offset
) == B_OK
)
153 block_run run
= disk
.ToBlockRun(offset
);
154 if (!gHashtable
.Contains(&run
))
156 printf(" inode at (%ld, %d), offset %Ld, doesn't exist!",run
.allocation_group
,run
.start
,offset
);
159 case BPLUSTREE_STRING_TYPE
:
160 printf(" (string = \"%s\")",name
);
162 case BPLUSTREE_INT32_TYPE
:
163 printf(" (int32 = %ld)",*(int32
*)&name
);
165 case BPLUSTREE_UINT32_TYPE
:
166 printf(" (uint32 = %lu)",*(uint32
*)&name
);
168 case BPLUSTREE_INT64_TYPE
:
169 printf(" (int64 = %Ld)",*(int64
*)&name
);
171 case BPLUSTREE_UINT64_TYPE
:
172 printf(" (uint64 = %Lu)",*(uint64
*)&name
);
174 case BPLUSTREE_FLOAT_TYPE
:
175 printf(" (float = %g)",*(float *)&name
);
177 case BPLUSTREE_DOUBLE_TYPE
:
178 printf(" (double = %g)",*(double *)&name
);
188 checkFiles(Disk
&disk
,BPlusTree
&tree
,char *attribute
)
190 block_run
*runs
= (block_run
*)malloc(gCount
* sizeof(block_run
));
193 fprintf(stderr
," Not enough memory!\n");
196 // copy hashtable to array
197 block_run
*run
= NULL
;
200 while (gHashtable
.GetNextEntry((void **)&run
) == B_OK
)
202 runs
[index
++] = *run
;
205 // sort array to speed up disk access
206 qsort(runs
,index
,sizeof(block_run
),(int (*)(const void *,const void *))compareBlockRuns
);
208 bool sizeIndex
= !strcmp(attribute
,"size");
209 bool nameIndex
= !strcmp(attribute
,"name");
210 bool modifiedIndex
= !strcmp(attribute
,"last_modified");
212 char key
[B_FILE_NAME_LENGTH
];
213 uint16 keyLength
= 0;
217 for (int32 i
= 0;i
< index
;i
++)
220 printf(" %7ld%s1A\n",i
,gEscape
);
223 inode
= Inode::Factory(&disk
,runs
[i
]);
224 if (inode
== NULL
|| inode
->InitCheck() < B_OK
)
226 fprintf(stderr
," inode at (%ld, %d) is corrupt!\n",runs
[i
].allocation_group
,runs
[i
].start
);
231 // check indices not based on standard attributes
234 if (inode
->IsDirectory())
237 memcpy(key
,&inode
->InodeBuffer()->data
.size
,sizeof(off_t
));
238 keyLength
= sizeof(off_t
);
242 strcpy(key
,inode
->Name());
243 keyLength
= strlen(key
);
245 else if (modifiedIndex
)
247 if (inode
->IsDirectory())
250 memcpy(key
,&inode
->InodeBuffer()->last_modified_time
,sizeof(off_t
));
251 keyLength
= sizeof(off_t
);
253 else // iterate through all attributes to find the right one (damn slow, sorry...)
255 inode
->RewindAttributes();
256 char name
[B_FILE_NAME_LENGTH
];
261 while (inode
->GetNextAttribute(name
,&type
,&data
,&length
) == B_OK
)
263 if (!strcmp(name
,attribute
))
265 strncpy(key
,(char *)data
,B_FILE_NAME_LENGTH
- 1);
266 key
[B_FILE_NAME_LENGTH
- 1] = '\0';
267 keyLength
= length
> B_FILE_NAME_LENGTH
? B_FILE_NAME_LENGTH
: length
;
277 if (tree
.Find((uint8
*)key
,keyLength
,&value
) < B_OK
)
280 fprintf(stderr
," inode at (%ld, %d) name \"%s\" is not in index!\n",runs
[i
].allocation_group
,runs
[i
].start
,inode
->Name());
283 // inode is obviously deleted!
284 block_run parent
= inode
->Parent();
285 Directory
*directory
= (Directory
*)Inode::Factory(&disk
,parent
);
286 if (directory
!= NULL
&& directory
->InitCheck() == B_OK
)
288 BPlusTree
*parentTree
;
289 if (directory
->GetTree(&parentTree
) == B_OK
)
291 char name
[B_FILE_NAME_LENGTH
];
293 off_t offset
,searchOffset
= disk
.ToBlock(runs
[i
]);
296 while (parentTree
->GetNextEntry(name
,&length
,B_FILE_NAME_LENGTH
,&offset
) == B_OK
)
298 if (offset
== searchOffset
)
300 fprintf(stderr
," inode at (%ld, %d) name \"%s\" was obviously deleted, but the parent \"%s\" still contains it!\n",runs
[i
].allocation_group
,runs
[i
].start
,name
,directory
->Name());
306 fprintf(stderr
," inode at (%ld, %d) was obviously deleted, and the parent \"%s\" obviously doesn't contain it anymore!\n",runs
[i
].allocation_group
,runs
[i
].start
,directory
->Name());
309 fprintf(stderr
," inode at (%ld, %d) was obviously deleted, but the parent \"%s\" is invalid and still contains it!\n",runs
[i
].allocation_group
,runs
[i
].start
,directory
->Name());
313 // not that this would be really possible... - but who knows
314 fprintf(stderr
," inode at (%ld, %d) is not in index and has invalid parent!\n",runs
[i
].allocation_group
,runs
[i
].start
);
321 if (bplustree_node::LinkType(value
) == BPLUSTREE_NODE
)
323 if (disk
.ToBlockRun(value
) != runs
[i
])
324 fprintf(stderr
," offset in index and inode offset doesn't match for inode \"%s\" at (%ld, %d)\n",inode
->Name(),runs
[i
].allocation_group
,runs
[i
].start
);
329 char name
[B_FILE_NAME_LENGTH
];
332 bool found
= false,duplicates
= false;
335 while (tree
.GetNextEntry(name
,&length
,B_FILE_NAME_LENGTH
,&offset
) == B_OK
)
337 //printf("search for = %ld, key = %ld -> value = %Ld (%ld, %d)\n",*(int32 *)&key,*(int32 *)&name,offset,disk.ToBlockRun(offset).allocation_group,disk.ToBlockRun(offset).start);
338 if (keyLength
== length
&& !memcmp(key
,name
,keyLength
))
341 if (disk
.ToBlockRun(offset
) == runs
[i
])
347 //else if (duplicates)
352 printf(" inode \"%s\" at (%ld, %d) not found in duplicates!\n",inode
->Name(),runs
[i
].allocation_group
,runs
[i
].start
);
359 printf(" %7Ld files processed.\n",gCount
);
364 checkIndex(Disk
&disk
,char *attribute
,block_run
&run
,bool collect
)
366 Directory
*index
= (Directory
*)Inode::Factory(&disk
,run
);
368 if (index
== NULL
|| (status
= index
->InitCheck()) < B_OK
)
370 fprintf(stderr
," Could not get index directory for \"%s\": %s!\n",attribute
,index
? strerror(status
) : "not found/corrupted");
374 printf("\nCheck \"%s\" index's on-disk structure...\n",attribute
);
375 //dump_inode(index->InodeBuffer());
378 if (index
->GetTree(&tree
) < B_OK
|| tree
->Validate(true) < B_OK
)
380 fprintf(stderr
," B+Tree of index \"%s\" seems to be corrupt!\n",attribute
);
384 if (collect
&& (!gDoNotCheckIndex
|| !gDoNotCheckForFiles
))
387 if (!gDoNotCheckIndex
)
389 printf("Check for non-existing files in index \"%s\"...\n",attribute
);
390 checkIndexForNonExistingFiles(disk
,*tree
);
393 if (!gDoNotCheckForFiles
)
395 printf("Check for files not in index \"%s\" (this may take even more time)...\n",attribute
);
396 checkFiles(disk
,*tree
,attribute
);
403 printUsage(char *tool
)
405 char *filename
= strrchr(tool
,'/');
406 fprintf(stderr
,"usage: %s [-ifa] index-name\n"
407 "\t-i\tdo not check for non-existing files in the index\n"
408 "\t-f\tdo not check if all the files are in the index\n"
409 "\t-a\tcheck all indices (could take some weeks...)\n"
410 ,filename
? filename
+ 1 : tool
);
415 main(int argc
,char **argv
)
417 puts("Copyright (c) 2001-2008 pinc Software.");
419 char *toolName
= argv
[0];
420 if (argc
< 2 || !strcmp(argv
[1],"--help"))
422 printUsage(toolName
);
431 while (*++arg
&& isalpha(*arg
))
436 gDoNotCheckIndex
= true;
439 gDoNotCheckForFiles
= true;
451 char *attribute
= argv
[0];
452 if (!gCheckAll
&& attribute
== NULL
)
454 printUsage(toolName
);
458 dev_t device
= dev_for_path(".");
461 fprintf(stderr
,"Could not find device for current location: %s\n",strerror(device
));
466 if (fs_stat_dev(device
,&info
) < B_OK
)
468 fprintf(stderr
,"Could not get stats for device: %s\n",strerror(errno
));
472 Disk
disk(info
.device_name
);
474 if ((status
= disk
.InitCheck()) < B_OK
)
476 fprintf(stderr
,"Could not open device or file \"%s\": %s\n",info
.device_name
,strerror(status
));
480 if (disk
.ValidateSuperBlock() < B_OK
)
482 fprintf(stderr
,"The disk's superblock is corrupt!\n");
486 Directory
*indices
= (Directory
*)Inode::Factory(&disk
,disk
.Indices());
487 if (indices
== NULL
|| (status
= indices
->InitCheck()) < B_OK
)
489 fprintf(stderr
," Could not get indices directory: %s!\n",indices
? strerror(status
) : "not found/corrupted");
494 if (indices
->GetTree(&tree
) < B_OK
|| tree
->Validate() < B_OK
)
496 fprintf(stderr
," Indices B+Tree seems to be corrupt!\n");
508 char name
[B_FILE_NAME_LENGTH
];
509 while (indices
->GetNextEntry(name
,&run
) >= B_OK
)
510 checkIndex(disk
,name
,run
,false);
512 else if (indices
->FindEntry(attribute
,&run
) == B_OK
)
513 checkIndex(disk
,attribute
,run
,true);
515 fprintf(stderr
," Could not find index directory for \"%s\"!\n",attribute
);
519 gHashtable
.MakeEmpty(HASH_EMPTY_NONE
,HASH_EMPTY_FREE
);