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 ((int32)node < (int32)buffer + size) { 41 printf("\n\n-------------------\n" 42 "** node at offset: %ld\n** used: %ld bytes\n", 43 int32(node) - int32(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 *)(int32(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[%02ld] = ", i); 83 84 char buffer[256]; 85 if (showOffsets) 86 snprintf(buffer, sizeof(buffer), " %16lld", 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 block_run 98 parseBlockRun(Disk &disk, char *first, char *last) 99 { 100 char *comma; 101 102 if (last) { 103 return block_run::Run(atol(first), atol(last), 1); 104 } else if ((comma = strchr(first, ',')) != NULL) { 105 *comma++ = '\0'; 106 return block_run::Run(atol(first), atol(comma)); 107 } 108 109 return disk.ToBlockRun(atoll(first)); 110 } 111 112 113 int 114 main(int argc, char **argv) 115 { 116 puts("Copyright (c) 2001-2010 pinc Software."); 117 118 if (argc < 2 || !strcmp(argv[1], "--help")) { 119 char *filename = strrchr(argv[0],'/'); 120 fprintf(stderr,"usage: %s [-srib] <device> [allocation_group start]\n" 121 "\t-s\tdump superblock\n" 122 "\t-r\tdump root node\n" 123 " the following options need the allocation_group/start " 124 "parameters:\n" 125 "\t-i\tdump inode\n" 126 "\t-b\tdump b+tree\n" 127 "\t-v\tvalidate b+tree\n" 128 "\t-h\thexdump\n" 129 "\t-o\tshow disk offsets\n", 130 filename ? filename + 1 : argv[0]); 131 return -1; 132 } 133 134 bool dumpRootNode = false; 135 bool dumpInode = false; 136 bool dumpSuperBlock = false; 137 bool dumpBTree = false; 138 bool validateBTree = false; 139 bool dumpHex = false; 140 bool showOffsets = false; 141 142 while (*++argv) { 143 char *arg = *argv; 144 if (*arg == '-') { 145 while (*++arg && isalpha(*arg)) { 146 switch (*arg) { 147 case 's': 148 dumpSuperBlock = true; 149 break; 150 case 'r': 151 dumpRootNode = true; 152 break; 153 case 'i': 154 dumpInode = true; 155 break; 156 case 'b': 157 dumpBTree = true; 158 break; 159 case 'v': 160 validateBTree = true; 161 break; 162 case 'h': 163 dumpHex = true; 164 break; 165 case 'o': 166 showOffsets = true; 167 break; 168 } 169 } 170 } else 171 break; 172 } 173 174 Disk disk(argv[0]); 175 if (disk.InitCheck() < B_OK) 176 { 177 fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck())); 178 return -1; 179 } 180 putchar('\n'); 181 182 if (!dumpSuperBlock && !dumpRootNode && !dumpInode && !dumpBTree 183 && !dumpHex) { 184 printf(" Name:\t\t\t\"%s\"\n", disk.SuperBlock()->name); 185 printf(" (disk is %s)\n\n", 186 disk.ValidateSuperBlock() == B_OK ? "valid" : "invalid!!"); 187 printf(" Block Size:\t\t%ld bytes\n", disk.BlockSize()); 188 printf(" Number of Blocks:\t%12Lu\t%10g MB\n", disk.NumBlocks(), 189 disk.NumBlocks() * disk.BlockSize() / (1024.0*1024)); 190 if (disk.BlockBitmap() != NULL) { 191 printf(" Used Blocks:\t\t%12Lu\t%10g MB\n", 192 disk.BlockBitmap()->UsedBlocks(), 193 disk.BlockBitmap()->UsedBlocks() * disk.BlockSize() 194 / (1024.0*1024)); 195 printf(" Free Blocks:\t\t%12Lu\t%10g MB\n", 196 disk.BlockBitmap()->FreeBlocks(), 197 disk.BlockBitmap()->FreeBlocks() * disk.BlockSize() 198 / (1024.0*1024)); 199 } 200 int32 size 201 = (disk.AllocationGroups() * disk.SuperBlock()->blocks_per_ag); 202 printf(" Bitmap Size:\t\t%ld bytes (%ld blocks, %ld per allocation " 203 "group)\n", disk.BlockSize() * size, size, 204 disk.SuperBlock()->blocks_per_ag); 205 printf(" Allocation Groups:\t%lu\n\n", disk.AllocationGroups()); 206 dump_block_run(" Log:\t\t\t", disk.Log()); 207 printf(" (was %s)\n\n", disk.SuperBlock()->flags == SUPER_BLOCK_CLEAN 208 ? "cleanly unmounted" : "not unmounted cleanly!"); 209 dump_block_run(" Root Directory:\t", disk.Root()); 210 putchar('\n'); 211 } else if (dumpSuperBlock) { 212 dump_super_block(disk.SuperBlock()); 213 putchar('\n'); 214 } 215 216 if (disk.ValidateSuperBlock() < B_OK) { 217 fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS " 218 "device)!\n"); 219 return 0; 220 } 221 222 if (dumpRootNode) { 223 bfs_inode inode; 224 if (disk.ReadAt(disk.ToOffset(disk.Root()), (void *)&inode, 225 sizeof(bfs_inode)) < B_OK) { 226 fprintf(stderr,"Could not read root node from disk!\n"); 227 } else { 228 puts("Root node:\n-----------------------------------------"); 229 dump_inode(NULL, &inode, showOffsets); 230 dump_indirect_stream(disk, &inode, showOffsets); 231 putchar('\n'); 232 } 233 } 234 235 char buffer[disk.BlockSize()]; 236 block_run run; 237 Inode *inode = NULL; 238 239 if (dumpInode || dumpBTree || dumpHex || validateBTree) { 240 // Set the block_run to the right value (as specified on the command 241 // line) 242 if (!argv[1]) { 243 fprintf(stderr, "The -i/b/f options need the allocation group and " 244 "starting offset (or the block number) of the node to dump!\n"); 245 return -1; 246 } 247 run = parseBlockRun(disk, argv[1], argv[2]); 248 249 if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) { 250 fprintf(stderr,"Could not read node from disk!\n"); 251 return -1; 252 } 253 254 inode = Inode::Factory(&disk, (bfs_inode *)buffer, false); 255 if (inode == NULL || inode->InitCheck() < B_OK) { 256 fprintf(stderr,"Not a valid inode!\n"); 257 delete inode; 258 inode = NULL; 259 } 260 } 261 262 if (dumpInode) { 263 printf("Inode at block %Ld:\n-----------------------------------------" 264 "\n", disk.ToBlock(run)); 265 dump_inode(inode, (bfs_inode *)buffer, showOffsets); 266 dump_indirect_stream(disk, (bfs_inode *)buffer, showOffsets); 267 dump_small_data(inode); 268 putchar('\n'); 269 } 270 271 if (dumpBTree && inode) { 272 printf("B+Tree at block %Ld:\n----------------------------------------" 273 "-\n", disk.ToBlock(run)); 274 if (inode->IsDirectory() || inode->IsAttributeDirectory()) { 275 dump_bplustree(disk, (Directory *)inode, inode->Size(), dumpHex); 276 putchar('\n'); 277 } else 278 fprintf(stderr, "Inode is not a directory!\n"); 279 } 280 281 if (validateBTree && inode) { 282 printf("Validating B+Tree at block %Ld:\n-----------------------------" 283 "------------\n", disk.ToBlock(run)); 284 if (inode->IsDirectory() || inode->IsAttributeDirectory()) { 285 BPlusTree *tree; 286 if (((Directory *)inode)->GetTree(&tree) == B_OK) { 287 if (tree->Validate(true) < B_OK) 288 puts("B+Tree is corrupt!"); 289 else 290 puts("B+Tree seems to be okay."); 291 } 292 } else 293 fprintf(stderr, "Inode is not a directory!\n"); 294 } 295 296 if (dumpHex) { 297 printf("Hexdump from inode at block %Ld:\n-----------------------------" 298 "------------\n", disk.ToBlock(run)); 299 dump_block(buffer, disk.BlockSize()); 300 putchar('\n'); 301 } 302 303 delete inode; 304 305 return 0; 306 } 307 308