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