2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
3 * Released under the terms of the MIT license.
6 //! sanity and completeness check for indices
10 #include "Hashtable.h"
11 #include "BPlusTree.h"
24 char gEscape
[3] = {0x1b, '[', 0};
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
37 BlockRunHashtable(int 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
));
55 memcpy(value
,&run
,sizeof(block_run
));
57 if (Hashtable::Put(value
,value
))
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);
80 compareBlockRuns(const block_run
*a
, const block_run
*b
)
82 int cmp
= (int)a
->allocation_group
- (int)b
->allocation_group
;
84 cmp
= (int)a
->start
- (int)b
->start
;
90 collectFiles(Disk
&disk
,Directory
*directory
)
92 if (directory
== NULL
)
96 char name
[B_FILE_NAME_LENGTH
];
98 while (directory
->GetNextEntry(name
,&run
) >= B_OK
)
100 if (!strcmp(name
,".") || !strcmp(name
,".."))
105 if (++gCount
% 50 == 0)
106 printf(" %7Ld%s1A\n",gCount
,gEscape
);
108 Inode
*inode
= Inode::Factory(&disk
,run
);
111 if (inode
->IsDirectory())
112 collectFiles(disk
,static_cast<Directory
*>(inode
));
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
);
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");
136 collectFiles(disk
,root
);
138 printf(" %7Ld files found.\n",gCount
);
145 checkIndexForNonExistingFiles(Disk
&disk
,BPlusTree
&tree
)
147 char name
[B_FILE_NAME_LENGTH
];
151 while (tree
.GetNextEntry(name
,&length
,B_FILE_NAME_LENGTH
,&offset
) == B_OK
)
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
);
160 case BPLUSTREE_STRING_TYPE
:
161 printf(" (string = \"%s\")",name
);
163 case BPLUSTREE_INT32_TYPE
:
164 printf(" (int32 = %ld)",*(int32
*)&name
);
166 case BPLUSTREE_UINT32_TYPE
:
167 printf(" (uint32 = %lu)",*(uint32
*)&name
);
169 case BPLUSTREE_INT64_TYPE
:
170 printf(" (int64 = %Ld)",*(int64
*)&name
);
172 case BPLUSTREE_UINT64_TYPE
:
173 printf(" (uint64 = %Lu)",*(uint64
*)&name
);
175 case BPLUSTREE_FLOAT_TYPE
:
176 printf(" (float = %g)",*(float *)&name
);
178 case BPLUSTREE_DOUBLE_TYPE
:
179 printf(" (double = %g)",*(double *)&name
);
189 checkFiles(Disk
&disk
,BPlusTree
&tree
,char *attribute
)
191 block_run
*runs
= (block_run
*)malloc(gCount
* sizeof(block_run
));
194 fprintf(stderr
," Not enough memory!\n");
197 // copy hashtable to array
198 block_run
*run
= NULL
;
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;
218 for (int32 i
= 0;i
< index
;i
++)
221 printf(" %7ld%s1A\n",i
,gEscape
);
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
);
232 // check indices not based on standard attributes
235 if (inode
->IsDirectory())
238 memcpy(key
,&inode
->InodeBuffer()->data
.size
,sizeof(off_t
));
239 keyLength
= sizeof(off_t
);
243 strcpy(key
,inode
->Name());
244 keyLength
= strlen(key
);
246 else if (modifiedIndex
)
248 if (inode
->IsDirectory())
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
];
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
;
278 if (tree
.Find((uint8
*)key
,keyLength
,&value
) < B_OK
)
281 fprintf(stderr
," inode at (%ld, %d) name \"%s\" is not in index!\n",runs
[i
].allocation_group
,runs
[i
].start
,inode
->Name());
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
];
294 off_t offset
,searchOffset
= disk
.ToBlock(runs
[i
]);
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());
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());
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());
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
);
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
);
330 char name
[B_FILE_NAME_LENGTH
];
333 bool found
= false,duplicates
= false;
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
))
342 if (disk
.ToBlockRun(offset
) == runs
[i
])
348 //else if (duplicates)
353 printf(" inode \"%s\" at (%ld, %d) not found in duplicates!\n",inode
->Name(),runs
[i
].allocation_group
,runs
[i
].start
);
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
);
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");
375 printf("\nCheck \"%s\" index's on-disk structure...\n",attribute
);
376 //dump_inode(index->InodeBuffer());
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
);
385 if (collect
&& (!gDoNotCheckIndex
|| !gDoNotCheckForFiles
))
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
);
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
);
432 while (*++arg
&& isalpha(*arg
))
437 gDoNotCheckIndex
= true;
440 gDoNotCheckForFiles
= true;
452 char *attribute
= argv
[0];
453 if (!gCheckAll
&& attribute
== NULL
)
455 printUsage(toolName
);
459 dev_t device
= dev_for_path(".");
462 fprintf(stderr
,"Could not find device for current location: %s\n",strerror(device
));
467 if (fs_stat_dev(device
,&info
) < B_OK
)
469 fprintf(stderr
,"Could not get stats for device: %s\n",strerror(errno
));
473 Disk
disk(info
.device_name
);
475 if ((status
= disk
.InitCheck()) < B_OK
)
477 fprintf(stderr
,"Could not open device or file \"%s\": %s\n",info
.device_name
,strerror(status
));
481 if (disk
.ValidateSuperBlock() < B_OK
)
483 fprintf(stderr
,"The disk's superblock is corrupt!\n");
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");
495 if (indices
->GetTree(&tree
) < B_OK
|| tree
->Validate() < B_OK
)
497 fprintf(stderr
," Indices B+Tree seems to be corrupt!\n");
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);
516 fprintf(stderr
," Could not find index directory for \"%s\"!\n",attribute
);
520 gHashtable
.MakeEmpty(HASH_EMPTY_NONE
,HASH_EMPTY_FREE
);