libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / bin / bfs_tools / chkindex.cpp
blob3850cba862dd95f7fe233893ea6556f2ff0fb318
1 /*
2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
3 * Released under the terms of the MIT license.
4 */
6 //! sanity and completeness check for indices
8 #include "Disk.h"
9 #include "Inode.h"
10 #include "Hashtable.h"
11 #include "BPlusTree.h"
12 #include "dump.h"
14 #include <fs_info.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <ctype.h>
21 #include <errno.h>
24 char gEscape[3] = {0x1b, '[', 0};
25 off_t gCount = 1;
26 bool gDoNotCheckForFiles = false;
27 bool gDoNotCheckIndex = false;
28 bool gCheckAll = false;
31 // we just want to know which inodes exist, so don't remember anything
32 // along the position of the inode.
34 class BlockRunHashtable : public Hashtable
36 public:
37 BlockRunHashtable(int capacity)
38 : Hashtable(capacity)
40 SetHashFunction((uint32 (*)(const void *))BlockRunHash);
41 SetCompareFunction((bool (*)(const void *, const void *))BlockRunCompare);
44 bool Contains(block_run *run)
46 return ContainsKey((void *)run);
49 bool Put(block_run &run)
51 block_run *value = (block_run *)malloc(sizeof(block_run));
52 if (!value)
53 return false;
55 memcpy(value,&run,sizeof(block_run));
57 if (Hashtable::Put(value,value))
58 return true;
60 free(value);
61 return false;
64 static uint32 BlockRunHash(const block_run *run)
66 return run->allocation_group << 16 | run->start;
69 static bool BlockRunCompare(const block_run *runA, const block_run *runB)
71 return *runA == *runB;
76 BlockRunHashtable gHashtable(1000);
79 int
80 compareBlockRuns(const block_run *a, const block_run *b)
82 int cmp = (int)a->allocation_group - (int)b->allocation_group;
83 if (cmp == 0)
84 cmp = (int)a->start - (int)b->start;
85 return cmp;
89 void
90 collectFiles(Disk &disk,Directory *directory)
92 if (directory == NULL)
93 return;
95 directory->Rewind();
96 char name[B_FILE_NAME_LENGTH];
97 block_run run;
98 while (directory->GetNextEntry(name,&run) >= B_OK)
100 if (!strcmp(name,".") || !strcmp(name,".."))
101 continue;
103 gHashtable.Put(run);
105 if (++gCount % 50 == 0)
106 printf(" %7Ld%s1A\n",gCount,gEscape);
108 Inode *inode = Inode::Factory(&disk,run);
109 if (inode != NULL)
111 if (inode->IsDirectory())
112 collectFiles(disk,static_cast<Directory *>(inode));
114 delete inode;
116 else
117 printf(" Directory \"%s\" (%ld, %d) points to corrupt inode \"%s\" (%ld, %d)\n",
118 directory->Name(),directory->BlockRun().allocation_group,directory->BlockRun().start,
119 name,run.allocation_group,run.start);
124 void
125 collectFiles(Disk &disk)
127 Directory *root = (Directory *)Inode::Factory(&disk,disk.Root());
129 puts("Collecting files (this will take some time)...");
131 if (root == NULL || root->InitCheck() < B_OK)
133 fprintf(stderr," Could not open root directory!\n");
134 return;
136 collectFiles(disk,root);
138 printf(" %7Ld files found.\n",gCount);
140 delete root;
144 void
145 checkIndexForNonExistingFiles(Disk &disk,BPlusTree &tree)
147 char name[B_FILE_NAME_LENGTH];
148 uint16 length;
149 off_t offset;
151 while (tree.GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK)
153 name[length] = 0;
154 block_run run = disk.ToBlockRun(offset);
155 if (!gHashtable.Contains(&run))
157 printf(" inode at (%ld, %d), offset %Ld, doesn't exist!",run.allocation_group,run.start,offset);
158 switch (tree.Type())
160 case BPLUSTREE_STRING_TYPE:
161 printf(" (string = \"%s\")",name);
162 break;
163 case BPLUSTREE_INT32_TYPE:
164 printf(" (int32 = %ld)",*(int32 *)&name);
165 break;
166 case BPLUSTREE_UINT32_TYPE:
167 printf(" (uint32 = %lu)",*(uint32 *)&name);
168 break;
169 case BPLUSTREE_INT64_TYPE:
170 printf(" (int64 = %Ld)",*(int64 *)&name);
171 break;
172 case BPLUSTREE_UINT64_TYPE:
173 printf(" (uint64 = %Lu)",*(uint64 *)&name);
174 break;
175 case BPLUSTREE_FLOAT_TYPE:
176 printf(" (float = %g)",*(float *)&name);
177 break;
178 case BPLUSTREE_DOUBLE_TYPE:
179 printf(" (double = %g)",*(double *)&name);
180 break;
182 putchar('\n');
188 void
189 checkFiles(Disk &disk,BPlusTree &tree,char *attribute)
191 block_run *runs = (block_run *)malloc(gCount * sizeof(block_run));
192 if (runs == NULL)
194 fprintf(stderr," Not enough memory!\n");
195 return;
197 // copy hashtable to array
198 block_run *run = NULL;
199 int32 index = 0;
200 gHashtable.Rewind();
201 while (gHashtable.GetNextEntry((void **)&run) == B_OK)
203 runs[index++] = *run;
206 // sort array to speed up disk access
207 qsort(runs,index,sizeof(block_run),(int (*)(const void *,const void *))compareBlockRuns);
209 bool sizeIndex = !strcmp(attribute,"size");
210 bool nameIndex = !strcmp(attribute,"name");
211 bool modifiedIndex = !strcmp(attribute,"last_modified");
213 char key[B_FILE_NAME_LENGTH];
214 uint16 keyLength = 0;
216 Inode *inode = NULL;
218 for (int32 i = 0;i < index;i++)
220 if (i % 50 == 0)
221 printf(" %7ld%s1A\n",i,gEscape);
223 delete inode;
224 inode = Inode::Factory(&disk,runs[i]);
225 if (inode == NULL || inode->InitCheck() < B_OK)
227 fprintf(stderr," inode at (%ld, %d) is corrupt!\n",runs[i].allocation_group,runs[i].start);
228 delete inode;
229 continue;
232 // check indices not based on standard attributes
233 if (sizeIndex)
235 if (inode->IsDirectory())
236 continue;
238 memcpy(key,&inode->InodeBuffer()->data.size,sizeof(off_t));
239 keyLength = sizeof(off_t);
241 else if (nameIndex)
243 strcpy(key,inode->Name());
244 keyLength = strlen(key);
246 else if (modifiedIndex)
248 if (inode->IsDirectory())
249 continue;
251 memcpy(key,&inode->InodeBuffer()->last_modified_time,sizeof(off_t));
252 keyLength = sizeof(off_t);
254 else // iterate through all attributes to find the right one (damn slow, sorry...)
256 inode->RewindAttributes();
257 char name[B_FILE_NAME_LENGTH];
258 uint32 type;
259 void *data;
260 size_t length;
261 bool found = false;
262 while (inode->GetNextAttribute(name,&type,&data,&length) == B_OK)
264 if (!strcmp(name,attribute))
266 strncpy(key,(char *)data,B_FILE_NAME_LENGTH - 1);
267 key[B_FILE_NAME_LENGTH - 1] = '\0';
268 keyLength = length > B_FILE_NAME_LENGTH ? B_FILE_NAME_LENGTH : length;
269 found = true;
270 break;
273 if (!found)
274 continue;
277 off_t value;
278 if (tree.Find((uint8 *)key,keyLength,&value) < B_OK)
280 if (*inode->Name())
281 fprintf(stderr," inode at (%ld, %d) name \"%s\" is not in index!\n",runs[i].allocation_group,runs[i].start,inode->Name());
282 else
284 // inode is obviously deleted!
285 block_run parent = inode->Parent();
286 Directory *directory = (Directory *)Inode::Factory(&disk,parent);
287 if (directory != NULL && directory->InitCheck() == B_OK)
289 BPlusTree *parentTree;
290 if (directory->GetTree(&parentTree) == B_OK)
292 char name[B_FILE_NAME_LENGTH];
293 uint16 length;
294 off_t offset,searchOffset = disk.ToBlock(runs[i]);
295 bool found = false;
297 while (parentTree->GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK)
299 if (offset == searchOffset)
301 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());
302 found = true;
303 break;
306 if (!found)
307 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 else
310 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());
312 else
314 // not that this would be really possible... - but who knows
315 fprintf(stderr," inode at (%ld, %d) is not in index and has invalid parent!\n",runs[i].allocation_group,runs[i].start);
317 delete directory;
320 else
322 if (bplustree_node::LinkType(value) == BPLUSTREE_NODE)
324 if (disk.ToBlockRun(value) != runs[i])
325 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);
327 else
329 // search duplicates
330 char name[B_FILE_NAME_LENGTH];
331 uint16 length;
332 off_t offset;
333 bool found = false,duplicates = false;
334 //puts("++");
335 tree.Rewind();
336 while (tree.GetNextEntry(name,&length,B_FILE_NAME_LENGTH,&offset) == B_OK)
338 //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);
339 if (keyLength == length && !memcmp(key,name,keyLength))
341 duplicates = true;
342 if (disk.ToBlockRun(offset) == runs[i])
344 found = true;
345 break;
348 //else if (duplicates)
349 // break;
351 if (!found)
353 printf(" inode \"%s\" at (%ld, %d) not found in duplicates!\n",inode->Name(),runs[i].allocation_group,runs[i].start);
354 // return;
359 delete inode;
360 printf(" %7Ld files processed.\n",gCount);
365 checkIndex(Disk &disk,char *attribute,block_run &run,bool collect)
367 Directory *index = (Directory *)Inode::Factory(&disk,run);
368 status_t status;
369 if (index == NULL || (status = index->InitCheck()) < B_OK)
371 fprintf(stderr," Could not get index directory for \"%s\": %s!\n",attribute,index ? strerror(status) : "not found/corrupted");
372 return -1;
375 printf("\nCheck \"%s\" index's on-disk structure...\n",attribute);
376 //dump_inode(index->InodeBuffer());
378 BPlusTree *tree;
379 if (index->GetTree(&tree) < B_OK || tree->Validate(true) < B_OK)
381 fprintf(stderr," B+Tree of index \"%s\" seems to be corrupt!\n",attribute);
382 //return -1;
385 if (collect && (!gDoNotCheckIndex || !gDoNotCheckForFiles))
386 collectFiles(disk);
388 if (!gDoNotCheckIndex)
390 printf("Check for non-existing files in index \"%s\"...\n",attribute);
391 checkIndexForNonExistingFiles(disk,*tree);
394 if (!gDoNotCheckForFiles)
396 printf("Check for files not in index \"%s\" (this may take even more time)...\n",attribute);
397 checkFiles(disk,*tree,attribute);
399 return 0;
403 void
404 printUsage(char *tool)
406 char *filename = strrchr(tool,'/');
407 fprintf(stderr,"usage: %s [-ifa] index-name\n"
408 "\t-i\tdo not check for non-existing files in the index\n"
409 "\t-f\tdo not check if all the files are in the index\n"
410 "\t-a\tcheck all indices (could take some weeks...)\n"
411 ,filename ? filename + 1 : tool);
416 main(int argc,char **argv)
418 puts("Copyright (c) 2001-2008 pinc Software.");
420 char *toolName = argv[0];
421 if (argc < 2 || !strcmp(argv[1],"--help"))
423 printUsage(toolName);
424 return -1;
427 while (*++argv)
429 char *arg = *argv;
430 if (*arg == '-')
432 while (*++arg && isalpha(*arg))
434 switch (*arg)
436 case 'i':
437 gDoNotCheckIndex = true;
438 break;
439 case 'f':
440 gDoNotCheckForFiles = true;
441 break;
442 case 'a':
443 gCheckAll = true;
444 break;
448 else
449 break;
452 char *attribute = argv[0];
453 if (!gCheckAll && attribute == NULL)
455 printUsage(toolName);
456 return -1;
459 dev_t device = dev_for_path(".");
460 if (device < B_OK)
462 fprintf(stderr,"Could not find device for current location: %s\n",strerror(device));
463 return -1;
466 fs_info info;
467 if (fs_stat_dev(device,&info) < B_OK)
469 fprintf(stderr,"Could not get stats for device: %s\n",strerror(errno));
470 return -1;
473 Disk disk(info.device_name);
474 status_t status;
475 if ((status = disk.InitCheck()) < B_OK)
477 fprintf(stderr,"Could not open device or file \"%s\": %s\n",info.device_name,strerror(status));
478 return -1;
481 if (disk.ValidateSuperBlock() < B_OK)
483 fprintf(stderr,"The disk's superblock is corrupt!\n");
484 return -1;
487 Directory *indices = (Directory *)Inode::Factory(&disk,disk.Indices());
488 if (indices == NULL || (status = indices->InitCheck()) < B_OK)
490 fprintf(stderr," Could not get indices directory: %s!\n",indices ? strerror(status) : "not found/corrupted");
491 delete indices;
492 return -1;
494 BPlusTree *tree;
495 if (indices->GetTree(&tree) < B_OK || tree->Validate() < B_OK)
497 fprintf(stderr," Indices B+Tree seems to be corrupt!\n");
498 delete indices;
499 return -1;
502 block_run run;
504 if (gCheckAll)
506 putchar('\n');
507 collectFiles(disk);
509 char name[B_FILE_NAME_LENGTH];
510 while (indices->GetNextEntry(name,&run) >= B_OK)
511 checkIndex(disk,name,run,false);
513 else if (indices->FindEntry(attribute,&run) == B_OK)
514 checkIndex(disk,attribute,run,true);
515 else
516 fprintf(stderr," Could not find index directory for \"%s\"!\n",attribute);
518 delete indices;
520 gHashtable.MakeEmpty(HASH_EMPTY_NONE,HASH_EMPTY_FREE);
522 return 0;