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