2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
5 //! Handles BFS superblock, disk access etc.
23 #define MIN_BLOCK_SIZE_INODES 50
24 #define MAX_BLOCK_SIZE_INODES 500
27 struct bfs_disk_info
{
29 disk_super_block super_block
;
33 class CacheableBlockRun
: public BlockRunCache::Cacheable
{
35 CacheableBlockRun(block_run run
,uint8
*data
)
42 virtual ~CacheableBlockRun()
47 virtual bool Equals(block_run run
)
52 void SetData(uint8
*data
)
68 BlockRunCache::BlockRunCache(Disk
*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
);
83 ssize_t read
= fDisk
->ReadAt(fDisk
->ToOffset(run
),buffer
,length
);
89 return new CacheableBlockRun(run
,(uint8
*)buffer
);
96 Disk::Disk(const char *deviceName
, bool rawMode
, off_t start
, off_t stop
)
104 BEntry
entry(deviceName
);
105 fPath
.SetTo(deviceName
);
107 if (entry
.IsDirectory()) {
108 dev_t on
= dev_for_path(deviceName
);
110 if (fs_stat_dev(on
, &info
) != B_OK
)
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");
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()));
130 fBufferedFile
= new BBufferIO(&fFile
, 1024 * 1024, false);
132 int device
= open(deviceName
, O_RDONLY
);
134 //fprintf(stderr,"Could not open device\n");
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
))
147 fSize
= (off_t
)geometry
.cylinder_count
* geometry
.sectors_per_track
148 * geometry
.head_count
* geometry
.bytes_per_sector
;
150 fprintf(stderr
,"Disk: Could not get partition info.\n Use file size as partition size\n");
151 fFile
.GetSize(&fSize
);
156 fprintf(stderr
,"Disk: Invalid file size (%" B_PRIdOFF
" bytes)!\n",
160 if (rawMode
&& ScanForSuperBlock(start
, stop
) < B_OK
) {
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);
175 delete fBufferedFile
;
179 status_t
Disk::InitCheck()
181 status_t status
= fFile
.InitCheck();
183 return fSize
== 0LL ? B_ERROR
: B_OK
;
189 block_run
Disk::ToBlockRun(off_t start
, int16 length
) const
192 run
.allocation_group
= start
>> fSuperBlock
.ag_shift
;
193 run
.start
= start
& ((1UL << fSuperBlock
.ag_shift
) - 1);
200 off_t
Disk::LogSize() const
202 if (fSuperBlock
.num_blocks
>= 4096)
209 uint8
*Disk::ReadBlockRun(block_run run
)
211 CacheableBlockRun
*entry
= (CacheableBlockRun
*)fCache
.Get(run
);
213 return entry
->Data();
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());
234 memset(buffer
, 0, BlockSize());
235 memcpy(buffer
+ 512, &fSuperBlock
, sizeof(disk_super_block
));
237 // write buffer to disk
238 WriteAt(0, buffer
, BlockSize());
244 // #pragma mark - Superblock recovery methods
248 Disk::ScanForSuperBlock(off_t start
, off_t stop
)
250 printf("Disk size %" B_PRIdOFF
" bytes, %.2f GB\n", fSize
, 1.0 * fSize
253 uint32 blockSize
= 4096;
254 char buffer
[blockSize
+ 1024];
259 char escape
[3] = {0x1b, '[', 0};
263 printf("Scanning Disk from %" B_PRIdOFF
" to %" B_PRIdOFF
"\n", start
,
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);
276 fprintf(stderr
,"Could not read from device: %s\n", strerror(bytes
));
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
));
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
321 ValidateSuperBlock(info
->super_block
) < B_OK
? "in" : "");
325 printf("\nChoose one (by number): ");
328 fgets(answer
, 15, stdin
);
329 int32 index
= atol(answer
);
331 if (index
> superBlocks
.CountItems() || index
< 1) {
332 puts("No disk there... exiting.");
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
;
346 // just make it open-end
347 fSize
-= fRawDiskOffset
;
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
))
377 Disk::ValidateSuperBlock()
379 if (ValidateSuperBlock(fSuperBlock
) < B_OK
)
389 Disk::RecreateSuperBlock()
391 memset(&fSuperBlock
, 0, sizeof(disk_super_block
));
393 puts("\n*** Determine block size");
395 status_t status
= DetermineBlockSize();
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...");
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;
433 if (fValidOffset
== 0LL) {
434 printf("No valid inode found so far - perform search.\n");
436 off_t offset
= 8LL * 65536 * BlockSize();
438 GetNextSpecialInode(buffer
,&offset
,offset
+ 32LL * 65536 * BlockSize(),true);
440 if (fValidOffset
== 0LL)
442 fprintf(stderr
,"FATAL ERROR: Could not find valid inode!\n");
447 // calculate allocation group size
449 int32 allocationGroupSize
= (fValidOffset
- fValidBlockRun
.start
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());
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)
477 if (fBitmap
.UsedBlocks())
478 fSuperBlock
.used_blocks
= fBitmap
.UsedBlocks();
480 fprintf(stderr
,"ERROR: couldn't calculate number of used blocks, marking all blocks as used!\n");
481 fSuperBlock
.used_blocks
= fSuperBlock
.num_blocks
;
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;
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
));
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)
523 // update block size counter
524 for (int i
= 0;i
< 6;i
++)
525 if ((1 << (i
+ 10)) == inode
->inode_size
)
526 blockSizeCounter
[i
]++;
529 for (int32 i
= 0;i
< 6;i
++)
530 if (blockSizeCounter
[i
])
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))
540 // find the safest bet
541 int32 maxCounter
= -1;
543 for (int32 i
= 0; i
< 6; i
++) {
544 if (maxCounter
< blockSizeCounter
[i
]) {
546 maxCounter
= blockSizeCounter
[i
];
549 fSuperBlock
.block_size
= (1 << (maxIndex
+ 10));
556 Disk::GetNextSpecialInode(char *buffer
, off_t
*_offset
, off_t end
,
557 bool skipAfterValidInode
= false)
559 off_t offset
= *_offset
;
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);
573 if (inode
->magic1
!= INODE_MAGIC1
574 || inode
->inode_size
!= fSuperBlock
.inode_size
)
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
)
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
);
600 return B_ENTRY_NOT_FOUND
;
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) {
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
);
620 memcpy(rootDir
, inode
, sizeof(bfs_inode
));
621 } else if (inode
->mode
& S_INDEX_DIR
) {
623 printf("\tindices node found!\n");
625 memcpy(indexDir
, inode
, sizeof(bfs_inode
));
627 // if (gDumpIndexRootNode)
628 // dump_inode(inode);
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;
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
);
654 fputs("ERROR: no indices node found!\n",stderr
);
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
)
664 off_t commonOffset
= start
;
665 if (GetNextSpecialInode(buffer
, &commonOffset
, commonOffset
) == B_OK
) {
666 SaveInode(inode
, &indices
, indexDir
, &root
, rootDir
);
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",
683 //fBufferedFile->ReadAt(logOffset + BlockSize(),buffer,1024);
684 //if (*(uint32 *)buffer == BPLUSTREE_MAGIC)
686 // puts("\t\tnext block in log contains a bplustree!");
694 printf("Should I perform a deeper search (that will take some time) (Y/N) [N]? ");
697 if (!strcasecmp("y",txt))
699 // search not so common places for the root node (all places)
702 offset += BlockSize(); // the block after the indices inode
704 offset = (fLogStart + 1) * BlockSize();
706 if (GetNextSpecialInode(buffer,&offset,65536LL * 16 * BlockSize()) == B_OK)
707 SaveInode(inode,&indices,indexDir,&root,rootDir);
717 // #pragma mark - BPositionIO methods
721 Disk::Read(void *buffer
, size_t size
)
723 return fBufferedFile
->Read(buffer
, size
);
728 Disk::Write(const void *buffer
, size_t size
)
730 return fBufferedFile
->Write(buffer
, size
);
735 Disk::ReadAt(off_t pos
, void *buffer
, size_t size
)
737 return fBufferedFile
->ReadAt(pos
+ fRawDiskOffset
, buffer
, size
);
742 Disk::WriteAt(off_t pos
, const void *buffer
, size_t size
)
744 return fBufferedFile
->WriteAt(pos
+ fRawDiskOffset
, buffer
, size
);
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
);
759 Disk::Position() const
761 return fBufferedFile
->Position() - fRawDiskOffset
;
766 Disk::SetSize(off_t
/*size*/)
768 // SetSize() is not supported