RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / bin / bfs_tools / lib / Disk.cpp
blob29d7da08b0ca75c0418ea3e109e8ba3d9a3f1a8d
1 /*
2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
3 */
5 //! Handles BFS superblock, disk access etc.
7 #include "Disk.h"
8 #include "dump.h"
10 #include <Drivers.h>
11 #include <File.h>
12 #include <Entry.h>
13 #include <List.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>
23 #define MIN_BLOCK_SIZE_INODES 50
24 #define MAX_BLOCK_SIZE_INODES 500
27 struct bfs_disk_info {
28 off_t offset;
29 disk_super_block super_block;
33 class CacheableBlockRun : public BlockRunCache::Cacheable {
34 public:
35 CacheableBlockRun(block_run run,uint8 *data)
37 fRun(run),
38 fData(data)
42 virtual ~CacheableBlockRun()
44 free(fData);
47 virtual bool Equals(block_run run)
49 return run == fRun;
52 void SetData(uint8 *data)
54 fData = data;
57 uint8 *Data()
59 return fData;
62 protected:
63 block_run fRun;
64 uint8 *fData;
68 BlockRunCache::BlockRunCache(Disk *disk)
69 : Cache<block_run>(),
70 fDisk(disk)
75 Cache<block_run>::Cacheable *BlockRunCache::NewCacheable(block_run run)
77 ssize_t length = (int32)run.length << fDisk->BlockShift();
79 void *buffer = malloc(length);
80 if (buffer == NULL)
81 return NULL;
83 ssize_t read = fDisk->ReadAt(fDisk->ToOffset(run),buffer,length);
84 if (read < length) {
85 free(buffer);
86 return NULL;
89 return new CacheableBlockRun(run,(uint8 *)buffer);
93 // #pragma mark -
96 Disk::Disk(const char *deviceName, bool rawMode, off_t start, off_t stop)
98 fBufferedFile(NULL),
99 fRawDiskOffset(0),
100 fSize(0LL),
101 fCache(this),
102 fRawMode(rawMode)
104 BEntry entry(deviceName);
105 fPath.SetTo(deviceName);
107 if (entry.IsDirectory()) {
108 dev_t on = dev_for_path(deviceName);
109 fs_info info;
110 if (fs_stat_dev(on, &info) != B_OK)
111 return;
113 fPath.SetTo(info.device_name);
114 deviceName = fPath.Path();
117 if (deviceName == NULL) {
118 fprintf(stderr, "Path is not mapped to any device.\n");
119 return;
122 if (!rawMode && !strncmp(deviceName, "/dev/", 5)
123 && !strcmp(deviceName + strlen(deviceName) - 3, "raw"))
124 fprintf(stderr, "Raw mode not selected, but raw device specified.\n");
126 if (fFile.SetTo(deviceName, B_READ_WRITE) < B_OK) {
127 //fprintf(stderr,"Could not open file: %s\n",strerror(fFile.InitCheck()));
128 return;
130 fBufferedFile = new BBufferIO(&fFile, 1024 * 1024, false);
132 int device = open(deviceName, O_RDONLY);
133 if (device < B_OK) {
134 //fprintf(stderr,"Could not open device\n");
135 return;
138 partition_info partitionInfo;
139 device_geometry geometry;
140 if (ioctl(device, B_GET_PARTITION_INFO, &partitionInfo,
141 sizeof(partition_info)) == 0) {
142 //if (gDumpPartitionInfo)
143 // dump_partition_info(&partitionInfo);
144 fSize = partitionInfo.size;
145 } else if (ioctl(device, B_GET_GEOMETRY, &geometry, sizeof(device_geometry))
146 == 0) {
147 fSize = (off_t)geometry.cylinder_count * geometry.sectors_per_track
148 * geometry.head_count * geometry.bytes_per_sector;
149 } else {
150 fprintf(stderr,"Disk: Could not get partition info.\n Use file size as partition size\n");
151 fFile.GetSize(&fSize);
153 close(device);
155 if (fSize == 0LL) {
156 fprintf(stderr,"Disk: Invalid file size (%" B_PRIdOFF " bytes)!\n",
157 fSize);
160 if (rawMode && ScanForSuperBlock(start, stop) < B_OK) {
161 fFile.Unset();
162 return;
165 if (fBufferedFile->ReadAt(512 + fRawDiskOffset, &fSuperBlock,
166 sizeof(disk_super_block)) < 1)
167 fprintf(stderr,"Disk: Could not read superblock\n");
169 //dump_super_block(&fSuperBlock);
173 Disk::~Disk()
175 delete fBufferedFile;
179 status_t Disk::InitCheck()
181 status_t status = fFile.InitCheck();
182 if (status == B_OK)
183 return fSize == 0LL ? B_ERROR : B_OK;
185 return status;
189 block_run Disk::ToBlockRun(off_t start, int16 length) const
191 block_run run;
192 run.allocation_group = start >> fSuperBlock.ag_shift;
193 run.start = start & ((1UL << fSuperBlock.ag_shift) - 1);
194 run.length = length;
196 return run;
200 off_t Disk::LogSize() const
202 if (fSuperBlock.num_blocks >= 4096)
203 return 2048;
205 return 512;
209 uint8 *Disk::ReadBlockRun(block_run run)
211 CacheableBlockRun *entry = (CacheableBlockRun *)fCache.Get(run);
212 if (entry)
213 return entry->Data();
215 return NULL;
219 status_t
220 Disk::DumpBootBlockToFile()
222 BFile file("/boot/home/bootblock.old", B_READ_WRITE | B_CREATE_FILE);
223 if (file.InitCheck() < B_OK)
224 return file.InitCheck();
226 char buffer[BlockSize()];
227 ssize_t bytes = ReadAt(0, buffer, BlockSize());
228 if (bytes < (int32)BlockSize())
229 return bytes < B_OK ? bytes : B_ERROR;
231 file.Write(buffer, BlockSize());
233 // initialize buffer
234 memset(buffer, 0, BlockSize());
235 memcpy(buffer + 512, &fSuperBlock, sizeof(disk_super_block));
237 // write buffer to disk
238 WriteAt(0, buffer, BlockSize());
240 return B_OK;
244 // #pragma mark - Superblock recovery methods
247 status_t
248 Disk::ScanForSuperBlock(off_t start, off_t stop)
250 printf("Disk size %" B_PRIdOFF " bytes, %.2f GB\n", fSize, 1.0 * fSize
251 / (1024*1024*1024));
253 uint32 blockSize = 4096;
254 char buffer[blockSize + 1024];
256 if (stop == -1)
257 stop = fSize;
259 char escape[3] = {0x1b, '[', 0};
261 BList superBlocks;
263 printf("Scanning Disk from %" B_PRIdOFF " to %" B_PRIdOFF "\n", start,
264 stop);
266 for (off_t offset = start; offset < stop; offset += blockSize)
268 if (((offset-start) % (blockSize * 100)) == 0) {
269 printf(" %12" B_PRIdOFF ", %.2f GB %s1A\n", offset,
270 1.0 * offset / (1024*1024*1024),escape);
273 ssize_t bytes = fBufferedFile->ReadAt(offset, buffer, blockSize + 1024);
274 if (bytes < B_OK)
276 fprintf(stderr,"Could not read from device: %s\n", strerror(bytes));
277 return -1;
280 for (uint32 i = 0;i < blockSize - 2;i++)
282 disk_super_block *super = (disk_super_block *)&buffer[i];
284 if (super->magic1 == (int32)SUPER_BLOCK_MAGIC1
285 && super->magic2 == (int32)SUPER_BLOCK_MAGIC2
286 && super->magic3 == (int32)SUPER_BLOCK_MAGIC3)
288 printf("\n(%" B_PRId32 ") *** BFS superblock found at: %"
289 B_PRIdOFF "\n", superBlocks.CountItems() + 1, offset);
290 dump_super_block(super);
292 // add a copy of the superblock to the list
293 bfs_disk_info *info = (bfs_disk_info *)malloc(sizeof(bfs_disk_info));
294 if (info == NULL)
295 return B_NO_MEMORY;
297 memcpy(&info->super_block, super, sizeof(disk_super_block));
298 info->offset = offset + i - 512;
299 /* location off the BFS superblock is 512 bytes after the partition start */
300 superBlocks.AddItem(info);
305 if (superBlocks.CountItems() == 0) {
306 puts("\nCouldn't find any BFS superblocks!");
307 return B_ENTRY_NOT_FOUND;
310 // Let the user decide which block to use
312 puts("\n\nThe following disks were found:");
314 for (int32 i = 0; i < superBlocks.CountItems(); i++) {
315 bfs_disk_info *info = (bfs_disk_info *)superBlocks.ItemAt(i);
317 printf("%" B_PRId32 ") %s, offset %" B_PRIdOFF ", size %g GB (%svalid)"
318 "\n", i + 1, info->super_block.name, info->offset,
319 1.0 * info->super_block.num_blocks * info->super_block.block_size
320 / (1024*1024*1024),
321 ValidateSuperBlock(info->super_block) < B_OK ? "in" : "");
324 char answer[16];
325 printf("\nChoose one (by number): ");
326 fflush(stdout);
328 fgets(answer, 15, stdin);
329 int32 index = atol(answer);
331 if (index > superBlocks.CountItems() || index < 1) {
332 puts("No disk there... exiting.");
333 return B_BAD_VALUE;
336 bfs_disk_info *info = (bfs_disk_info *)superBlocks.ItemAt(index - 1);
338 // ToDo: free the other disk infos
340 fRawDiskOffset = info->offset;
341 fBufferedFile->Seek(fRawDiskOffset, SEEK_SET);
343 if (ValidateSuperBlock(info->super_block))
344 fSize = info->super_block.block_size * info->super_block.block_size;
345 else {
346 // just make it open-end
347 fSize -= fRawDiskOffset;
350 return B_OK;
354 status_t
355 Disk::ValidateSuperBlock(disk_super_block &superBlock)
357 if (superBlock.magic1 != (int32)SUPER_BLOCK_MAGIC1
358 || superBlock.magic2 != (int32)SUPER_BLOCK_MAGIC2
359 || superBlock.magic3 != (int32)SUPER_BLOCK_MAGIC3
360 || (int32)superBlock.block_size != superBlock.inode_size
361 || superBlock.fs_byte_order != SUPER_BLOCK_FS_LENDIAN
362 || (1UL << superBlock.block_shift) != superBlock.block_size
363 || superBlock.num_ags < 1
364 || superBlock.ag_shift < 1
365 || superBlock.blocks_per_ag < 1
366 || superBlock.num_blocks < 10
367 || superBlock.used_blocks > superBlock.num_blocks
368 || superBlock.num_ags != divide_roundup(superBlock.num_blocks,
369 1L << superBlock.ag_shift))
370 return B_ERROR;
372 return B_OK;
376 status_t
377 Disk::ValidateSuperBlock()
379 if (ValidateSuperBlock(fSuperBlock) < B_OK)
380 return B_ERROR;
382 fBitmap.SetTo(this);
384 return B_OK;
388 status_t
389 Disk::RecreateSuperBlock()
391 memset(&fSuperBlock, 0, sizeof(disk_super_block));
393 puts("\n*** Determine block size");
395 status_t status = DetermineBlockSize();
396 if (status < B_OK)
397 return status;
399 printf("\tblock size = %" B_PRId32 "\n", BlockSize());
401 strcpy(fSuperBlock.name,"recovered");
402 fSuperBlock.magic1 = SUPER_BLOCK_MAGIC1;
403 fSuperBlock.fs_byte_order = SUPER_BLOCK_FS_LENDIAN;
404 fSuperBlock.block_shift = get_shift(BlockSize());
405 fSuperBlock.num_blocks = fSize / BlockSize(); // only full blocks
406 fSuperBlock.inode_size = BlockSize();
407 fSuperBlock.magic2 = SUPER_BLOCK_MAGIC2;
409 fSuperBlock.flags = SUPER_BLOCK_CLEAN;
411 // size of the block bitmap + root block
412 fLogStart = 1 + divide_roundup(fSuperBlock.num_blocks,BlockSize() * 8);
414 // set it anywhere in the log
415 fSuperBlock.log_start = fLogStart + (LogSize() >> 1);
416 fSuperBlock.log_end = fSuperBlock.log_start;
419 // scan for indices and root inode
422 puts("\n*** Scanning for indices and root node...");
423 fValidOffset = 0LL;
424 bfs_inode indexDir;
425 bfs_inode rootDir;
426 if (ScanForIndexAndRoot(&indexDir,&rootDir) < B_OK) {
427 fprintf(stderr,"ERROR: Could not find root directory! Trying to recreate the superblock will have no effect!\n\tSetting standard values for the root dir.\n");
428 rootDir.inode_num.allocation_group = 8;
429 rootDir.inode_num.start = 0;
430 rootDir.inode_num.length = 1;
431 //gErrors++;
433 if (fValidOffset == 0LL) {
434 printf("No valid inode found so far - perform search.\n");
436 off_t offset = 8LL * 65536 * BlockSize();
437 char buffer[1024];
438 GetNextSpecialInode(buffer,&offset,offset + 32LL * 65536 * BlockSize(),true);
440 if (fValidOffset == 0LL)
442 fprintf(stderr,"FATAL ERROR: Could not find valid inode!\n");
443 return B_ERROR;
447 // calculate allocation group size
449 int32 allocationGroupSize = (fValidOffset - fValidBlockRun.start
450 * BlockSize()
451 + BlockSize() - 1) / (BlockSize() * fValidBlockRun.allocation_group);
453 fSuperBlock.blocks_per_ag = allocationGroupSize / (BlockSize() << 3);
454 fSuperBlock.ag_shift = get_shift(allocationGroupSize);
455 fSuperBlock.num_ags = divide_roundup(fSuperBlock.num_blocks,allocationGroupSize);
457 // calculate rest of log area
459 fSuperBlock.log_blocks.allocation_group = fLogStart / allocationGroupSize;
460 fSuperBlock.log_blocks.start = fLogStart - fSuperBlock.log_blocks.allocation_group * allocationGroupSize;
461 fSuperBlock.log_blocks.length = LogSize(); // assumed length of 2048 blocks
463 if (fLogStart != ((indexDir.inode_num.allocation_group
464 << fSuperBlock.ag_shift) + indexDir.inode_num.start - LogSize())) {
465 fprintf(stderr,"ERROR: start of logging area doesn't fit assumed value "
466 "(%" B_PRIdOFF " blocks before indices)!\n", LogSize());
467 //gErrors++;
470 fSuperBlock.magic3 = SUPER_BLOCK_MAGIC3;
471 fSuperBlock.root_dir = rootDir.inode_num;
472 fSuperBlock.indices = indexDir.inode_num;
474 // calculate used blocks (from block bitmap)
476 fBitmap.SetTo(this);
477 if (fBitmap.UsedBlocks())
478 fSuperBlock.used_blocks = fBitmap.UsedBlocks();
479 else {
480 fprintf(stderr,"ERROR: couldn't calculate number of used blocks, marking all blocks as used!\n");
481 fSuperBlock.used_blocks = fSuperBlock.num_blocks;
482 //gErrors++;
485 return B_OK;
489 status_t
490 Disk::DetermineBlockSize()
492 // scan for about 500 inodes to determine the disk's block size
494 // min. block bitmap size (in bytes, rounded to a 1024 boundary)
495 // root_block_size + (num_blocks / bits_per_block) * block_size
496 off_t offset = 1024 + divide_roundup(fSize / 1024,8 * 1024) * 1024;
498 off_t inodesFound = 0;
499 char buffer[1024];
500 bfs_inode *inode = (bfs_inode *)buffer;
501 // valid block sizes from 1024 to 32768 bytes
502 int32 blockSizeCounter[6] = {0};
503 status_t status = B_OK;
505 // read a quarter of the drive at maximum
506 for (; offset < (fSize >> 2); offset += 1024) {
507 if (fBufferedFile->ReadAt(offset, buffer, sizeof(buffer)) < B_OK) {
508 fprintf(stderr, "could not read from device (offset = %" B_PRIdOFF
509 ", size = %ld)!\n", offset, sizeof(buffer));
510 status = B_IO_ERROR;
511 break;
514 if (inode->magic1 != INODE_MAGIC1
515 || inode->inode_size != 1024
516 && inode->inode_size != 2048
517 && inode->inode_size != 4096
518 && inode->inode_size != 8192)
519 continue;
521 inodesFound++;
523 // update block size counter
524 for (int i = 0;i < 6;i++)
525 if ((1 << (i + 10)) == inode->inode_size)
526 blockSizeCounter[i]++;
528 int32 count = 0;
529 for (int32 i = 0;i < 6;i++)
530 if (blockSizeCounter[i])
531 count++;
533 // do we have a clear winner at 100 inodes?
534 // if not, break at 500 inodes
535 if (inodesFound >= MAX_BLOCK_SIZE_INODES
536 || (inodesFound >= MIN_BLOCK_SIZE_INODES && count == 1))
537 break;
540 // find the safest bet
541 int32 maxCounter = -1;
542 int32 maxIndex = 0;
543 for (int32 i = 0; i < 6; i++) {
544 if (maxCounter < blockSizeCounter[i]) {
545 maxIndex = i;
546 maxCounter = blockSizeCounter[i];
549 fSuperBlock.block_size = (1 << (maxIndex + 10));
551 return status;
555 status_t
556 Disk::GetNextSpecialInode(char *buffer, off_t *_offset, off_t end,
557 bool skipAfterValidInode = false)
559 off_t offset = *_offset;
560 if (end == offset)
561 end++;
563 bfs_inode *inode = (bfs_inode *)buffer;
565 for (; offset < end; offset += BlockSize()) {
566 if (fBufferedFile->ReadAt(offset, buffer, 1024) < B_OK) {
567 fprintf(stderr,"could not read from device (offset = %" B_PRIdOFF
568 ", size = %d)!\n", offset, 1024);
569 *_offset = offset;
570 return B_IO_ERROR;
573 if (inode->magic1 != INODE_MAGIC1
574 || inode->inode_size != fSuperBlock.inode_size)
575 continue;
577 // can compute allocation group only for inodes which are
578 // a) not in the first allocation group
579 // b) not in the log area
581 if (inode->inode_num.allocation_group > 0
582 && offset >= (BlockSize() * (fLogStart + LogSize()))) {
583 fValidBlockRun = inode->inode_num;
584 fValidOffset = offset;
586 if (skipAfterValidInode)
587 return B_OK;
590 // is the inode a special root inode (parent == self)?
592 if (!memcmp(&inode->parent, &inode->inode_num, sizeof(inode_addr))) {
593 printf("\t special inode found at %" B_PRIdOFF "\n", offset);
595 *_offset = offset;
596 return B_OK;
599 *_offset = offset;
600 return B_ENTRY_NOT_FOUND;
604 void
605 Disk::SaveInode(bfs_inode *inode, bool *indices, bfs_inode *indexDir,
606 bool *root, bfs_inode *rootDir)
608 if ((inode->mode & S_INDEX_DIR) == 0) {
609 *root = true;
610 printf("\troot node found!\n");
611 if (inode->inode_num.allocation_group != 8
612 || inode->inode_num.start != 0
613 || inode->inode_num.length != 1) {
614 printf("WARNING: root node has unexpected position: (ag = %"
615 B_PRId32 ", start = %d, length = %d)\n",
616 inode->inode_num.allocation_group,
617 inode->inode_num.start, inode->inode_num.length);
619 if (rootDir)
620 memcpy(rootDir, inode, sizeof(bfs_inode));
621 } else if (inode->mode & S_INDEX_DIR) {
622 *indices = true;
623 printf("\tindices node found!\n");
624 if (indexDir)
625 memcpy(indexDir, inode, sizeof(bfs_inode));
627 // if (gDumpIndexRootNode)
628 // dump_inode(inode);
632 status_t
633 Disk::ScanForIndexAndRoot(bfs_inode *indexDir,bfs_inode *rootDir)
635 memset(rootDir,0,sizeof(bfs_inode));
636 memset(indexDir,0,sizeof(bfs_inode));
638 bool indices = false,root = false;
639 char buffer[1024];
640 bfs_inode *inode = (bfs_inode *)buffer;
642 // The problem here is that we don't want to find copies of the
643 // inodes in the log.
644 // The first offset where a root node can be is therefore the
645 // first allocation group with a block size of 1024, and 16384
646 // blocks per ag; that should be relatively save.
648 // search for the indices inode, start reading from log size + boot block size
649 off_t offset = (fLogStart + LogSize()) * BlockSize();
650 if (GetNextSpecialInode(buffer,&offset,offset + 65536LL * BlockSize()) == B_OK)
651 SaveInode(inode,&indices,indexDir,&root,rootDir);
653 if (!indices) {
654 fputs("ERROR: no indices node found!\n",stderr);
655 //gErrors++;
658 // search common places for root node, iterating from allocation group
659 // size from 1024 to 65536
660 for (off_t start = 8LL * 1024 * BlockSize();start <= 8LL * 65536 * BlockSize();start <<= 1) {
661 if (start < fLogStart)
662 continue;
664 off_t commonOffset = start;
665 if (GetNextSpecialInode(buffer, &commonOffset, commonOffset) == B_OK) {
666 SaveInode(inode, &indices, indexDir, &root, rootDir);
667 if (root)
668 break;
672 if (!root) {
673 printf("WARNING: Could not find root node at common places!\n");
674 printf("\tScanning log area for root node\n");
676 off_t logOffset = ToOffset(fSuperBlock.log_blocks);
677 if (GetNextSpecialInode(buffer,&logOffset,logOffset + LogSize() * BlockSize()) == B_OK)
679 SaveInode(inode,&indices,indexDir,&root,rootDir);
681 printf("root node at: 0x%" B_PRIxOFF " (DiskProbe)\n",
682 logOffset / 512);
683 //fBufferedFile->ReadAt(logOffset + BlockSize(),buffer,1024);
684 //if (*(uint32 *)buffer == BPLUSTREE_MAGIC)
686 // puts("\t\tnext block in log contains a bplustree!");
691 /*if (!root)
693 char txt[64];
694 printf("Should I perform a deeper search (that will take some time) (Y/N) [N]? ");
695 gets(txt);
697 if (!strcasecmp("y",txt))
699 // search not so common places for the root node (all places)
701 if (indices)
702 offset += BlockSize(); // the block after the indices inode
703 else
704 offset = (fLogStart + 1) * BlockSize();
706 if (GetNextSpecialInode(buffer,&offset,65536LL * 16 * BlockSize()) == B_OK)
707 SaveInode(inode,&indices,indexDir,&root,rootDir);
710 if (root)
711 return B_OK;
713 return B_ERROR;
717 // #pragma mark - BPositionIO methods
720 ssize_t
721 Disk::Read(void *buffer, size_t size)
723 return fBufferedFile->Read(buffer, size);
727 ssize_t
728 Disk::Write(const void *buffer, size_t size)
730 return fBufferedFile->Write(buffer, size);
734 ssize_t
735 Disk::ReadAt(off_t pos, void *buffer, size_t size)
737 return fBufferedFile->ReadAt(pos + fRawDiskOffset, buffer, size);
741 ssize_t
742 Disk::WriteAt(off_t pos, const void *buffer, size_t size)
744 return fBufferedFile->WriteAt(pos + fRawDiskOffset, buffer, size);
748 off_t
749 Disk::Seek(off_t position, uint32 seekMode)
751 // ToDo: only correct for seekMode == SEEK_SET, right??
752 if (seekMode != SEEK_SET)
753 puts("OH NO, I AM BROKEN!");
754 return fBufferedFile->Seek(position + fRawDiskOffset, seekMode);
758 off_t
759 Disk::Position() const
761 return fBufferedFile->Position() - fRawDiskOffset;
765 status_t
766 Disk::SetSize(off_t /*size*/)
768 // SetSize() is not supported
769 return B_ERROR;