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