19483Svlivanov/*
29483Svlivanov * Copyright (c) 2001-2008 pinc Software. All Rights Reserved.
39483Svlivanov */
49483Svlivanov
59483Svlivanov//!	Handles BFS superblock, disk access etc.
69483Svlivanov
79483Svlivanov#include "Disk.h"
89483Svlivanov#include "dump.h"
99483Svlivanov
109483Svlivanov#include <Drivers.h>
119483Svlivanov#include <File.h>
129483Svlivanov#include <Entry.h>
139483Svlivanov#include <List.h>
149483Svlivanov#include <fs_info.h>
159483Svlivanov
169483Svlivanov#include <stdlib.h>
179483Svlivanov#include <stdio.h>
189483Svlivanov#include <string.h>
199483Svlivanov#include <unistd.h>
209483Svlivanov#include <ctype.h>
219483Svlivanov
229483Svlivanov
239483Svlivanov#define MIN_BLOCK_SIZE_INODES 50
249483Svlivanov#define MAX_BLOCK_SIZE_INODES 500
259483Svlivanov
269483Svlivanov
279483Svlivanovstruct bfs_disk_info {
289483Svlivanov	off_t	offset;
299483Svlivanov	disk_super_block super_block;
309483Svlivanov};
319483Svlivanov
329483Svlivanov
339483Svlivanovclass CacheableBlockRun : public BlockRunCache::Cacheable {
349483Svlivanov	public:
359483Svlivanov		CacheableBlockRun(block_run run,uint8 *data)
369483Svlivanov			:
379483Svlivanov			fRun(run),
389483Svlivanov			fData(data)
399483Svlivanov		{
409483Svlivanov		}
419483Svlivanov
429483Svlivanov		virtual ~CacheableBlockRun()
439483Svlivanov		{
449483Svlivanov			free(fData);
459483Svlivanov		}
469483Svlivanov
479483Svlivanov		virtual bool Equals(block_run run)
489483Svlivanov		{
499483Svlivanov			return run == fRun;
509483Svlivanov		}
519483Svlivanov
529483Svlivanov		void SetData(uint8 *data)
539483Svlivanov		{
549483Svlivanov			fData = data;
559483Svlivanov		}
569483Svlivanov
579483Svlivanov		uint8 *Data()
589483Svlivanov		{
599483Svlivanov			return fData;
609483Svlivanov		}
619483Svlivanov
629483Svlivanov	protected:
639483Svlivanov		block_run	fRun;
649483Svlivanov		uint8		*fData;
659483Svlivanov};
669483Svlivanov
679483Svlivanov
689483SvlivanovBlockRunCache::BlockRunCache(Disk *disk)
699483Svlivanov	: Cache<block_run>(),
709483Svlivanov	fDisk(disk)
719483Svlivanov{
729483Svlivanov}
739483Svlivanov
749483Svlivanov
759483SvlivanovCache<block_run>::Cacheable *BlockRunCache::NewCacheable(block_run run)
7610312Svlivanov{
779483Svlivanov	ssize_t length = (int32)run.length << fDisk->BlockShift();
789483Svlivanov
799483Svlivanov	void *buffer = malloc(length);
809483Svlivanov	if (buffer == NULL)
819483Svlivanov		return NULL;
829483Svlivanov
839483Svlivanov	ssize_t read = fDisk->ReadAt(fDisk->ToOffset(run),buffer,length);
849483Svlivanov	if (read < length) {
859483Svlivanov		free(buffer);
869483Svlivanov		return NULL;
879483Svlivanov	}
889483Svlivanov
899483Svlivanov	return new CacheableBlockRun(run,(uint8 *)buffer);
909483Svlivanov}
919483Svlivanov
929483Svlivanov
939483Svlivanov//	#pragma mark -
949483Svlivanov
959483Svlivanov
969483SvlivanovDisk::Disk(const char *deviceName, bool rawMode, off_t start, off_t stop)
979483Svlivanov	:
989483Svlivanov	fBufferedFile(NULL),
999483Svlivanov	fRawDiskOffset(0),
1009483Svlivanov	fSize(0LL),
1019483Svlivanov	fCache(this),
1029483Svlivanov	fRawMode(rawMode)
1039483Svlivanov{
1049483Svlivanov	BEntry entry(deviceName);
1059483Svlivanov	fPath.SetTo(deviceName);
1069483Svlivanov
1079483Svlivanov	if (entry.IsDirectory()) {
1089483Svlivanov		dev_t on = dev_for_path(deviceName);
1099483Svlivanov		fs_info info;
1109483Svlivanov		if (fs_stat_dev(on, &info) != B_OK)
1119483Svlivanov			return;
1129483Svlivanov
1139483Svlivanov		fPath.SetTo(info.device_name);
1149483Svlivanov		deviceName = fPath.Path();
1159483Svlivanov	}
1169483Svlivanov
1179483Svlivanov	if (!rawMode && !strncmp(deviceName, "/dev/", 5)
1189483Svlivanov		&& !strcmp(deviceName + strlen(deviceName) - 3, "raw"))
1199483Svlivanov		fprintf(stderr, "Raw mode not selected, but raw device specified.\n");
1209483Svlivanov
1219483Svlivanov	if (fFile.SetTo(deviceName, B_READ_WRITE) < B_OK) {
1229483Svlivanov		//fprintf(stderr,"Could not open file: %s\n",strerror(fFile.InitCheck()));
1239483Svlivanov		return;
1249483Svlivanov	}
1259483Svlivanov	fBufferedFile = new BBufferIO(&fFile, 1024 * 1024, false);
1269483Svlivanov
1279483Svlivanov	int device = open(deviceName, O_RDONLY);
1289483Svlivanov	if (device < B_OK) {
1299483Svlivanov		//fprintf(stderr,"Could not open device\n");
1309483Svlivanov		return;
1319483Svlivanov	}
1329483Svlivanov
1339483Svlivanov	partition_info partitionInfo;
1349483Svlivanov	device_geometry geometry;
1359483Svlivanov	if (ioctl(device, B_GET_PARTITION_INFO, &partitionInfo,
1369483Svlivanov			sizeof(partition_info)) == 0) {
1379483Svlivanov		//if (gDumpPartitionInfo)
1389483Svlivanov		//	dump_partition_info(&partitionInfo);
1399483Svlivanov		fSize = partitionInfo.size;
1409483Svlivanov	} else if (ioctl(device, B_GET_GEOMETRY, &geometry, sizeof(device_geometry))
1419483Svlivanov			== 0) {
1429483Svlivanov		fSize = (off_t)geometry.cylinder_count * geometry.sectors_per_track
1439483Svlivanov				* geometry.head_count * geometry.bytes_per_sector;
1449483Svlivanov	} else {
1459483Svlivanov		fprintf(stderr,"Disk: Could not get partition info.\n  Use file size as partition size\n");
1469483Svlivanov		fFile.GetSize(&fSize);
1479483Svlivanov	}
1489483Svlivanov	close(device);
1499483Svlivanov
1509483Svlivanov	if (fSize == 0LL)
1519483Svlivanov		fprintf(stderr,"Disk: Invalid file size (%Ld bytes)!\n",fSize);
1529483Svlivanov
1539483Svlivanov	if (rawMode && ScanForSuperBlock(start, stop) < B_OK) {
1549483Svlivanov		fFile.Unset();
1559483Svlivanov		return;
1569483Svlivanov	}
1579483Svlivanov
1589483Svlivanov	if (fBufferedFile->ReadAt(512 + fRawDiskOffset, &fSuperBlock,
1599483Svlivanov			sizeof(disk_super_block)) < 1)
1609483Svlivanov		fprintf(stderr,"Disk: Could not read superblock\n");
1619483Svlivanov
1629483Svlivanov	//dump_super_block(&fSuperBlock);
1639483Svlivanov}
1649483Svlivanov
1659483Svlivanov
1669483SvlivanovDisk::~Disk()
1679483Svlivanov{
1689483Svlivanov	delete fBufferedFile;
1699483Svlivanov}
1709483Svlivanov
1719483Svlivanov
1729483Svlivanovstatus_t Disk::InitCheck()
1739483Svlivanov{
1749483Svlivanov	status_t status = fFile.InitCheck();
1759483Svlivanov	if (status == B_OK)
1769483Svlivanov		return fSize == 0LL ? B_ERROR : B_OK;
1779483Svlivanov
1789483Svlivanov	return status;
1799483Svlivanov}
1809483Svlivanov
1819483Svlivanov
1829483Svlivanovblock_run Disk::ToBlockRun(off_t start, int16 length) const
1839483Svlivanov{
1849483Svlivanov	block_run run;
1859483Svlivanov	run.allocation_group = start >> fSuperBlock.ag_shift;
1869483Svlivanov	run.start = start & ((1UL << fSuperBlock.ag_shift) - 1);
1879483Svlivanov	run.length = length;
1889483Svlivanov
1899483Svlivanov	return run;
1909483Svlivanov}
1919483Svlivanov
1929483Svlivanov
1939483Svlivanovoff_t Disk::LogSize() const
1949483Svlivanov{
1959483Svlivanov	if (fSuperBlock.num_blocks >= 4096)
1969483Svlivanov		return 2048;
1979483Svlivanov
1989483Svlivanov	return 512;
1999483Svlivanov}
2009483Svlivanov
2019483Svlivanov
2029483Svlivanovuint8 *Disk::ReadBlockRun(block_run run)
2039483Svlivanov{
2049483Svlivanov	CacheableBlockRun *entry = (CacheableBlockRun *)fCache.Get(run);
2059483Svlivanov	if (entry)
2069483Svlivanov		return entry->Data();
2079483Svlivanov
2089483Svlivanov	return NULL;
2099483Svlivanov}
2109483Svlivanov
2119483Svlivanov
2129483Svlivanovstatus_t
2139483SvlivanovDisk::DumpBootBlockToFile()
2149483Svlivanov{
2159483Svlivanov	BFile file("/boot/home/bootblock.old", B_READ_WRITE | B_CREATE_FILE);
2169483Svlivanov	if (file.InitCheck() < B_OK)
2179483Svlivanov		return file.InitCheck();
2189483Svlivanov
2199483Svlivanov	char buffer[BlockSize()];
2209483Svlivanov	ssize_t bytes = ReadAt(0, buffer, BlockSize());
2219509Svlivanov	if (bytes < (int32)BlockSize())
2229509Svlivanov		return bytes < B_OK ? bytes : B_ERROR;
2239509Svlivanov
2249509Svlivanov	file.Write(buffer, BlockSize());
2259509Svlivanov
2269509Svlivanov	// initialize buffer
2279509Svlivanov	memset(buffer, 0, BlockSize());
2289509Svlivanov	memcpy(buffer + 512, &fSuperBlock, sizeof(disk_super_block));
2299509Svlivanov
2309509Svlivanov	// write buffer to disk
2319509Svlivanov	WriteAt(0, buffer, BlockSize());
2329509Svlivanov
2339483Svlivanov	return B_OK;
2349483Svlivanov}
2359483Svlivanov
2369483Svlivanov
2379483Svlivanov//	#pragma mark - Superblock recovery methods
2389483Svlivanov
2399483Svlivanov
2409483Svlivanovstatus_t
2419483SvlivanovDisk::ScanForSuperBlock(off_t start, off_t stop)
2429483Svlivanov{
2439483Svlivanov	printf("Disk size %Ld bytes, %.2f GB\n", fSize, 1.0 * fSize / (1024*1024*1024));
2449483Svlivanov
2459483Svlivanov	uint32 blockSize = 4096;
2469483Svlivanov	char buffer[blockSize + 1024];
2479483Svlivanov
2489483Svlivanov	if (stop == -1)
2499483Svlivanov		stop = fSize;
2509483Svlivanov
2519483Svlivanov	char escape[3] = {0x1b, '[', 0};
2529483Svlivanov
2539483Svlivanov	BList superBlocks;
2549483Svlivanov
2559483Svlivanov	printf("Scanning Disk from %Ld to %Ld\n", start, stop);
2569483Svlivanov
2579483Svlivanov	for (off_t offset = start; offset < stop; offset += blockSize)
2589483Svlivanov	{
2599483Svlivanov		if (((offset-start) % (blockSize * 100)) == 0)
2609483Svlivanov			printf("  %12Ld, %.2f GB     %s1A\n",offset,1.0 * offset / (1024*1024*1024),escape);
2619483Svlivanov
2629483Svlivanov		ssize_t bytes = fBufferedFile->ReadAt(offset, buffer, blockSize + 1024);
2639483Svlivanov		if (bytes < B_OK)
2649483Svlivanov		{
2659483Svlivanov			fprintf(stderr,"Could not read from device: %s\n", strerror(bytes));
2669483Svlivanov			return -1;
2679483Svlivanov		}
2689483Svlivanov
2699483Svlivanov		for (uint32 i = 0;i < blockSize - 2;i++)
2709483Svlivanov		{
2719483Svlivanov			disk_super_block *super = (disk_super_block *)&buffer[i];
2729483Svlivanov
2739483Svlivanov			if (super->magic1 == (int32)SUPER_BLOCK_MAGIC1
2749483Svlivanov				&& 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