RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / bin / bfs_tools / chkindex.cpp
blob8c141b6bf1ff7c5c1c79562e35448cfb1ba41895
1 /*
2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
3 */
5 //! sanity and completeness check for indices
7 #include "Disk.h"
8 #include "Inode.h"
9 #include "Hashtable.h"
10 #include "BPlusTree.h"
11 #include "dump.h"
13 #include <fs_info.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <errno.h>
23 char gEscape[3] = {0x1b, '[', 0};
24 off_t gCount = 1;
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
35 public:
36 BlockRunHashtable(int capacity)
37 : Hashtable(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));
51 if (!value)
52 return false;
54 memcpy(value,&run,sizeof(block_run));
56 if (Hashtable::Put(value,value))
57 return true;
59 free(value);
60 return false;
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);
78 int
79 compareBlockRuns(const block_run *a, const block_run *b)
81 int cmp = (int)a->allocation_group - (int)b->allocation_group;
82 if (cmp == 0)
83 cmp = (int)a->start - (int)b->start;
84 return cmp;
88 void
89 collectFiles(Disk &disk,Directory *directory)
91 if (directory == NULL)
92 return;
94 directory->Rewind();
95 char name[B_FILE_NAME_LENGTH];
96 block_run run;
97 while (directory->GetNextEntry(name,&run) >= B_OK)
99 if (!strcmp(name,".") || !strcmp(name,".."))
100 continue;
102 gHashtable.Put(run);
104 if (++gCount % 50 == 0)
105 printf(" %7Ld%s1A\n",gCount,gEscape);
107 Inode *inode = Inode::Factory(&disk,run);
108 if (inode != NULL)
110 if (inode->IsDirectory())
111 collectFiles(disk,static_cast<Directory *>(inode));
113 delete inode;
115 else
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);
123 void
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");
133 return;
135 collectFiles(disk,root);
137 printf(" %7Ld files found.\n",gCount);
139 delete root;
143 void
144 checkIndexForNonExistingFiles(Disk &disk,BPlusTree &tree)
146 char name[B_FILE_NAME_LENGTH];
147 uint16 length;
148 off_t offset;
150 while (tree.GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK)
152 name[length] = 0;
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);
157 switch (tree.Type())
159 case BPLUSTREE_STRING_TYPE:
160 printf(" (string = \"%s\")",name);
161 break;
162 case BPLUSTREE_INT32_TYPE:
163 printf(" (int32 = %ld)",*(int32 *)&name);
164 break;
165 case BPLUSTREE_UINT32_TYPE:
166 printf(" (uint32 = %lu)",*(uint32 *)&name);
167 break;
168 case BPLUSTREE_INT64_TYPE:
169 printf(" (int64 = %Ld)",*(int64 *)&name);
170 break;
171 case BPLUSTREE_UINT64_TYPE:
172 printf(" (uint64 = %Lu)",*(uint64 *)&name);
173 break;
174 case BPLUSTREE_FLOAT_TYPE:
175 printf(" (float = %g)",*(float *)&name);
176 break;
177 case BPLUSTREE_DOUBLE_TYPE:
178 printf(" (double = %g)",*(double *)&name);
179 break;
181 putchar('\n');
187 void
188 checkFiles(Disk &disk,BPlusTree &tree,char *attribute)
190 block_run *runs = (block_run *)malloc(gCount * sizeof(block_run));
191 if (runs == NULL)
193 fprintf(stderr," Not enough memory!\n");
194 return;
196 // copy hashtable to array
197 block_run *run = NULL;
198 int32 index = 0;
199 gHashtable.Rewind();
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;
215 Inode *inode = NULL;
217 for (int32 i = 0;i < index;i++)
219 if (i % 50 == 0)
220 printf(" %7ld%s1A\n",i,gEscape);
222 delete inode;
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);
227 delete inode;
228 continue;
231 // check indices not based on standard attributes
232 if (sizeIndex)
234 if (inode->IsDirectory())
235 continue;
237 memcpy(key,&inode->InodeBuffer()->data.size,sizeof(off_t));
238 keyLength = sizeof(off_t);
240 else if (nameIndex)
242 strcpy(key,inode->Name());
243 keyLength = strlen(key);
245 else if (modifiedIndex)
247 if (inode->IsDirectory())
248 continue;
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];
257 uint32 type;
258 void *data;
259 size_t length;
260 bool found = false;
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;
268 found = true;
269 break;
272 if (!found)
273 continue;
276 off_t value;
277 if (tree.Find((uint8 *)key,keyLength,&value) < B_OK)
279 if (*inode->Name())
280 fprintf(stderr," inode at (%ld, %d) name \"%s\" is not in index!\n",runs[i].allocation_group,runs[i].start,inode->Name());
281 else
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];
292 uint16 length;
293 off_t offset,searchOffset = disk.ToBlock(runs[i]);
294 bool found = false;
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());
301 found = true;
302 break;
305 if (!found)
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());
308 else
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());
311 else
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);
316 delete directory;
319 else
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);
326 else
328 // search duplicates
329 char name[B_FILE_NAME_LENGTH];
330 uint16 length;
331 off_t offset;
332 bool found = false,duplicates = false;
333 //puts("++");
334 tree.Rewind();
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))
340 duplicates = true;
341 if (disk.ToBlockRun(offset) == runs[i])
343 found = true;
344 break;
347 //else if (duplicates)
348 // break;
350 if (!found)
352 printf(" inode \"%s\" at (%ld, %d) not found in duplicates!\n",inode->Name(),runs[i].allocation_group,runs[i].start);
353 // return;
358 delete inode;
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);
367 status_t status;
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");
371 return -1;
374 printf("\nCheck \"%s\" index's on-disk structure...\n",attribute);
375 //dump_inode(index->InodeBuffer());
377 BPlusTree *tree;
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);
381 //return -1;
384 if (collect && (!gDoNotCheckIndex || !gDoNotCheckForFiles))
385 collectFiles(disk);
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);
398 return 0;
402 void
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);
423 return -1;
426 while (*++argv)
428 char *arg = *argv;
429 if (*arg == '-')
431 while (*++arg && isalpha(*arg))
433 switch (*arg)
435 case 'i':
436 gDoNotCheckIndex = true;
437 break;
438 case 'f':
439 gDoNotCheckForFiles = true;
440 break;
441 case 'a':
442 gCheckAll = true;
443 break;
447 else
448 break;
451 char *attribute = argv[0];
452 if (!gCheckAll && attribute == NULL)
454 printUsage(toolName);
455 return -1;
458 dev_t device = dev_for_path(".");
459 if (device < B_OK)
461 fprintf(stderr,"Could not find device for current location: %s\n",strerror(device));
462 return -1;
465 fs_info info;
466 if (fs_stat_dev(device,&info) < B_OK)
468 fprintf(stderr,"Could not get stats for device: %s\n",strerror(errno));
469 return -1;
472 Disk disk(info.device_name);
473 status_t status;
474 if ((status = disk.InitCheck()) < B_OK)
476 fprintf(stderr,"Could not open device or file \"%s\": %s\n",info.device_name,strerror(status));
477 return -1;
480 if (disk.ValidateSuperBlock() < B_OK)
482 fprintf(stderr,"The disk's superblock is corrupt!\n");
483 return -1;
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");
490 delete indices;
491 return -1;
493 BPlusTree *tree;
494 if (indices->GetTree(&tree) < B_OK || tree->Validate() < B_OK)
496 fprintf(stderr," Indices B+Tree seems to be corrupt!\n");
497 delete indices;
498 return -1;
501 block_run run;
503 if (gCheckAll)
505 putchar('\n');
506 collectFiles(disk);
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);
514 else
515 fprintf(stderr," Could not find index directory for \"%s\"!\n",attribute);
517 delete indices;
519 gHashtable.MakeEmpty(HASH_EMPTY_NONE,HASH_EMPTY_FREE);
521 return 0;