2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
3 * Released under the terms of the MIT license.
6 //! Handles BFS superblock, disk access etc.
24 #define MIN_BLOCK_SIZE_INODES 50
25 #define MAX_BLOCK_SIZE_INODES 500
28 struct bfs_disk_info
{
30 disk_super_block super_block
;
34 class CacheableBlockRun
: public BlockRunCache::Cacheable
{
36 CacheableBlockRun(block_run run
,uint8
*data
)
43 virtual ~CacheableBlockRun()
48 virtual bool Equals(block_run run
)
53 void SetData(uint8
*data
)
69 BlockRunCache::BlockRunCache(Disk
*disk
)
76 Cache
<block_run
>::Cacheable
*BlockRunCache::NewCacheable(block_run run
)
78 ssize_t length
= (int32
)run
.length
<< fDisk
->BlockShift();
80 void *buffer
= malloc(length
);
84 ssize_t read
= fDisk
->ReadAt(fDisk
->ToOffset(run
),buffer
,length
);
90 return new CacheableBlockRun(run
,(uint8
*)buffer
);
97 Disk::Disk(const char *deviceName
, bool rawMode
, off_t start
, off_t stop
)
105 BEntry
entry(deviceName
);
106 fPath
.SetTo(deviceName
);
108 if (entry
.IsDirectory()) {
109 dev_t on
= dev_for_path(deviceName
);
111 if (fs_stat_dev(on
, &info
) != B_OK
)
114 fPath
.SetTo(info
.device_name
);
115 deviceName
= fPath
.Path();
118 if (deviceName
== NULL
) {
119 fprintf(stderr
, "Path is not mapped to any device.\n");
123 if (!rawMode
&& !strncmp(deviceName
, "/dev/", 5)
124 && !strcmp(deviceName
+ strlen(deviceName
) - 3, "raw"))
125 fprintf(stderr
, "Raw mode not selected, but raw device specified.\n");
127 if (fFile
.SetTo(deviceName
, B_READ_WRITE
) < B_OK
) {
128 //fprintf(stderr,"Could not open file: %s\n",strerror(fFile.InitCheck()));
131 fBufferedFile
= new BBufferIO(&fFile
, 1024 * 1024, false);
133 int device
= open(deviceName
, O_RDONLY
);
135 //fprintf(stderr,"Could not open device\n");
139 partition_info partitionInfo
;
140 device_geometry geometry
;
141 if (ioctl(device
, B_GET_PARTITION_INFO
, &partitionInfo
,
142 sizeof(partition_info
)) == 0) {
143 //if (gDumpPartitionInfo)
144 // dump_partition_info(&partitionInfo);
145 fSize
= partitionInfo
.size
;
146 } else if (ioctl(device
, B_GET_GEOMETRY
, &geometry
, sizeof(device_geometry
))
148 fSize
= (off_t
)geometry
.cylinder_count
* geometry
.sectors_per_track
149 * geometry
.head_count
* geometry
.bytes_per_sector
;
151 fprintf(stderr
,"Disk: Could not get partition info.\n Use file size as partition size\n");
152 fFile
.GetSize(&fSize
);
157 fprintf(stderr
,"Disk: Invalid file size (%" B_PRIdOFF
" bytes)!\n",
161 if (rawMode
&& ScanForSuperBlock(start
, stop
) < B_OK
) {
166 if (fBufferedFile
->ReadAt(512 + fRawDiskOffset
, &fSuperBlock
,
167 sizeof(disk_super_block
)) < 1)
168 fprintf(stderr
,"Disk: Could not read superblock\n");
170 //dump_super_block(&fSuperBlock);
176 delete fBufferedFile
;
180 status_t
Disk::InitCheck()
182 status_t status
= fFile
.InitCheck();
184 return fSize
== 0LL ? B_ERROR
: B_OK
;
190 block_run
Disk::ToBlockRun(off_t start
, int16 length
) const
193 run
.allocation_group
= start
>> fSuperBlock
.ag_shift
;
194 run
.start
= start
& ((1UL << fSuperBlock
.ag_shift
) - 1);
201 off_t
Disk::LogSize() const
203 if (fSuperBlock
.num_blocks
>= 4096)
210 uint8
*Disk::ReadBlockRun(block_run run
)
212 CacheableBlockRun
*entry
= (CacheableBlockRun
*)fCache
.Get(run
);
214 return entry
->Data();
221 Disk::DumpBootBlockToFile()
223 BFile
file("/boot/home/bootblock.old", B_READ_WRITE
| B_CREATE_FILE
);
224 if (file
.InitCheck() < B_OK
)
225 return file
.InitCheck();
227 char buffer
[BlockSize()];
228 ssize_t bytes
= ReadAt(0, buffer
, BlockSize());
229 if (bytes
< (int32
)BlockSize())
230 return bytes
< B_OK
? bytes
: B_ERROR
;
232 file
.Write(buffer
, BlockSize());
235 memset(buffer
, 0, BlockSize());
236 memcpy(buffer
+ 512, &fSuperBlock
, sizeof(disk_super_block
));
238 // write buffer to disk
239 WriteAt(0, buffer
, BlockSize());
245 // #pragma mark - Superblock recovery methods
249 Disk::ScanForSuperBlock(off_t start
, off_t stop
)
251 printf("Disk size %" B_PRIdOFF
" bytes, %.2f GB\n", fSize
, 1.0 * fSize
254 uint32 blockSize
= 4096;
255 char buffer
[blockSize
+ 1024];
260 char escape
[3] = {0x1b, '[', 0};
264 printf("Scanning Disk from %" B_PRIdOFF
" to %" B_PRIdOFF
"\n", start
,
267 for (off_t offset
= start
; offset
< stop
; offset
+= blockSize
)
269 if (((offset
-start
) % (blockSize
* 100)) == 0) {
270 printf(" %12" B_PRIdOFF
", %.2f GB %s1A\n", offset
,
271 1.0 * offset
/ (1024*1024*1024),escape
);
274 ssize_t bytes
= fBufferedFile
->ReadAt(offset
, buffer
, blockSize
+ 1024);
277 fprintf(stderr
,"Could not read from device: %s\n", strerror(bytes
));
281 for (uint32 i
= 0;i
< blockSize
- 2;i
++)
283 disk_super_block
*super
= (disk_super_block
*)&buffer
[i
];
285 if (super
->magic1
== (int32
)SUPER_BLOCK_MAGIC1
286 && super
->magic2
== (int32
)SUPER_BLOCK_MAGIC2
287 && super
->magic3
== (int32
)SUPER_BLOCK_MAGIC3
)
289 printf("\n(%" B_PRId32
") *** BFS superblock found at: %"
290 B_PRIdOFF
"\n", superBlocks
.CountItems() + 1, offset
);
291 dump_super_block(super
);
293 // add a copy of the superblock to the list
294 bfs_disk_info
*info
= (bfs_disk_info
*)malloc(sizeof(bfs_disk_info
));
298 memcpy(&info
->super_block
, super
, sizeof(disk_super_block
));
299 info
->offset
= offset
+ i
- 512;
300 /* location off the BFS superblock is 512 bytes after the partition start */
301 superBlocks
.AddItem(info
);
306 if (superBlocks
.CountItems() == 0) {
307 puts("\nCouldn't find any BFS superblocks!");
308 return B_ENTRY_NOT_FOUND
;
311 // Let the user decide which block to use
313 puts("\n\nThe following disks were found:");
315 for (int32 i
= 0; i
< superBlocks
.CountItems(); i
++) {
316 bfs_disk_info
*info
= (bfs_disk_info
*)superBlocks
.ItemAt(i
);
318 printf("%" B_PRId32
") %s, offset %" B_PRIdOFF
", size %g GB (%svalid)"
319 "\n", i
+ 1, info
->super_block
.name
, info
->offset
,
320 1.0 * info
->super_block
.num_blocks
* info
->super_block
.block_size
322 ValidateSuperBlock(info
->super_block
) < B_OK
? "in" : "");
326 printf("\nChoose one (by number): ");
329 fgets(answer
, 15, stdin
);
330 int32 index
= atol(answer
);
332 if (index
> superBlocks
.CountItems() || index
< 1) {
333 puts("No disk there... exiting.");
337 bfs_disk_info
*info
= (bfs_disk_info
*)superBlocks
.ItemAt(index
- 1);
339 // ToDo: free the other disk infos
341 fRawDiskOffset
= info
->offset
;
342 fBufferedFile
->Seek(fRawDiskOffset
, SEEK_SET
);
344 if (ValidateSuperBlock(info
->super_block
))
345 fSize
= info
->super_block
.block_size
* info
->super_block
.block_size
;
347 // just make it open-end
348 fSize
-= fRawDiskOffset
;
356 Disk::ValidateSuperBlock(disk_super_block
&superBlock
)
358 if (superBlock
.magic1
!= (int32
)SUPER_BLOCK_MAGIC1
359 || superBlock
.magic2
!= (int32
)SUPER_BLOCK_MAGIC2
360 || superBlock
.magic3
!= (int32
)SUPER_BLOCK_MAGIC3
361 || (int32
)superBlock
.block_size
!= superBlock
.inode_size
362 || superBlock
.fs_byte_order
!= SUPER_BLOCK_FS_LENDIAN
363 || (1UL << superBlock
.block_shift
) != superBlock
.block_size
364 || superBlock
.num_ags
< 1
365 || superBlock
.ag_shift
< 1
366 || superBlock
.blocks_per_ag
< 1
367 || superBlock
.num_blocks
< 10
368 || superBlock
.used_blocks
> superBlock
.num_blocks
369 || superBlock
.num_ags
!= divide_roundup(superBlock
.num_blocks
,
370 1L << superBlock
.ag_shift
))
378 Disk::ValidateSuperBlock()
380 if (ValidateSuperBlock(fSuperBlock
) < B_OK
)
390 Disk::RecreateSuperBlock()
392 memset(&fSuperBlock
, 0, sizeof(disk_super_block
));
394 puts("\n*** Determine block size");
396 status_t status
= DetermineBlockSize();
400 printf("\tblock size = %" B_PRId32
"\n", BlockSize());
402 strcpy(fSuperBlock
.name
,"recovered");
403 fSuperBlock
.magic1
= SUPER_BLOCK_MAGIC1
;
404 fSuperBlock
.fs_byte_order
= SUPER_BLOCK_FS_LENDIAN
;
405 fSuperBlock
.block_shift
= get_shift(BlockSize());
406 fSuperBlock
.num_blocks
= fSize
/ BlockSize(); // only full blocks
407 fSuperBlock
.inode_size
= BlockSize();
408 fSuperBlock
.magic2
= SUPER_BLOCK_MAGIC2
;
410 fSuperBlock
.flags
= SUPER_BLOCK_CLEAN
;
412 // size of the block bitmap + root block
413 fLogStart
= 1 + divide_roundup(fSuperBlock
.num_blocks
,BlockSize() * 8);
415 // set it anywhere in the log
416 fSuperBlock
.log_start
= fLogStart
+ (LogSize() >> 1);
417 fSuperBlock
.log_end
= fSuperBlock
.log_start
;
420 // scan for indices and root inode
423 puts("\n*** Scanning for indices and root node...");
427 if (ScanForIndexAndRoot(&indexDir
,&rootDir
) < B_OK
) {
428 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");
429 rootDir
.inode_num
.allocation_group
= 8;
430 rootDir
.inode_num
.start
= 0;
431 rootDir
.inode_num
.length
= 1;
434 if (fValidOffset
== 0LL) {
435 printf("No valid inode found so far - perform search.\n");
437 off_t offset
= 8LL * 65536 * BlockSize();
439 GetNextSpecialInode(buffer
,&offset
,offset
+ 32LL * 65536 * BlockSize(),true);
441 if (fValidOffset
== 0LL)
443 fprintf(stderr
,"FATAL ERROR: Could not find valid inode!\n");
448 // calculate allocation group size
450 int32 allocationGroupSize
= (fValidOffset
- fValidBlockRun
.start
452 + BlockSize() - 1) / (BlockSize() * fValidBlockRun
.allocation_group
);
454 fSuperBlock
.blocks_per_ag
= allocationGroupSize
/ (BlockSize() << 3);
455 fSuperBlock
.ag_shift
= get_shift(allocationGroupSize
);
456 fSuperBlock
.num_ags
= divide_roundup(fSuperBlock
.num_blocks
,allocationGroupSize
);
458 // calculate rest of log area
460 fSuperBlock
.log_blocks
.allocation_group
= fLogStart
/ allocationGroupSize
;
461 fSuperBlock
.log_blocks
.start
= fLogStart
- fSuperBlock
.log_blocks
.allocation_group
* allocationGroupSize
;
462 fSuperBlock
.log_blocks
.length
= LogSize(); // assumed length of 2048 blocks
464 if (fLogStart
!= ((indexDir
.inode_num
.allocation_group
465 << fSuperBlock
.ag_shift
) + indexDir
.inode_num
.start
- LogSize())) {
466 fprintf(stderr
,"ERROR: start of logging area doesn't fit assumed value "
467 "(%" B_PRIdOFF
" blocks before indices)!\n", LogSize());
471 fSuperBlock
.magic3
= SUPER_BLOCK_MAGIC3
;
472 fSuperBlock
.root_dir
= rootDir
.inode_num
;
473 fSuperBlock
.indices
= indexDir
.inode_num
;
475 // calculate used blocks (from block bitmap)
478 if (fBitmap
.UsedBlocks())
479 fSuperBlock
.used_blocks
= fBitmap
.UsedBlocks();
481 fprintf(stderr
,"ERROR: couldn't calculate number of used blocks, marking all blocks as used!\n");
482 fSuperBlock
.used_blocks
= fSuperBlock
.num_blocks
;
491 Disk::DetermineBlockSize()
493 // scan for about 500 inodes to determine the disk's block size
495 // min. block bitmap size (in bytes, rounded to a 1024 boundary)
496 // root_block_size + (num_blocks / bits_per_block) * block_size
497 off_t offset
= 1024 + divide_roundup(fSize
/ 1024,8 * 1024) * 1024;
499 off_t inodesFound
= 0;
501 bfs_inode
*inode
= (bfs_inode
*)buffer
;
502 // valid block sizes from 1024 to 32768 bytes
503 int32 blockSizeCounter
[6] = {0};
504 status_t status
= B_OK
;
506 // read a quarter of the drive at maximum
507 for (; offset
< (fSize
>> 2); offset
+= 1024) {
508 if (fBufferedFile
->ReadAt(offset
, buffer
, sizeof(buffer
)) < B_OK
) {
509 fprintf(stderr
, "could not read from device (offset = %" B_PRIdOFF
510 ", size = %ld)!\n", offset
, sizeof(buffer
));
515 if (inode
->magic1
!= INODE_MAGIC1
516 || inode
->inode_size
!= 1024
517 && inode
->inode_size
!= 2048
518 && inode
->inode_size
!= 4096
519 && inode
->inode_size
!= 8192)
524 // update block size counter
525 for (int i
= 0;i
< 6;i
++)
526 if ((1 << (i
+ 10)) == inode
->inode_size
)
527 blockSizeCounter
[i
]++;
530 for (int32 i
= 0;i
< 6;i
++)
531 if (blockSizeCounter
[i
])
534 // do we have a clear winner at 100 inodes?
535 // if not, break at 500 inodes
536 if (inodesFound
>= MAX_BLOCK_SIZE_INODES
537 || (inodesFound
>= MIN_BLOCK_SIZE_INODES
&& count
== 1))
541 // find the safest bet
542 int32 maxCounter
= -1;
544 for (int32 i
= 0; i
< 6; i
++) {
545 if (maxCounter
< blockSizeCounter
[i
]) {
547 maxCounter
= blockSizeCounter
[i
];
550 fSuperBlock
.block_size
= (1 << (maxIndex
+ 10));
557 Disk::GetNextSpecialInode(char *buffer
, off_t
*_offset
, off_t end
,
558 bool skipAfterValidInode
= false)
560 off_t offset
= *_offset
;
564 bfs_inode
*inode
= (bfs_inode
*)buffer
;
566 for (; offset
< end
; offset
+= BlockSize()) {
567 if (fBufferedFile
->ReadAt(offset
, buffer
, 1024) < B_OK
) {
568 fprintf(stderr
,"could not read from device (offset = %" B_PRIdOFF
569 ", size = %d)!\n", offset
, 1024);
574 if (inode
->magic1
!= INODE_MAGIC1
575 || inode
->inode_size
!= fSuperBlock
.inode_size
)
578 // can compute allocation group only for inodes which are
579 // a) not in the first allocation group
580 // b) not in the log area
582 if (inode
->inode_num
.allocation_group
> 0
583 && offset
>= (BlockSize() * (fLogStart
+ LogSize()))) {
584 fValidBlockRun
= inode
->inode_num
;
585 fValidOffset
= offset
;
587 if (skipAfterValidInode
)
591 // is the inode a special root inode (parent == self)?
593 if (!memcmp(&inode
->parent
, &inode
->inode_num
, sizeof(inode_addr
))) {
594 printf("\t special inode found at %" B_PRIdOFF
"\n", offset
);
601 return B_ENTRY_NOT_FOUND
;
606 Disk::SaveInode(bfs_inode
*inode
, bool *indices
, bfs_inode
*indexDir
,
607 bool *root
, bfs_inode
*rootDir
)
609 if ((inode
->mode
& S_INDEX_DIR
) == 0) {
611 printf("\troot node found!\n");
612 if (inode
->inode_num
.allocation_group
!= 8
613 || inode
->inode_num
.start
!= 0
614 || inode
->inode_num
.length
!= 1) {
615 printf("WARNING: root node has unexpected position: (ag = %"
616 B_PRId32
", start = %d, length = %d)\n",
617 inode
->inode_num
.allocation_group
,
618 inode
->inode_num
.start
, inode
->inode_num
.length
);
621 memcpy(rootDir
, inode
, sizeof(bfs_inode
));
622 } else if (inode
->mode
& S_INDEX_DIR
) {
624 printf("\tindices node found!\n");
626 memcpy(indexDir
, inode
, sizeof(bfs_inode
));
628 // if (gDumpIndexRootNode)
629 // dump_inode(inode);
634 Disk::ScanForIndexAndRoot(bfs_inode
*indexDir
,bfs_inode
*rootDir
)
636 memset(rootDir
,0,sizeof(bfs_inode
));
637 memset(indexDir
,0,sizeof(bfs_inode
));
639 bool indices
= false,root
= false;
641 bfs_inode
*inode
= (bfs_inode
*)buffer
;
643 // The problem here is that we don't want to find copies of the
644 // inodes in the log.
645 // The first offset where a root node can be is therefore the
646 // first allocation group with a block size of 1024, and 16384
647 // blocks per ag; that should be relatively save.
649 // search for the indices inode, start reading from log size + boot block size
650 off_t offset
= (fLogStart
+ LogSize()) * BlockSize();
651 if (GetNextSpecialInode(buffer
,&offset
,offset
+ 65536LL * BlockSize()) == B_OK
)
652 SaveInode(inode
,&indices
,indexDir
,&root
,rootDir
);
655 fputs("ERROR: no indices node found!\n",stderr
);
659 // search common places for root node, iterating from allocation group
660 // size from 1024 to 65536
661 for (off_t start
= 8LL * 1024 * BlockSize();start
<= 8LL * 65536 * BlockSize();start
<<= 1) {
662 if (start
< fLogStart
)
665 off_t commonOffset
= start
;
666 if (GetNextSpecialInode(buffer
, &commonOffset
, commonOffset
) == B_OK
) {
667 SaveInode(inode
, &indices
, indexDir
, &root
, rootDir
);
674 printf("WARNING: Could not find root node at common places!\n");
675 printf("\tScanning log area for root node\n");
677 off_t logOffset
= ToOffset(fSuperBlock
.log_blocks
);
678 if (GetNextSpecialInode(buffer
,&logOffset
,logOffset
+ LogSize() * BlockSize()) == B_OK
)
680 SaveInode(inode
,&indices
,indexDir
,&root
,rootDir
);
682 printf("root node at: 0x%" B_PRIxOFF
" (DiskProbe)\n",
684 //fBufferedFile->ReadAt(logOffset + BlockSize(),buffer,1024);
685 //if (*(uint32 *)buffer == BPLUSTREE_MAGIC)
687 // puts("\t\tnext block in log contains a bplustree!");
695 printf("Should I perform a deeper search (that will take some time) (Y/N) [N]? ");
698 if (!strcasecmp("y",txt))
700 // search not so common places for the root node (all places)
703 offset += BlockSize(); // the block after the indices inode
705 offset = (fLogStart + 1) * BlockSize();
707 if (GetNextSpecialInode(buffer,&offset,65536LL * 16 * BlockSize()) == B_OK)
708 SaveInode(inode,&indices,indexDir,&root,rootDir);
718 // #pragma mark - BPositionIO methods
722 Disk::Read(void *buffer
, size_t size
)
724 return fBufferedFile
->Read(buffer
, size
);
729 Disk::Write(const void *buffer
, size_t size
)
731 return fBufferedFile
->Write(buffer
, size
);
736 Disk::ReadAt(off_t pos
, void *buffer
, size_t size
)
738 return fBufferedFile
->ReadAt(pos
+ fRawDiskOffset
, buffer
, size
);
743 Disk::WriteAt(off_t pos
, const void *buffer
, size_t size
)
745 return fBufferedFile
->WriteAt(pos
+ fRawDiskOffset
, buffer
, size
);
750 Disk::Seek(off_t position
, uint32 seekMode
)
752 // ToDo: only correct for seekMode == SEEK_SET, right??
753 if (seekMode
!= SEEK_SET
)
754 puts("OH NO, I AM BROKEN!");
755 return fBufferedFile
->Seek(position
+ fRawDiskOffset
, seekMode
);
760 Disk::Position() const
762 return fBufferedFile
->Position() - fRawDiskOffset
;
767 Disk::SetSize(off_t
/*size*/)
769 // SetSize() is not supported