1/* 2 * Copyright (c) 2001-2008 pinc Software. All Rights Reserved. 3 */ 4 5//! Handles BFS superblock, disk access etc. 6 7#include "Disk.h" 8#include "dump.h" 9 10#include <Drivers.h> 11#include <File.h> 12#include <Entry.h> 13#include <List.h> 14#include <fs_info.h> 15 16#include <stdlib.h> 17#include <stdio.h> 18#include <string.h> 19#include <unistd.h> 20#include <ctype.h> 21 22 23#define MIN_BLOCK_SIZE_INODES 50 24#define MAX_BLOCK_SIZE_INODES 500 25 26 27struct bfs_disk_info { 28 off_t offset; 29 disk_super_block super_block; 30}; 31 32 33class CacheableBlockRun : public BlockRunCache::Cacheable { 34 public: 35 CacheableBlockRun(block_run run,uint8 *data) 36 : 37 fRun(run), 38 fData(data) 39 { 40 } 41 42 virtual ~CacheableBlockRun() 43 { 44 free(fData); 45 } 46 47 virtual bool Equals(block_run run) 48 { 49 return run == fRun; 50 } 51 52 void SetData(uint8 *data) 53 { 54 fData = data; 55 } 56 57 uint8 *Data() 58 { 59 return fData; 60 } 61 62 protected: 63 block_run fRun; 64 uint8 *fData; 65}; 66 67 68BlockRunCache::BlockRunCache(Disk *disk) 69 : Cache<block_run>(), 70 fDisk(disk) 71{ 72} 73 74 75Cache<block_run>::Cacheable *BlockRunCache::NewCacheable(block_run run) 76{ 77 ssize_t length = (int32)run.length << fDisk->BlockShift(); 78 79 void *buffer = malloc(length); 80 if (buffer == NULL) 81 return NULL; 82 83 ssize_t read = fDisk->ReadAt(fDisk->ToOffset(run),buffer,length); 84 if (read < length) { 85 free(buffer); 86 return NULL; 87 } 88 89 return new CacheableBlockRun(run,(uint8 *)buffer); 90} 91 92 93// #pragma mark - 94 95 96Disk::Disk(const char *deviceName, bool rawMode, off_t start, off_t stop) 97 : 98 fBufferedFile(NULL), 99 fRawDiskOffset(0), 100 fSize(0LL), 101 fCache(this), 102 fRawMode(rawMode) 103{ 104 BEntry entry(deviceName); 105 fPath.SetTo(deviceName); 106 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; 112 113 fPath.SetTo(info.device_name); 114 deviceName = fPath.Path(); 115 } 116 117 if (!rawMode && !strncmp(deviceName, "/dev/", 5) 118 && !strcmp(deviceName + strlen(deviceName) - 3, "raw")) 119 fprintf(stderr, "Raw mode not selected, but raw device specified.\n"); 120 121 if (fFile.SetTo(deviceName, B_READ_WRITE) < B_OK) { 122 //fprintf(stderr,"Could not open file: %s\n",strerror(fFile.InitCheck())); 123 return; 124 } 125 fBufferedFile = new BBufferIO(&fFile, 1024 * 1024, false); 126 127 int device = open(deviceName, O_RDONLY); 128 if (device < B_OK) { 129 //fprintf(stderr,"Could not open device\n"); 130 return; 131 } 132 133 partition_info partitionInfo; 134 device_geometry geometry; 135 if (ioctl(device, B_GET_PARTITION_INFO, &partitionInfo, 136 sizeof(partition_info)) == 0) { 137 //if (gDumpPartitionInfo) 138 // dump_partition_info(&partitionInfo); 139 fSize = partitionInfo.size; 140 } else if (ioctl(device, B_GET_GEOMETRY, &geometry, sizeof(device_geometry)) 141 == 0) { 142 fSize = (off_t)geometry.cylinder_count * geometry.sectors_per_track 143 * geometry.head_count * geometry.bytes_per_sector; 144 } else { 145 fprintf(stderr,"Disk: Could not get partition info.\n Use file size as partition size\n"); 146 fFile.GetSize(&fSize); 147 } 148 close(device); 149 150 if (fSize == 0LL) 151 fprintf(stderr,"Disk: Invalid file size (%Ld bytes)!\n",fSize); 152 153 if (rawMode && ScanForSuperBlock(start, stop) < B_OK) { 154 fFile.Unset(); 155 return; 156 } 157 158 if (fBufferedFile->ReadAt(512 + fRawDiskOffset, &fSuperBlock, 159 sizeof(disk_super_block)) < 1) 160 fprintf(stderr,"Disk: Could not read superblock\n"); 161 162 //dump_super_block(&fSuperBlock); 163} 164 165 166Disk::~Disk() 167{ 168 delete fBufferedFile; 169} 170 171 172status_t Disk::InitCheck() 173{ 174 status_t status = fFile.InitCheck(); 175 if (status == B_OK) 176 return fSize == 0LL ? B_ERROR : B_OK; 177 178 return status; 179} 180 181 182block_run Disk::ToBlockRun(off_t start, int16 length) const 183{ 184 block_run run; 185 run.allocation_group = start >> fSuperBlock.ag_shift; 186 run.start = start & ((1UL << fSuperBlock.ag_shift) - 1); 187 run.length = length; 188 189 return run; 190} 191 192 193off_t Disk::LogSize() const 194{ 195 if (fSuperBlock.num_blocks >= 4096) 196 return 2048; 197 198 return 512; 199} 200 201 202uint8 *Disk::ReadBlockRun(block_run run) 203{ 204 CacheableBlockRun *entry = (CacheableBlockRun *)fCache.Get(run); 205 if (entry) 206 return entry->Data(); 207 208 return NULL; 209} 210 211 212status_t 213Disk::DumpBootBlockToFile() 214{ 215 BFile file("/boot/home/bootblock.old", B_READ_WRITE | B_CREATE_FILE); 216 if (file.InitCheck() < B_OK) 217 return file.InitCheck(); 218 219 char buffer[BlockSize()]; 220 ssize_t bytes = ReadAt(0, buffer, BlockSize()); 221 if (bytes < (int32)BlockSize()) 222 return bytes < B_OK ? bytes : B_ERROR; 223 224 file.Write(buffer, BlockSize()); 225 226 // initialize buffer 227 memset(buffer, 0, BlockSize()); 228 memcpy(buffer + 512, &fSuperBlock, sizeof(disk_super_block)); 229 230 // write buffer to disk 231 WriteAt(0, buffer, BlockSize()); 232 233 return B_OK; 234} 235 236 237// #pragma mark - Superblock recovery methods 238 239 240status_t 241Disk::ScanForSuperBlock(off_t start, off_t stop) 242{ 243 printf("Disk size %Ld bytes, %.2f GB\n", fSize, 1.0 * fSize / (1024*1024*1024)); 244 245 uint32 blockSize = 4096; 246 char buffer[blockSize + 1024]; 247 248 if (stop == -1) 249 stop = fSize; 250 251 char escape[3] = {0x1b, '[', 0}; 252 253 BList superBlocks; 254 255 printf("Scanning Disk from %Ld to %Ld\n", start, stop); 256 257 for (off_t offset = start; offset < stop; offset += blockSize) 258 { 259 if (((offset-start) % (blockSize * 100)) == 0) 260 printf(" %12Ld, %.2f GB %s1A\n",offset,1.0 * offset / (1024*1024*1024),escape); 261 262 ssize_t bytes = fBufferedFile->ReadAt(offset, buffer, blockSize + 1024); 263 if (bytes < B_OK) 264 { 265 fprintf(stderr,"Could not read from device: %s\n", strerror(bytes)); 266 return -1; 267 } 268 269 for (uint32 i = 0;i < blockSize - 2;i++) 270 { 271 disk_super_block *super = (disk_super_block *)&buffer[i]; 272 273 if (super->magic1 == (int32)SUPER_BLOCK_MAGIC1 274 && super->magic2 == (int32)SUPER_BLOCK_MAGIC2 275 && super->magic3 == (int32)SUPER_BLOCK_MAGIC3) 276 { 277 printf("\n(%ld) *** BFS superblock found at: %Ld\n",superBlocks.CountItems() + 1,offset); 278 dump_super_block(super); 279 280 // add a copy of the superblock to the list 281 bfs_disk_info *info = (bfs_disk_info *)malloc(sizeof(bfs_disk_info)); 282 if (info == NULL) 283 return B_NO_MEMORY; 284 285 memcpy(&info->super_block, super, sizeof(disk_super_block)); 286 info->offset = offset + i - 512; 287 /* location off the BFS superblock is 512 bytes after the partition start */ 288 superBlocks.AddItem(info); 289 } 290 } 291 } 292 293 if (superBlocks.CountItems() == 0) { 294 puts("\nCouldn't find any BFS superblocks!"); 295 return B_ENTRY_NOT_FOUND; 296 } 297 298 // Let the user decide which block to use 299 300 puts("\n\nThe following disks were found:"); 301 302 for (int32 i = 0; i < superBlocks.CountItems(); i++) { 303 bfs_disk_info *info = (bfs_disk_info *)superBlocks.ItemAt(i); 304 305 printf("%ld) %s, offset %Ld, size %g GB (%svalid)\n", i + 1, 306 info->super_block.name, info->offset, 307 1.0 * info->super_block.num_blocks * info->super_block.block_size / (1024*1024*1024), 308 ValidateSuperBlock(info->super_block) < B_OK ? "in" : ""); 309 } 310 311 char answer[16]; 312 printf("\nChoose one (by number): "); 313 fflush(stdout); 314 315 fgets(answer, 15, stdin); 316 int32 index = atol(answer); 317 318 if (index > superBlocks.CountItems() || index < 1) { 319 puts("No disk there... exiting."); 320 return B_BAD_VALUE; 321 } 322 323 bfs_disk_info *info = (bfs_disk_info *)superBlocks.ItemAt(index - 1); 324 325 // ToDo: free the other disk infos 326 327 fRawDiskOffset = info->offset; 328 fBufferedFile->Seek(fRawDiskOffset, SEEK_SET); 329 330 if (ValidateSuperBlock(info->super_block)) 331 fSize = info->super_block.block_size * info->super_block.block_size; 332 else { 333 // just make it open-end 334 fSize -= fRawDiskOffset; 335 } 336 337 return B_OK; 338} 339 340 341status_t 342Disk::ValidateSuperBlock(disk_super_block &superBlock) 343{ 344 if (superBlock.magic1 != (int32)SUPER_BLOCK_MAGIC1 345 || superBlock.magic2 != (int32)SUPER_BLOCK_MAGIC2 346 || superBlock.magic3 != (int32)SUPER_BLOCK_MAGIC3 347 || (int32)superBlock.block_size != superBlock.inode_size 348 || superBlock.fs_byte_order != SUPER_BLOCK_FS_LENDIAN 349 || (1UL << superBlock.block_shift) != superBlock.block_size 350 || superBlock.num_ags < 1 351 || superBlock.ag_shift < 1 352 || superBlock.blocks_per_ag < 1 353 || superBlock.num_blocks < 10 354 || superBlock.used_blocks > superBlock.num_blocks 355 || superBlock.num_ags != divide_roundup(superBlock.num_blocks, 356 1L << superBlock.ag_shift)) 357 return B_ERROR; 358 359 return B_OK; 360} 361 362 363status_t 364Disk::ValidateSuperBlock() 365{ 366 if (ValidateSuperBlock(fSuperBlock) < B_OK) 367 return B_ERROR; 368 369 fBitmap.SetTo(this); 370 371 return B_OK; 372} 373 374 375status_t 376Disk::RecreateSuperBlock() 377{ 378 memset(&fSuperBlock,0,sizeof(disk_super_block)); 379 380 puts("\n*** Determine block size"); 381 382 status_t status = DetermineBlockSize(); 383 if (status < B_OK) 384 return status; 385 386 printf("\tblock size = %ld\n",BlockSize()); 387 388 strcpy(fSuperBlock.name,"recovered"); 389 fSuperBlock.magic1 = SUPER_BLOCK_MAGIC1; 390 fSuperBlock.fs_byte_order = SUPER_BLOCK_FS_LENDIAN; 391 fSuperBlock.block_shift = get_shift(BlockSize()); 392 fSuperBlock.num_blocks = fSize / BlockSize(); // only full blocks 393 fSuperBlock.inode_size = BlockSize(); 394 fSuperBlock.magic2 = SUPER_BLOCK_MAGIC2; 395 396 fSuperBlock.flags = SUPER_BLOCK_CLEAN; 397 398 // size of the block bitmap + root block 399 fLogStart = 1 + divide_roundup(fSuperBlock.num_blocks,BlockSize() * 8); 400 401 // set it anywhere in the log 402 fSuperBlock.log_start = fLogStart + (LogSize() >> 1); 403 fSuperBlock.log_end = fSuperBlock.log_start; 404 405 // 406 // scan for indices and root inode 407 // 408 409 puts("\n*** Scanning for indices and root node..."); 410 fValidOffset = 0LL; 411 bfs_inode indexDir; 412 bfs_inode rootDir; 413 if (ScanForIndexAndRoot(&indexDir,&rootDir) < B_OK) { 414 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"); 415 rootDir.inode_num.allocation_group = 8; 416 rootDir.inode_num.start = 0; 417 rootDir.inode_num.length = 1; 418 //gErrors++; 419 } 420 if (fValidOffset == 0LL) { 421 printf("No valid inode found so far - perform search.\n"); 422 423 off_t offset = 8LL * 65536 * BlockSize(); 424 char buffer[1024]; 425 GetNextSpecialInode(buffer,&offset,offset + 32LL * 65536 * BlockSize(),true); 426 427 if (fValidOffset == 0LL) 428 { 429 fprintf(stderr,"FATAL ERROR: Could not find valid inode!\n"); 430 return B_ERROR; 431 } 432 } 433 434 // calculate allocation group size 435 436 int32 allocationGroupSize = (fValidOffset - fValidBlockRun.start 437 * BlockSize() 438 + BlockSize() - 1) / (BlockSize() * fValidBlockRun.allocation_group); 439 440 fSuperBlock.blocks_per_ag = allocationGroupSize / (BlockSize() << 3); 441 fSuperBlock.ag_shift = get_shift(allocationGroupSize); 442 fSuperBlock.num_ags = divide_roundup(fSuperBlock.num_blocks,allocationGroupSize); 443 444 // calculate rest of log area 445 446 fSuperBlock.log_blocks.allocation_group = fLogStart / allocationGroupSize; 447 fSuperBlock.log_blocks.start = fLogStart - fSuperBlock.log_blocks.allocation_group * allocationGroupSize; 448 fSuperBlock.log_blocks.length = LogSize(); // assumed length of 2048 blocks 449 450 if (fLogStart != ((indexDir.inode_num.allocation_group 451 << fSuperBlock.ag_shift) + indexDir.inode_num.start - LogSize())) { 452 fprintf(stderr,"ERROR: start of logging area doesn't fit assumed value " 453 "(%Ld blocks before indices)!\n", LogSize()); 454 //gErrors++; 455 } 456 457 fSuperBlock.magic3 = SUPER_BLOCK_MAGIC3; 458 fSuperBlock.root_dir = rootDir.inode_num; 459 fSuperBlock.indices = indexDir.inode_num; 460 461 // calculate used blocks (from block bitmap) 462 463 fBitmap.SetTo(this); 464 if (fBitmap.UsedBlocks()) 465 fSuperBlock.used_blocks = fBitmap.UsedBlocks(); 466 else { 467 fprintf(stderr,"ERROR: couldn't calculate number of used blocks, marking all blocks as used!\n"); 468 fSuperBlock.used_blocks = fSuperBlock.num_blocks; 469 //gErrors++; 470 } 471 472 return B_OK; 473} 474 475 476status_t 477Disk::DetermineBlockSize() 478{ 479 // scan for about 500 inodes to determine the disk's block size 480 481 // min. block bitmap size (in bytes, rounded to a 1024 boundary) 482 // root_block_size + (num_blocks / bits_per_block) * block_size 483 off_t offset = 1024 + divide_roundup(fSize / 1024,8 * 1024) * 1024; 484 485 off_t inodesFound = 0; 486 char buffer[1024]; 487 bfs_inode *inode = (bfs_inode *)buffer; 488 // valid block sizes from 1024 to 32768 bytes 489 int32 blockSizeCounter[6] = {0}; 490 status_t status = B_OK; 491 492 // read a quarter of the drive at maximum 493 for (; offset < (fSize >> 2); offset += 1024) { 494 if (fBufferedFile->ReadAt(offset, buffer, sizeof(buffer)) < B_OK) { 495 fprintf(stderr, "could not read from device (offset = %Ld, " 496 "size = %ld)!\n", offset, sizeof(buffer)); 497 status = B_IO_ERROR; 498 break; 499 } 500 501 if (inode->magic1 != INODE_MAGIC1 502 || inode->inode_size != 1024 503 && inode->inode_size != 2048 504 && inode->inode_size != 4096 505 && inode->inode_size != 8192) 506 continue; 507 508 inodesFound++; 509 510 // update block size counter 511 for (int i = 0;i < 6;i++) 512 if ((1 << (i + 10)) == inode->inode_size) 513 blockSizeCounter[i]++; 514 515 int32 count = 0; 516 for (int32 i = 0;i < 6;i++) 517 if (blockSizeCounter[i]) 518 count++; 519 520 // do we have a clear winner at 100 inodes? 521 // if not, break at 500 inodes 522 if (inodesFound >= MAX_BLOCK_SIZE_INODES 523 || (inodesFound >= MIN_BLOCK_SIZE_INODES && count == 1)) 524 break; 525 } 526 527 // find the safest bet 528 int32 maxCounter = -1; 529 int32 maxIndex = 0; 530 for (int32 i = 0; i < 6; i++) { 531 if (maxCounter < blockSizeCounter[i]) { 532 maxIndex = i; 533 maxCounter = blockSizeCounter[i]; 534 } 535 } 536 fSuperBlock.block_size = (1 << (maxIndex + 10)); 537 538 return status; 539} 540 541 542status_t 543Disk::GetNextSpecialInode(char *buffer, off_t *_offset, off_t end, 544 bool skipAfterValidInode = false) 545{ 546 off_t offset = *_offset; 547 if (end == offset) 548 end++; 549 550 bfs_inode *inode = (bfs_inode *)buffer; 551 552 for (; offset < end; offset += BlockSize()) { 553 if (fBufferedFile->ReadAt(offset, buffer, 1024) < B_OK) { 554 fprintf(stderr,"could not read from device (offset = %Ld, size = %d)!\n",offset,1024); 555 *_offset = offset; 556 return B_IO_ERROR; 557 } 558 559 if (inode->magic1 != INODE_MAGIC1 560 || inode->inode_size != fSuperBlock.inode_size) 561 continue; 562 563 // can compute allocation group only for inodes which are 564 // a) not in the first allocation group 565 // b) not in the log area 566 567 if (inode->inode_num.allocation_group > 0 568 && offset >= (BlockSize() * (fLogStart + LogSize()))) { 569 fValidBlockRun = inode->inode_num; 570 fValidOffset = offset; 571 572 if (skipAfterValidInode) 573 return B_OK; 574 } 575 576 // is the inode a special root inode (parent == self)? 577 578 if (!memcmp(&inode->parent, &inode->inode_num, sizeof(inode_addr))) { 579 printf("\t special inode found at %Ld\n", offset); 580 581 *_offset = offset; 582 return B_OK; 583 } 584 } 585 *_offset = offset; 586 return B_ENTRY_NOT_FOUND; 587} 588 589 590void 591Disk::SaveInode(bfs_inode *inode, bool *indices, bfs_inode *indexDir, 592 bool *root, bfs_inode *rootDir) 593{ 594 if ((inode->mode & S_INDEX_DIR) == 0) { 595 *root = true; 596 printf("\troot node found!\n"); 597 if (inode->inode_num.allocation_group != 8 598 || inode->inode_num.start != 0 599 || inode->inode_num.length != 1) 600 printf("WARNING: root node has unexpected position: (ag = %ld, " 601 "start = %d, length = %d)\n", inode->inode_num.allocation_group, 602 inode->inode_num.start, inode->inode_num.length); 603 if (rootDir) 604 memcpy(rootDir, inode, sizeof(bfs_inode)); 605 } else if (inode->mode & S_INDEX_DIR) { 606 *indices = true; 607 printf("\tindices node found!\n"); 608 if (indexDir) 609 memcpy(indexDir, inode, sizeof(bfs_inode)); 610 } 611// if (gDumpIndexRootNode) 612// dump_inode(inode); 613} 614 615 616status_t 617Disk::ScanForIndexAndRoot(bfs_inode *indexDir,bfs_inode *rootDir) 618{ 619 memset(rootDir,0,sizeof(bfs_inode)); 620 memset(indexDir,0,sizeof(bfs_inode)); 621 622 bool indices = false,root = false; 623 char buffer[1024]; 624 bfs_inode *inode = (bfs_inode *)buffer; 625 626 // The problem here is that we don't want to find copies of the 627 // inodes in the log. 628 // The first offset where a root node can be is therefore the 629 // first allocation group with a block size of 1024, and 16384 630 // blocks per ag; that should be relatively save. 631 632 // search for the indices inode, start reading from log size + boot block size 633 off_t offset = (fLogStart + LogSize()) * BlockSize(); 634 if (GetNextSpecialInode(buffer,&offset,offset + 65536LL * BlockSize()) == B_OK) 635 SaveInode(inode,&indices,indexDir,&root,rootDir); 636 637 if (!indices) { 638 fputs("ERROR: no indices node found!\n",stderr); 639 //gErrors++; 640 } 641 642 // search common places for root node, iterating from allocation group 643 // size from 1024 to 65536 644 for (off_t start = 8LL * 1024 * BlockSize();start <= 8LL * 65536 * BlockSize();start <<= 1) { 645 if (start < fLogStart) 646 continue; 647 648 off_t commonOffset = start; 649 if (GetNextSpecialInode(buffer, &commonOffset, commonOffset) == B_OK) { 650 SaveInode(inode, &indices, indexDir, &root, rootDir); 651 if (root) 652 break; 653 } 654 } 655 656 if (!root) { 657 printf("WARNING: Could not find root node at common places!\n"); 658 printf("\tScanning log area for root node\n"); 659 660 off_t logOffset = ToOffset(fSuperBlock.log_blocks); 661 if (GetNextSpecialInode(buffer,&logOffset,logOffset + LogSize() * BlockSize()) == B_OK) 662 { 663 SaveInode(inode,&indices,indexDir,&root,rootDir); 664 665 printf("root node at: 0x%Lx (DiskProbe)\n",logOffset / 512); 666 //fBufferedFile->ReadAt(logOffset + BlockSize(),buffer,1024); 667 //if (*(uint32 *)buffer == BPLUSTREE_MAGIC) 668 //{ 669 // puts("\t\tnext block in log contains a bplustree!"); 670 //} 671 } 672 } 673 674 /*if (!root) 675 { 676 char txt[64]; 677 printf("Should I perform a deeper search (that will take some time) (Y/N) [N]? "); 678 gets(txt); 679 680 if (!strcasecmp("y",txt)) 681 { 682 // search not so common places for the root node (all places) 683 684 if (indices) 685 offset += BlockSize(); // the block after the indices inode 686 else 687 offset = (fLogStart + 1) * BlockSize(); 688 689 if (GetNextSpecialInode(buffer,&offset,65536LL * 16 * BlockSize()) == B_OK) 690 SaveInode(inode,&indices,indexDir,&root,rootDir); 691 } 692 }*/ 693 if (root) 694 return B_OK; 695 696 return B_ERROR; 697} 698 699 700// #pragma mark - BPositionIO methods 701 702 703ssize_t 704Disk::Read(void *buffer, size_t size) 705{ 706 return fBufferedFile->Read(buffer, size); 707} 708 709 710ssize_t 711Disk::Write(const void *buffer, size_t size) 712{ 713 return fBufferedFile->Write(buffer, size); 714} 715 716 717ssize_t 718Disk::ReadAt(off_t pos, void *buffer, size_t size) 719{ 720 return fBufferedFile->ReadAt(pos + fRawDiskOffset, buffer, size); 721} 722 723 724ssize_t 725Disk::WriteAt(off_t pos, const void *buffer, size_t size) 726{ 727 return fBufferedFile->WriteAt(pos + fRawDiskOffset, buffer, size); 728} 729 730 731off_t 732Disk::Seek(off_t position, uint32 seekMode) 733{ 734 // ToDo: only correct for seekMode == SEEK_SET, right?? 735 if (seekMode != SEEK_SET) 736 puts("OH NO, I AM BROKEN!"); 737 return fBufferedFile->Seek(position + fRawDiskOffset, seekMode); 738} 739 740 741off_t 742Disk::Position() const 743{ 744 return fBufferedFile->Position() - fRawDiskOffset; 745} 746 747 748status_t 749Disk::SetSize(off_t /*size*/) 750{ 751 // SetSize() is not supported 752 return B_ERROR; 753} 754 755