1 /* 2 * Copyright 2001-2010 pinc Software. All Rights Reserved. 3 */ 4 5 6 //! Dumps various information about BFS volumes. 7 8 9 #include "Disk.h" 10 #include "BPlusTree.h" 11 #include "Inode.h" 12 #include "dump.h" 13 14 #include <stdio.h> 15 #include <string.h> 16 #include <stdlib.h> 17 #include <ctype.h> 18 19 20 void 21 dump_bplustree(Disk &disk, BPositionIO *file, off_t size, bool hexDump) 22 { 23 uint8 *buffer = (uint8 *)malloc(size); 24 if (buffer == NULL) { 25 puts("no buffer"); 26 return; 27 } 28 29 if (file->ReadAt(0, buffer, size) != size) { 30 puts("couldn't read whole file"); 31 return; 32 } 33 34 bplustree_header *header = (bplustree_header *)buffer; 35 int32 nodeSize = header->node_size; 36 37 dump_bplustree_header(header); 38 39 bplustree_node *node = (bplustree_node *)(buffer + nodeSize); 40 while ((addr_t)node < (addr_t)buffer + size) { 41 printf("\n\n-------------------\n" 42 "** node at offset: %" B_PRIuADDR "\n** used: %" B_PRId32 " bytes" 43 "\n", (addr_t)node - (addr_t)buffer, node->Used()); 44 dump_bplustree_node(node, header, &disk); 45 46 if (hexDump) { 47 putchar('\n'); 48 dump_block((char *)node, header->node_size, 0); 49 } 50 51 node = (bplustree_node *)((addr_t)node + nodeSize); 52 } 53 } 54 55 56 void 57 dump_indirect_stream(Disk &disk, bfs_inode *node, bool showOffsets) 58 { 59 if (node->data.max_indirect_range == 0) 60 return; 61 62 int32 bytes = node->data.indirect.length * disk.BlockSize(); 63 int32 count = bytes / sizeof(block_run); 64 block_run runs[count]; 65 66 off_t offset = node->data.max_direct_range; 67 68 ssize_t bytesRead = disk.ReadAt(disk.ToOffset(node->data.indirect), 69 (uint8 *)runs, bytes); 70 if (bytesRead < bytes) { 71 fprintf(stderr, "couldn't read indirect runs: %s\n", 72 strerror(bytesRead)); 73 return; 74 } 75 76 puts("indirect stream:"); 77 78 for (int32 i = 0; i < count; i++) { 79 if (runs[i].IsZero()) 80 return; 81 82 printf(" indirect[%04" B_PRId32 "] = ", i); 83 84 char buffer[256]; 85 if (showOffsets) 86 snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset); 87 else 88 buffer[0] = '\0'; 89 90 dump_block_run("", runs[i], buffer); 91 92 offset += runs[i].length * disk.BlockSize(); 93 } 94 } 95 96 97 void 98 dump_double_indirect_stream(Disk& disk, bfs_inode* node, bool showOffsets) 99 { 100 if (node->data.max_double_indirect_range == 0) 101 return; 102 103 int32 bytes = node->data.double_indirect.length * disk.BlockSize(); 104 int32 count = bytes / sizeof(block_run); 105 block_run runs[count]; 106 107 off_t offset = node->data.max_indirect_range; 108 109 ssize_t bytesRead = disk.ReadAt(disk.ToOffset(node->data.double_indirect), 110 (uint8*)runs, bytes); 111 if (bytesRead < bytes) { 112 fprintf(stderr, "couldn't read double indirect runs: %s\n", 113 strerror(bytesRead)); 114 return; 115 } 116 117 puts("double indirect stream:"); 118 119 for (int32 i = 0; i < count; i++) { 120 if (runs[i].IsZero()) 121 return; 122 123 printf(" double_indirect[%02" B_PRId32 "] = ", i); 124 125 dump_block_run("", runs[i], ""); 126 127 int32 indirectBytes = runs[i].length * disk.BlockSize(); 128 int32 indirectCount = indirectBytes / sizeof(block_run); 129 block_run indirectRuns[indirectCount]; 130 131 bytesRead = disk.ReadAt(disk.ToOffset(runs[i]), (uint8*)indirectRuns, 132 indirectBytes); 133 if (bytesRead < indirectBytes) { 134 fprintf(stderr, "couldn't read double indirect runs: %s\n", 135 strerror(bytesRead)); 136 continue; 137 } 138 139 for (int32 j = 0; j < indirectCount; j++) { 140 if (indirectRuns[j].IsZero()) 141 break; 142 143 printf(" [%04" B_PRId32 "] = ", j); 144 145 char buffer[256]; 146 if (showOffsets) 147 snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset); 148 else 149 buffer[0] = '\0'; 150 151 dump_block_run("", indirectRuns[j], buffer); 152 153 offset += indirectRuns[j].length * disk.BlockSize(); 154 } 155 } 156 } 157 158 159 void 160 list_bplustree(Disk& disk, Directory* directory, off_t size) 161 { 162 directory->Rewind(); 163 164 char name[B_FILE_NAME_LENGTH]; 165 char buffer[512]; 166 uint64 count = 0; 167 block_run run; 168 while (directory->GetNextEntry(name, &run) == B_OK) { 169 snprintf(buffer, sizeof(buffer), " %s", name); 170 dump_block_run("", run, buffer); 171 count++; 172 } 173 174 printf("--\n%lld items.\n", count); 175 } 176 177 178 void 179 count_bplustree(Disk& disk, Directory* directory, off_t size) 180 { 181 directory->Rewind(); 182 183 char name[B_FILE_NAME_LENGTH]; 184 uint64 count = 0; 185 block_run run; 186 while (directory->GetNextEntry(name, &run) == B_OK) 187 count++; 188 189 printf("%lld items.\n", count); 190 } 191 192 193 block_run 194 parseBlockRun(Disk &disk, char *first, char *last) 195 { 196 char *comma; 197 198 if (last) { 199 return block_run::Run(atol(first), atol(last), 1); 200 } else if ((comma = strchr(first, ',')) != NULL) { 201 *comma++ = '\0'; 202 return block_run::Run(atol(first), atol(comma)); 203 } 204 205 return disk.ToBlockRun(atoll(first)); 206 } 207 208 209 int 210 main(int argc, char **argv) 211 { 212 puts("Copyright (c) 2001-2010 pinc Software."); 213 214 if (argc < 2 || !strcmp(argv[1], "--help")) { 215 char *filename = strrchr(argv[0],'/'); 216 fprintf(stderr,"usage: %s [-srib] <device> [allocation_group start]\n" 217 "\t-s\tdump superblock\n" 218 "\t-r\tdump root node\n" 219 " the following options need the allocation_group/start " 220 "parameters:\n" 221 "\t-i\tdump inode\n" 222 "\t-b\tdump b+tree\n" 223 "\t-c\tlist b+tree leaves\n" 224 "\t-c\tcount b+tree leaves\n" 225 "\t-v\tvalidate b+tree\n" 226 "\t-h\thexdump\n" 227 "\t-o\tshow disk offsets\n", 228 filename ? filename + 1 : argv[0]); 229 return -1; 230 } 231 232 bool dumpRootNode = false; 233 bool dumpInode = false; 234 bool dumpSuperBlock = false; 235 bool dumpBTree = false; 236 bool listBTree = false; 237 bool countBTree = false; 238 bool validateBTree = false; 239 bool dumpHex = false; 240 bool showOffsets = false; 241 242 while (*++argv) { 243 char *arg = *argv; 244 if (*arg == '-') { 245 while (*++arg && isalpha(*arg)) { 246 switch (*arg) { 247 case 's': 248 dumpSuperBlock = true; 249 break; 250 case 'r': 251 dumpRootNode = true; 252 break; 253 case 'i': 254 dumpInode = true; 255 break; 256 case 'b': 257 dumpBTree = true; 258 break; 259 case 'l': 260 listBTree = true; 261 break; 262 case 'c': 263 countBTree = true; 264 break; 265 case 'v': 266 validateBTree = true; 267 break; 268 case 'h': 269 dumpHex = true; 270 break; 271 case 'o': 272 showOffsets = true; 273 break; 274 } 275 } 276 } else 277 break; 278 } 279 280 Disk disk(argv[0]); 281 if (disk.InitCheck() < B_OK) 282 { 283 fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck())); 284 return -1; 285 } 286 putchar('\n'); 287 288 if (!dumpSuperBlock && !dumpRootNode && !dumpInode && !dumpBTree 289 && !dumpHex && !listBTree && !countBTree) { 290 printf(" Name:\t\t\t\"%s\"\n", disk.SuperBlock()->name); 291 printf(" (disk is %s)\n\n", 292 disk.ValidateSuperBlock() == B_OK ? "valid" : "invalid!!"); 293 printf(" Block Size:\t\t%" B_PRIu32 " bytes\n", disk.BlockSize()); 294 printf(" Number of Blocks:\t%12" B_PRIdOFF "\t%10g MB\n", 295 disk.NumBlocks(), disk.NumBlocks() * disk.BlockSize() 296 / (1024.0*1024)); 297 if (disk.BlockBitmap() != NULL) { 298 printf(" Used Blocks:\t\t%12" B_PRIdOFF "\t%10g MB\n", 299 disk.BlockBitmap()->UsedBlocks(), 300 disk.BlockBitmap()->UsedBlocks() * disk.BlockSize() 301 / (1024.0*1024)); 302 printf(" Free Blocks:\t\t%12" B_PRIdOFF "\t%10g MB\n", 303 disk.BlockBitmap()->FreeBlocks(), 304 disk.BlockBitmap()->FreeBlocks() * disk.BlockSize() 305 / (1024.0*1024)); 306 } 307 int32 size 308 = (disk.AllocationGroups() * disk.SuperBlock()->blocks_per_ag); 309 printf(" Bitmap Size:\t\t%" B_PRIu32 " bytes (%" B_PRId32 " blocks, %" 310 B_PRId32 " per allocation group)\n", disk.BlockSize() * size, size, 311 disk.SuperBlock()->blocks_per_ag); 312 printf(" Allocation Groups:\t%" B_PRIu32 "\n\n", 313 disk.AllocationGroups()); 314 dump_block_run(" Log:\t\t\t", disk.Log()); 315 printf(" (was %s)\n\n", disk.SuperBlock()->flags == SUPER_BLOCK_CLEAN 316 ? "cleanly unmounted" : "not unmounted cleanly!"); 317 dump_block_run(" Root Directory:\t", disk.Root()); 318 putchar('\n'); 319 } else if (dumpSuperBlock) { 320 dump_super_block(disk.SuperBlock()); 321 putchar('\n'); 322 } 323 324 if (disk.ValidateSuperBlock() < B_OK) { 325 fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS " 326 "device)!\n"); 327 return 0; 328 } 329 330 if (dumpRootNode) { 331 bfs_inode inode; 332 if (disk.ReadAt(disk.ToOffset(disk.Root()), (void *)&inode, 333 sizeof(bfs_inode)) < B_OK) { 334 fprintf(stderr,"Could not read root node from disk!\n"); 335 } else { 336 puts("Root node:\n-----------------------------------------"); 337 dump_inode(NULL, &inode, showOffsets); 338 dump_indirect_stream(disk, &inode, showOffsets); 339 putchar('\n'); 340 } 341 } 342 343 char buffer[disk.BlockSize()]; 344 bfs_inode* bfsInode = (bfs_inode*)buffer; 345 block_run run; 346 Inode *inode = NULL; 347 348 if (dumpInode || dumpBTree || dumpHex || validateBTree || listBTree 349 || countBTree) { 350 // Set the block_run to the right value (as specified on the command 351 // line) 352 if (!argv[1]) { 353 fprintf(stderr, "The -i/b/f options need the allocation group and " 354 "starting offset (or the block number) of the node to dump!\n"); 355 return -1; 356 } 357 run = parseBlockRun(disk, argv[1], argv[2]); 358 359 if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) { 360 fprintf(stderr,"Could not read node from disk!\n"); 361 return -1; 362 } 363 364 inode = Inode::Factory(&disk, bfsInode, false); 365 if (inode == NULL || inode->InitCheck() < B_OK) { 366 fprintf(stderr,"Not a valid inode!\n"); 367 delete inode; 368 inode = NULL; 369 } 370 } 371 372 if (dumpInode) { 373 printf("Inode at block %" B_PRIdOFF ":\n------------------------------" 374 "-----------\n", disk.ToBlock(run)); 375 dump_inode(inode, bfsInode, showOffsets); 376 dump_indirect_stream(disk, bfsInode, showOffsets); 377 dump_double_indirect_stream(disk, bfsInode, showOffsets); 378 dump_small_data(inode); 379 putchar('\n'); 380 } 381 382 if (dumpBTree && inode != NULL) { 383 printf("B+Tree at block %" B_PRIdOFF ":\n-----------------------------" 384 "------------\n", disk.ToBlock(run)); 385 if (inode->IsDirectory() || inode->IsAttributeDirectory()) { 386 dump_bplustree(disk, (Directory*)inode, inode->Size(), dumpHex); 387 putchar('\n'); 388 } else 389 fprintf(stderr, "Inode is not a directory!\n"); 390 } 391 392 if (listBTree && inode != NULL) { 393 printf("Directory contents: ------------------------------------------\n"); 394 if (inode->IsDirectory() || inode->IsAttributeDirectory()) { 395 list_bplustree(disk, (Directory*)inode, inode->Size()); 396 putchar('\n'); 397 } else 398 fprintf(stderr, "Inode is not a directory!\n"); 399 } 400 401 if (countBTree && inode != NULL) { 402 printf("Count contents: ------------------------------------------\n"); 403 if (inode->IsDirectory() || inode->IsAttributeDirectory()) { 404 count_bplustree(disk, (Directory*)inode, inode->Size()); 405 putchar('\n'); 406 } else 407 fprintf(stderr, "Inode is not a directory!\n"); 408 } 409 410 if (validateBTree && inode != NULL) { 411 printf("Validating B+Tree at block %" B_PRIdOFF ":\n------------------" 412 "-----------------------\n", disk.ToBlock(run)); 413 if (inode->IsDirectory() || inode->IsAttributeDirectory()) { 414 BPlusTree *tree; 415 if (((Directory *)inode)->GetTree(&tree) == B_OK) { 416 if (tree->Validate(true) < B_OK) 417 puts("B+Tree is corrupt!"); 418 else 419 puts("B+Tree seems to be okay."); 420 } 421 } else 422 fprintf(stderr, "Inode is not a directory!\n"); 423 } 424 425 if (dumpHex) { 426 printf("Hexdump from inode at block %" B_PRIdOFF ":\n-----------------" 427 "------------------------\n", disk.ToBlock(run)); 428 dump_block(buffer, disk.BlockSize()); 429 putchar('\n'); 430 } 431 432 delete inode; 433 434 return 0; 435 } 436 437