#include "precomp.h"

#include "reiserfs.h"

#include "string.h"

#include "assert.h"

#include "pdrivefile.h"

#include "time.h"


#define MAXLOOPTRY 10245


#ifndef ZeroMemory

#define ZeroMemory(Destination,Length) memset((Destination),0,(Length))

#endif


#define SIZE_OF_LAYOUT_BLOCK 20240


class LayoutBlock
    {
    public:

        LayoutBlock()
        {
            m_lpbData = new BYTE[SIZE_OF_LAYOUT_BLOCK];
        }
        bool isValid()
        {
            return (m_lpbData != NULL);
        }

        int getSize()
        {
            return SIZE_OF_LAYOUT_BLOCK;
        }

        LPBYTE getData()
        {
            return m_lpbData;
        }

        virtual ~LayoutBlock()
        {
            delete [] m_lpbData;
        }
    protected:
        int m_nSize;
        LPBYTE m_lpbData;
    };

DWORD dwTypeDirect = (DWORD) -1;
DWORD dwTypeIndirect = (DWORD) -2;
#ifdef _WIN32

void FileTimeFromUnixTime(FILETIME* pf, U32 unixtime )
{
    struct tm* t = localtime((const time_t*) &unixtime );
    ZeroMemory( pf, sizeof(FILETIME) );
    if( t )
    {
        SYSTEMTIME st;
        st.wYear = (WORD) t->tm_year + 1900;
        st.wMonth = (WORD) t->tm_mon + 1;
        st.wDayOfWeek = (WORD) t->tm_wday;
        st.wDay = (WORD) t->tm_mday;
        st.wHour = (WORD) t->tm_hour;
        st.wMinute = (WORD) t->tm_min;
        st.wSecond = (WORD) t->tm_sec;
        st.wMilliseconds = (WORD) 0;
        SystemTimeToFileTime(&st, pf);
    }
}
#endif


void SetUnixFileTime( LPCSTR lpszPath, ReiserFsFileInfo* pFile )
{
#ifdef _WIN32

    FILETIME f_atime, f_mtime, f_ctime;

    FileTimeFromUnixTime(&f_atime, pFile->m_stat.sd_atime );
    FileTimeFromUnixTime(&f_mtime, pFile->m_stat.sd_mtime );
    FileTimeFromUnixTime(&f_ctime, pFile->m_stat.sd_ctime );

    HANDLE hFile = CreateFile( lpszPath, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
    if( hFile != INVALID_HANDLE_VALUE )
    {
        SetFileTime(hFile, &f_ctime, &f_atime, &f_mtime );
        CloseHandle(hFile);
    } 
#endif

}
ReiserFsFileInfo::ReiserFsFileInfo(REISERFS_DIRECTORY_HEAD* pDH, LPCSTR lpszName)
{
	DEBUGTRACE(("FOUND FILE %s\n", lpszName))

    m_deh = *pDH;
    m_strName = lpszName;
    memset(&m_stat,0,sizeof(m_stat));

    // rfs does not store file info for special dirs "." and "..", so simulate this here...

    if( (stricmp(lpszName, ".") == 0) ||
        (stricmp(lpszName, "..") == 0) )
    {
        m_stat.sd_mode |= S_IFDIR;
    }

}

int comp_keys(REISERFS_CPU_KEY* a, REISERFS_CPU_KEY *b)
{
    if( a->k_dir_id < b->k_dir_id )
        return -1;
    if( a->k_dir_id > b->k_dir_id )
        return 1;
    if( a->k_objectid < b->k_objectid )
        return -1;
    if( a->k_objectid > b->k_objectid )
        return 1;
    if( a->k_offset < b->k_offset )
        return -1;
    if( a->k_offset > b->k_offset )
        return 1;
    if( a->k_type < b->k_type )
        return -1;
    if( a->k_type > b->k_type )
        return 1;
	return 0;
}

int comp_keys_no_offset (REISERFS_CPU_KEY * le_key, REISERFS_CPU_KEY * cpu_key)
{
  if( le_key->k_dir_id < cpu_key->k_dir_id )
	return -1;
  if( le_key->k_dir_id > cpu_key->k_dir_id )
	return 1;
  if( le_key->k_objectid < cpu_key->k_objectid )
	return -1;
  if( le_key->k_objectid > cpu_key->k_objectid )
	return 1;
  if (le_key->k_type < cpu_key->k_type)
    return -1;
  if (le_key->k_type > cpu_key->k_type)
      return 1;
	return 0;
}

ReiserFsPartition::ReiserFsPartition()
{
    m_dwBlockSize = 4096;
	m_pDrive = CreatePhysicalDriveInstance();
} // ReiserFsPartition()


ReiserFsPartition::~ReiserFsPartition()
{
	delete m_pDrive;
} // ~ReiserFsPartition()


BOOL ReiserFsPartition::Read( LPBYTE lpbMemory, DWORD dwSize, INT64 BlockNumber )
{
	INT64 offset = m_sb.s_blocksize;
	offset *= BlockNumber;
	offset += m_PartitionStartingOffset;
	return m_pDrive->ReadAbsolute( lpbMemory, dwSize, offset );
} // Read()


bool ReiserFsPartition::CheckReiserFsPartition()
{
    INT64 diskoffset = (m_PartitionStartingOffset + REISERFS_DISK_OFFSET_IN_BYTES);

    BYTE* lpbMemory = new BYTE[ (DWORD)( m_BytesPerSector*2 ) ];
    if( !m_pDrive->ReadAbsolute(lpbMemory, (DWORD) m_BytesPerSector, diskoffset ) )
    {
        printf("ERROR %s, unable to read ReiserFS superblock\n", (LPCSTR) GetLastErrorString() ); 
        return false;
    }

    LPREISERFS_SUPER_BLOCK p = (LPREISERFS_SUPER_BLOCK) lpbMemory;
    if( stricmp(p->s_magic,REISERFS_SUPER_MAGIC_STRING) )
    {
        if( stricmp(p->s_magic,REISER2FS_SUPER_MAGIC_STRING) )
        {
            if( stricmp(p->s_magic,REISER3FS_SUPER_MAGIC_STRING) )
            {
                return false;
            }
        }
    }
    return true;
}

void ReiserFsPartition::AutodetectFirstUsable( int* piPartition, int* piDrive )
{
    DISK_GEOMETRY dg;
    LayoutBlock layout;

	printf("No drives specified, performing an autodetect...\n" );

    for( int iDrive = 0; iDrive < 10; iDrive++ )
    {
        if( !m_pDrive->Open(iDrive) ) 
            continue;

        printf("Testing drive %d\n", iDrive );

#ifdef SUPPORT_WINDOWS_XP_PARTITIONS

        if( m_pDrive->GetDriveGeometryEx( (DISK_GEOMETRY_EX*) layout.getData(), layout.getSize() ) )
        {
            DISK_GEOMETRY_EX* pDG = (DISK_GEOMETRY_EX*) layout.getData();
            m_BytesPerSector = pDG->Geometry.BytesPerSector;
        }
        else 
#endif

        if( m_pDrive->GetDriveGeometry(&dg) )
        {
            m_BytesPerSector = dg.BytesPerSector;
        }
        else
        {
            printf("ERROR %s, unable to get drive geometry from this drive\n", (LPCSTR) GetLastErrorString() ); 
            continue;
        }
    
        memset( layout.getData(), 0, layout.getSize() );

#ifdef SUPPORT_WINDOWS_XP_PARTITIONS

        if( m_pDrive->GetDriveLayoutEx(layout.getData(), layout.getSize()) )
        {
            PDRIVE_LAYOUT_INFORMATION_EX pLI = (PDRIVE_LAYOUT_INFORMATION_EX)layout.getData();

            for( DWORD iPartition = 0; iPartition < pLI->PartitionCount; iPartition++ )
            {
                m_PartitionStartingOffset = pLI->PartitionEntry[iPartition].StartingOffset.QuadPart;
                if( CheckReiserFsPartition() )
                {
                    printf( "Drive %d, Partition %d is a ReiserFS\n",iDrive, iPartition );
                    printf( "Partition Size: %" FMT_QWORD " Bytes (%.2f MB)\n", 
                        pLI->PartitionEntry[iPartition].PartitionLength.QuadPart, 
                        PBytesInMBytes(pLI->PartitionEntry[iPartition].PartitionLength.QuadPart) );
                }
            }
        }
        else 
#endif

        if( m_pDrive->GetDriveLayout(layout.getData(), layout.getSize()) )
        {
            PDRIVE_LAYOUT_INFORMATION pLI = (PDRIVE_LAYOUT_INFORMATION)layout.getData();
            for( DWORD iPartition = 0; iPartition < pLI->PartitionCount; iPartition++ )
            {
                m_PartitionStartingOffset = pLI->PartitionEntry[iPartition].StartingOffset.QuadPart;
                if( CheckReiserFsPartition() )
                {
					*piPartition = iPartition;
					*piDrive = iDrive;
                    printf( "Drive %d, Partition %d is a ReiserFS -> using that\n",iDrive, iPartition );
                    printf( "Partition Size: %" FMT_QWORD " Bytes (%.2f MB)\n", 
                        pLI->PartitionEntry[iPartition].PartitionLength.QuadPart, 
                        PBytesInMBytes(pLI->PartitionEntry[iPartition].PartitionLength.QuadPart) );
					break;
                }
            }
        }
        else
        {
            printf("ERROR %s, unable to get drive layout from this drive\n", (LPCSTR) GetLastErrorString() ); 
            continue;
        }
    }
}

void ReiserFsPartition::Autodetect( int iMaxDrive, LPFNFoundPartition lpCallback, LPVOID lpContext )
{
    DISK_GEOMETRY dg;
    LayoutBlock layout;

    for( int iDrive = 0; iDrive < 10; iDrive++ )
    {
        if( !m_pDrive->Open(iDrive) ) 
            continue;

        //printf("Testing drive %d\n", iDrive );


#ifdef SUPPORT_WINDOWS_XP_PARTITIONS

        if( m_pDrive->GetDriveGeometryEx( (DISK_GEOMETRY_EX*) layout.getData(), layout.getSize() ) )
        {
            DISK_GEOMETRY_EX* pDG = (DISK_GEOMETRY_EX*) layout.getData();
            m_BytesPerSector = pDG->Geometry.BytesPerSector;
        }
        else 
#endif

        if( m_pDrive->GetDriveGeometry(&dg) )
        {
            m_BytesPerSector = dg.BytesPerSector;
        }
        else
        {
            printf("ERROR %s, unable to get drive geometry from this drive\n", (LPCSTR) GetLastErrorString() ); 
            continue;
        }
    
        memset( layout.getData(), 0, layout.getSize() );

#ifdef SUPPORT_WINDOWS_XP_PARTITIONS

        if( m_pDrive->GetDriveLayoutEx(layout.getData(), layout.getSize()) )
        {
            PDRIVE_LAYOUT_INFORMATION_EX pLI = (PDRIVE_LAYOUT_INFORMATION_EX)layout.getData();

            for( DWORD iPartition = 0; iPartition < pLI->PartitionCount; iPartition++ )
            {
                m_PartitionStartingOffset = pLI->PartitionEntry[iPartition].StartingOffset.QuadPart;
                if( CheckReiserFsPartition() )
                {
                    if( lpCallback )
                    {
                        static char szDriveLetters[] = "abcdefghijklmnopqrstuvwxyz";

                        lpCallback( PString( 0, "/dev/hd%c%d", szDriveLetters[iDrive], iPartition ), lpContext ); 
                    }
                    else
                    {
                        printf( "Drive %d, Partition %d is a ReiserFS\n",iDrive, iPartition );
                        printf( "Partition Size: %" FMT_QWORD " Bytes (%.2f MB)\n", 
                            pLI->PartitionEntry[iPartition].PartitionLength.QuadPart, 
                            PBytesInMBytes(pLI->PartitionEntry[iPartition].PartitionLength.QuadPart) );
                    }
                }
            }
        }
        else 
#endif

        if( m_pDrive->GetDriveLayout(layout.getData(), layout.getSize()) )
        {
            PDRIVE_LAYOUT_INFORMATION pLI = (PDRIVE_LAYOUT_INFORMATION)layout.getData();
            for( DWORD iPartition = 0; iPartition < pLI->PartitionCount; iPartition++ )
            {
                m_PartitionStartingOffset = pLI->PartitionEntry[iPartition].StartingOffset.QuadPart;
                if( CheckReiserFsPartition() )
                {
                    if( lpCallback )
                    {
                        static char szDriveLetters[] = "abcdefghijklmnopqrstuvwxyz";

                        lpCallback( PString( 0, "/dev/hd%c%d", szDriveLetters[iDrive], iPartition ), lpContext ); 
                    }
                    else
                    {
                        printf( "Drive %d, Partition %d is a ReiserFS\n",iDrive, iPartition );
                        printf( "Partition Size: %" FMT_QWORD " Bytes (%.2f MB)\n", 
                            pLI->PartitionEntry[iPartition].PartitionLength.QuadPart, 
                            PBytesInMBytes(pLI->PartitionEntry[iPartition].PartitionLength.QuadPart) );
                    }
                }
            }
        }
        else
        {
            printf("ERROR %s, unable to get drive layout from this drive\n", (LPCSTR) GetLastErrorString() ); 
            continue;
        }
    }
}

bool ReiserFsPartition::Open( int iDrive, int iPartition )
{
    if( iDrive == USE_BACKUP_FILENAME )
    {
        delete m_pDrive;
        m_pDrive = new PSimulatedDriveFromBackupFile(g_szUseSpecificDevice);
    }
    if( !m_pDrive->Open(iDrive) ) 
    {
        printf("ERROR %s, unable to open drive %d\n", (LPCSTR) GetLastErrorString(), iDrive ); 
        return false;
    }

    DISK_GEOMETRY dg;
    LayoutBlock layout;
    
#ifdef SUPPORT_WINDOWS_XP_PARTITIONS

    if( m_pDrive->GetDriveGeometryEx( (DISK_GEOMETRY_EX*) layout.getData(), layout.getSize()) )
    {
        DISK_GEOMETRY_EX* pDG = (DISK_GEOMETRY_EX*) layout.getData();
        m_BytesPerSector = pDG->Geometry.BytesPerSector;
    }
    else 
#endif

    if( m_pDrive->GetDriveGeometry(&dg) )
    {
        m_BytesPerSector = dg.BytesPerSector;
    }
    else
    {
        printf("ERROR %s, unable to get drive geometry from this drive\n", (LPCSTR) GetLastErrorString()  ); 
        return false;
    }
    
    memset( layout.getData(), 0, layout.getSize() );

#ifdef SUPPORT_WINDOWS_XP_PARTITIONS

    if( m_pDrive->GetDriveLayoutEx(layout.getData(), layout.getSize()) )
    {
        printf("Got newstyle partition information, assuming Windows XP\n" );

        PDRIVE_LAYOUT_INFORMATION_EX pLI = (PDRIVE_LAYOUT_INFORMATION_EX)layout.getData();
        if( iPartition < 0 || iPartition >= (int) pLI->PartitionCount )
        {
            printf("ERROR, drive only has partitions [0..%d]\n", pLI->PartitionCount ); 
            return false;
        }
        m_PartitionStartingOffset = pLI->PartitionEntry[iPartition].StartingOffset.QuadPart;
    }
    else 
#endif

    if( m_pDrive->GetDriveLayout(layout.getData(), layout.getSize()) )
    {
        PDRIVE_LAYOUT_INFORMATION pLI = (PDRIVE_LAYOUT_INFORMATION)layout.getData();
        if( iPartition < 0 || iPartition >= (int) pLI->PartitionCount )
        {
            printf("ERROR, drive only has partitions [0..%d]\n", pLI->PartitionCount ); 
            return false;
        }
        m_PartitionStartingOffset = pLI->PartitionEntry[iPartition].StartingOffset.QuadPart;
    }
    else
    {
        printf("ERROR %s, unable to get drive layout from this drive\n", (LPCSTR) GetLastErrorString() ); 
        return false;
    }
    INT64 diskoffset = (m_PartitionStartingOffset + REISERFS_DISK_OFFSET_IN_BYTES);
    
    BYTE* lpbMemory = new BYTE[ (DWORD)( m_BytesPerSector*2 ) ];
    if( !m_pDrive->ReadAbsolute(lpbMemory, (DWORD) m_BytesPerSector, diskoffset ) )
    {
        printf("ERROR %s, unable to read ReiserFS superblock\n", (LPCSTR) GetLastErrorString() ); 
        return false;
    }
    LPREISERFS_SUPER_BLOCK p = (LPREISERFS_SUPER_BLOCK) lpbMemory;

    if( stricmp(p->s_magic,REISERFS_SUPER_MAGIC_STRING) )
    {
        if( stricmp(p->s_magic,REISER2FS_SUPER_MAGIC_STRING) &&
            stricmp(p->s_magic,REISER3FS_SUPER_MAGIC_STRING) )
        {
            printf("ERROR, doesn't seem to be a ReiserFS partition (magic string invalid)\n" ); 
            return false;
        }
		else
		{
			dwTypeDirect = (DWORD) 2;
			dwTypeIndirect = (DWORD) 1;
		}
    }
	else
	{
		dwTypeDirect = (DWORD) -1;
		dwTypeIndirect = (DWORD) -2;
	}
    memcpy(&m_sb,p,sizeof(REISERFS_SUPER_BLOCK));

//	{ // code to dump a directory item

//		BYTE bMemory[559];

//		//FILE* fp = fopen("T:\\dir\\8211.007.dat","rb");

//		FILE* fp = fopen(TARGETDIR "dir\\10708.004.dat","rb");

//		fread(bMemory,1,103,fp);

//		bMemory[103] = 0;

//		fclose(fp);

//		int dh_offset = 0;

//		int dh_strpos = 103;

//		int i = 0;

//

//		while( dh_offset < dh_strpos )

//		{

//			printf(" ----------------- %d --------------------\n", i++ );

//			REISERFS_DIRECTORY_HEAD* pDH = (REISERFS_DIRECTORY_HEAD*) (bMemory+dh_offset);

//

//			printf(" deh_offset = %d\n", pDH->deh_offset);

//			printf(" deh_dir_id = %d\n", pDH->deh_dir_id);

//			printf(" deh_objectid = %d\n", pDH->deh_objectid);

//			printf(" deh_location = %d\n", (int)pDH->deh_location);

//			printf(" deh_state = %d\n", pDH->deh_state);

//			printf(" name='%s'\n",bMemory+pDH->deh_location);

//			printf("\n");

//			(bMemory+pDH->deh_location)[0] = 0;

//			dh_strpos = pDH->deh_location;

//			dh_offset += sizeof(REISERFS_DIRECTORY_HEAD);

//

//		}

//	}

//


	

//

//    m_dwBitmapSize = p->s_blocksize * p->s_bmap_nr;

//    m_lpbBitmap = new BYTE[m_dwBitmapSize];

//    if( !m_lpbBitmap )

//    {

//        printf("ERROR, unable to allocate %d bytes for bitmap information\n", m_dwBitmapSize ); 

//        return false;

//    }

//

//

//    // read the whole bitmap in memory. The bitmap format is...well...weird.

//    // The first block is at (REISERFS_DISK_OFFSET_IN_BYTES + p->s_blocksize)

//    // all following blocks are at (8*p->s_blocksize*i)

//

//    INT64 iBitmapBlock = (m_pi.StartingOffset.QuadPart + REISERFS_DISK_OFFSET_IN_BYTES + p->s_blocksize) / m_dg.BytesPerSector;

//

//    // read first block

//    if( m_pDrive->ReadSector(m_lpbBitmap,p->s_blocksize,iBitmapBlock) )

//    {

//        for( int iBlock = 1; iBlock < p->s_bmap_nr; iBlock++ )

//        {

//            INT64 faktor;

//            INT64 offset = p->s_blocksize;

//            offset *= offset;

//            faktor = 8;

//            offset *= faktor;

//            faktor = iBlock;

//            offset *= faktor;

//            offset += m_pi.StartingOffset.QuadPart;

//            offset /= m_dg.BytesPerSector;

//            m_pDrive->ReadSector(m_lpbBitmap+(iBlock*p->s_blocksize),p->s_blocksize,offset);

//        }

//    }



	//printf("p->s_root_block=%d\n", p->s_root_block );

    // code to dump the whole directory structure


	//DumpBlock(p->s_root_block,0);


//	static REISERFS_CPU_KEY rootkey = { 1, 2, 1, 500 };

//

///*

//~~>  dir_id = 2

//~~>  objectid = 15

//~~>  location = 422

//~~>  state = 4

//~~>  name = 'boot'

//*/

//

//	static REISERFS_CPU_KEY bootkey = { 2, 17, 1, 500 };

//    static REISERFS_CPU_KEY usrkey = { 2, 137, 1, 500 };

//    static REISERFS_CPU_KEY includekey = { 79249, 80823, 1, 500 };

//    static REISERFS_CPU_KEY filestat = { 80823, 80824, 1, -1 };

    
	//DWORD BlockNumber = FindBlockFromKey(&filestat,p->s_root_block);

	
    return true;
}

void WINAPI ListDirCallback(ReiserFsPartition* partition, 
    REISERFS_CPU_KEY* lpKey, LPBYTE lpbMemory, 
    int nSize, void* lpContext )
{
    PList* pDirectory = (PList*) lpContext;
	int dh_offset = 0;
	int dh_strpos = nSize;
	int i = 0, dh_strlen;
                               
    CHAR szTempBuffer[512];
	while( dh_offset < dh_strpos )
	{
		REISERFS_DIRECTORY_HEAD* pDH = (REISERFS_DIRECTORY_HEAD*) (lpbMemory+dh_offset);

        dh_strlen = dh_strpos-pDH->deh_location;
        memcpy(szTempBuffer, lpbMemory+pDH->deh_location, dh_strlen);
        szTempBuffer[dh_strlen] = 0;
		pDirectory->AddTail( new ReiserFsFileInfo(pDH,szTempBuffer) );
		dh_strpos = pDH->deh_location;
		dh_offset += sizeof(REISERFS_DIRECTORY_HEAD);
	}
}


void WINAPI GetFileStat(ReiserFsPartition* partition, REISERFS_CPU_KEY* lpKey, LPBYTE lpbMemory, int nSize, void* lpContext )
{
    ReiserFsFileInfo* pFile = (ReiserFsFileInfo*) lpContext;

	if( nSize == sizeof(REISERFS_STAT2) )
	{
        pFile->m_stat = *(REISERFS_STAT2*) lpbMemory;
	}
	else if( nSize == sizeof(REISERFS_STAT1) )
	{
		REISERFS_STAT1* v1 = (REISERFS_STAT1*) lpbMemory;
        pFile->m_stat.sd_mode = v1->sd_mode;
        pFile->m_stat.sd_nlink = v1->sd_nlink;
        pFile->m_stat.sd_uid = v1->sd_uid;
        pFile->m_stat.sd_gid = v1->sd_gid;
        pFile->m_stat.sd_size = v1->sd_size;
        pFile->m_stat.sd_atime = v1->sd_atime;
        pFile->m_stat.sd_mtime = v1->sd_mtime;
        pFile->m_stat.sd_ctime = v1->sd_ctime;
	    pFile->m_stat.u.sd_rdev = v1->u.sd_rdev;
	}
    else assert(false);
}

typedef struct
{
    FILE* fp;
    PString* pString;
    ICreateFileInfo* pCFI;
    INT64 FileSize;
} GETFILEINDIRECTCONTEXT;

void WINAPI GetFileIndirect(ReiserFsPartition* partition, REISERFS_CPU_KEY* lpKey, LPBYTE lpbMemory, int nSize, void* lpContext )
{
    GETFILEINDIRECTCONTEXT* pc = (GETFILEINDIRECTCONTEXT*) lpContext;

    DWORD dwBlockSize = partition->m_dwBlockSize;

    LPBYTE bMemory = new BYTE[dwBlockSize];
    assert(bMemory);

    long* pBlocks = (long*) lpbMemory;
    int count = nSize / 4;
    INT64 SizeLeft = pc->FileSize;

    for( int index = 0; index < count; index++ )
    {
        long block2 = pBlocks[index];
	    if(partition->Read(bMemory,dwBlockSize,block2) )
        {   
            if( SizeLeft >= dwBlockSize )
            {
                //printf("found %d bytes in indirect item at block %d\n", dwBlockSize, block2 );

                //putchar('.');

                if( pc->fp )
                {
                    fwrite(bMemory,1,dwBlockSize,pc->fp);
                }
                else if( pc->pCFI )
                {
                    pc->pCFI->Write(bMemory, dwBlockSize);
                }
                else
                {
                    pc->pString->Append((LPSTR)bMemory,dwBlockSize);
                }
                SizeLeft -= dwBlockSize;
            }
            else
            {
                //printf("found %d bytes in indirect item at block %d\n", (int)SizeLeft, block2 );

                //putchar('.');

                if( pc->fp )
                {
                    fwrite(bMemory,1,(int)SizeLeft,pc->fp);
                }
                else if( pc->pCFI )
                {
                    pc->pCFI->Write(bMemory, (DWORD) SizeLeft);
                }
                else
                {
                    pc->pString->Append((LPSTR)bMemory,(int)SizeLeft);
                }
                break;
            }
                
        }
    }
    delete bMemory;
    pc->FileSize=SizeLeft;
}

void WINAPI GetFileDirect(ReiserFsPartition* partition, REISERFS_CPU_KEY* lpKey, LPBYTE lpbMemory, int nSize, void* lpContext )
{
    GETFILEINDIRECTCONTEXT* pc = (GETFILEINDIRECTCONTEXT*) lpContext;
    int toWrite=pc->FileSize>nSize?nSize:(int)pc->FileSize;
    pc->FileSize-=toWrite;

    //putchar('.');


    if( pc->fp )
    {
        fwrite(lpbMemory,1,toWrite,pc->fp);
    }
    else if( pc->pCFI )
    {
        pc->pCFI->Write(lpbMemory, (DWORD) toWrite);
    }
    else
    {
        pc->pString->Append((LPSTR)lpbMemory,toWrite);
    }
}

PString ReiserFsPartition::GetFileAsString(ReiserFsFileInfo* pFile) 
{
    GETFILEINDIRECTCONTEXT context;
    ZeroMemory(&context, sizeof(context));
    REISERFS_CPU_KEY key;

    PString strResult;
    BOOL bSuccess = FALSE;

    // get indirect parts

    context.fp = NULL;
    context.pString = &strResult;
    context.FileSize = pFile->m_stat.sd_size;
    if( pFile->m_stat.sd_size > 500 )
    {
        key.k_dir_id = pFile->m_deh.deh_dir_id;
        key.k_objectid = pFile->m_deh.deh_objectid;
        key.k_offset = 1;
        key.k_type = dwTypeIndirect;
        ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileIndirect, &context );
    }
    // get direct parts

    context.fp = NULL;
    context.pString = &strResult;
    // context.FileSize = pFile->m_stat.sd_size;  // do not set file size again

    key.k_dir_id = pFile->m_deh.deh_dir_id;
    key.k_objectid = pFile->m_deh.deh_objectid;
    key.k_offset = 1;
    key.k_type = -1;

    DEBUGTRACE(("READ SYMBOLIC LINK !!!! %s", (LPCSTR) pFile->m_strName ))
    ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileDirect, &context );

    return strResult;
}


bool ReiserFsPartition::CopyFilesRecursive( PList* Directory, LPCSTR lpszName )
{
    REISERFS_CPU_KEY key = { 1, 2, 1, 500 };
	BOOL bSuccess;
    ENUMERATE( Directory, ReiserFsFileInfo, pFile )
    {
		if( !strcmp(pFile->m_strName,".") || 
			!strcmp(pFile->m_strName,"..") )
			continue;


        key.k_dir_id = pFile->m_deh.deh_dir_id;
        key.k_objectid = pFile->m_deh.deh_objectid;
        key.k_offset = 0;
        key.k_type = 0;

        ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileStat, pFile );
//		if( !bSuccess ) 

//		{

//			printf("ERROR, unable to stat %s, ignoring\n", (char*) pFile->m_strName );

//			continue;

//		}

		PString strLocalPath( 0, "%s" SLASH_STRING "%s", lpszName, (char*) pFile->m_strName );
		
        if( S_ISDIR(pFile->m_stat.sd_mode) )
        {
			printf("Directory %s\n", (char*) strLocalPath );
			if( !MakeSurePathExists(strLocalPath) )
				continue;

			key.k_dir_id = pFile->m_deh.deh_dir_id;
			key.k_objectid = pFile->m_deh.deh_objectid;
			key.k_offset = 1;
			key.k_type = 500;

			PList Subdir;
			bSuccess = FALSE;
			ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::ListDirCallback, &Subdir );
			CopyFilesRecursive( &Subdir, strLocalPath );
        }
        else if( pFile->isSymlink() )
        {
			printf("Warning, link '%s' ignored\n", (const char*) strLocalPath );
        }
        else
        {
			printf("File %s (%"FMT_QWORD" Bytes)\n", (const char*) strLocalPath, pFile->m_stat.sd_size );

            FILE* fp = fopen( strLocalPath,"wb");
            if( fp != NULL )
            {
                GETFILEINDIRECTCONTEXT context;
                ZeroMemory(&context, sizeof(context));

                // get indirect parts

                context.fp = fp;
                context.FileSize = pFile->m_stat.sd_size;
                key.k_dir_id = pFile->m_deh.deh_dir_id;
                key.k_objectid = pFile->m_deh.deh_objectid;

                key.k_offset = 1; // perhaps, file offset ???

                key.k_type = dwTypeIndirect;
                ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileIndirect, &context );

                // get direct parts

                context.fp = fp;
                // context.FileSize = pFile->m_stat.sd_size;  // do not set file size again

                key.k_dir_id = pFile->m_deh.deh_dir_id;
                key.k_objectid = pFile->m_deh.deh_objectid;
                key.k_offset = 1;
                key.k_type = dwTypeDirect;
                ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileDirect, &context );

                fclose(fp);
                SetUnixFileTime(strLocalPath, pFile );

            }
            else
				printf("ERROR, unable to open local file %s\n", (char*) strLocalPath );
        }
    }
	
	return true;
}

bool ReiserFsPartition::GetFile( LPCSTR lpszReiserFsName, LPCSTR lpszLocalName, bool bRecurseSubdirectories )
{
    // get array of tokens

    PList Tokens;
    PString strTemp(lpszReiserFsName);
    char* token = strtok( strTemp, "/" );
    while( token != NULL )
    {
        Tokens.AddTail( new PString(token) );
        token = strtok( NULL, "/" );
    }
    if( !Tokens.m_lCount )
    {
        printf("ERROR, you must specify a filename\n" );
        return false;
    }

    // find root directory first

    REISERFS_CPU_KEY key = { 1, 2, 1, 500 };

    // find root directory

    BOOL bSuccess = FALSE;
    PList Directory;
    ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::ListDirCallback, &Directory );

    ENUMERATE( &Tokens, PString, pToken )
    {
        int found = 0;

        ENUMERATE( &Directory, ReiserFsFileInfo, pFile )
        {
            if( strcmp( pFile->m_strName, *pToken ) == 0 )
            {
                // read file|dir stat

                bSuccess = FALSE;

                key.k_dir_id = pFile->m_deh.deh_dir_id;
                key.k_objectid = pFile->m_deh.deh_objectid;
                key.k_offset = 0;
                key.k_type = 0;

                DEBUGTRACE(("**** BEGIN GETFILESTAT\n" ))
				ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileStat, pFile );
				DEBUGTRACE(("**** END GETFILESTAT\n" ))

                if( S_ISDIR(pFile->m_stat.sd_mode) )
                {
					key.k_dir_id = pFile->m_deh.deh_dir_id;
					key.k_objectid = pFile->m_deh.deh_objectid;
					key.k_offset = 1;
					key.k_type = 500;

					Directory.DeleteContents();
					bSuccess = FALSE;
					ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::ListDirCallback, &Directory );
					found = 1;

					if( pToken->m_pNext == 0 )
					{
						MakeSurePathExists(lpszLocalName);
						return CopyFilesRecursive( &Directory, lpszLocalName );
					}
					break;
                }
                else if( pFile->isSymlink() )
                {
                    PString strData(GetFileAsString(pFile) );
                    if( !IsEmptyString(strData) )
                    {
                        token = strtok( strData, "/" );
                        PString* pInsertPos = pToken;
                        while( token != NULL )
                        {
                            PString* pNewToken = new PString(token);
                            Tokens.InsertAfter( pNewToken, pInsertPos );
                            pInsertPos = pNewToken; 
                            token = strtok( NULL, "/" );
                        }
                        found = 1;
                        break;
                    }
                    else
                    {
                        return false;
                    }
                }
                else if( pToken->m_pNext )
                {
                    printf("ERROR, directory '%s' contains file or link\n", lpszReiserFsName );
                    return false;    
                }
                else
                {
                    printf("found\nRetrieving a total of %"FMT_QWORD" bytes\n", pFile->m_stat.sd_size );

                    FILE* fp = fopen( lpszLocalName,"wb");
                    if( fp != NULL )
                    {
                        GETFILEINDIRECTCONTEXT context;
                        ZeroMemory(&context, sizeof(context));

                        // get indirect parts

                        context.fp = fp;
                        context.FileSize = pFile->m_stat.sd_size;
                        key.k_dir_id = pFile->m_deh.deh_dir_id;
                        key.k_objectid = pFile->m_deh.deh_objectid;

                        key.k_offset = 1; // perhaps, file offset ???

                        key.k_type = dwTypeIndirect;
                        ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileIndirect, &context );

                        // get direct parts

                        context.fp = fp;
                        // context.FileSize = pFile->m_stat.sd_size;  // do not set file size again

                        key.k_dir_id = pFile->m_deh.deh_dir_id;
                        key.k_objectid = pFile->m_deh.deh_objectid;
                        key.k_offset = 1;
                        key.k_type = dwTypeDirect;
                        ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileDirect, &context );

                        fclose(fp);

                        SetUnixFileTime(lpszLocalName, pFile );
                        return true;
                    }
                    printf("ERROR, unable to open local file %s\n", lpszLocalName );
                    return false;
                }
            }
        }
        if( !found )
        {
            printf("ERROR, directory '%s' not found\n", lpszReiserFsName );
            return false;    
        }
        token = strtok( NULL, "/" );
    }
    return false;    
}

bool ReiserFsPartition::GetFileEx( LPCSTR lpszReiserFsName, ICreateFileInfo* pCFI )
{
    // get array of tokens

    PList Tokens;
    PString strTemp(lpszReiserFsName);
    char* token = strtok( strTemp, "/" );
    while( token != NULL )
    {
        Tokens.AddTail( new PString(token) );
        token = strtok( NULL, "/" );
    }
    if( !Tokens.m_lCount )
    {
        printf("ERROR, you must specify a filename\n" );
        return false;
    }

    // find root directory first

    REISERFS_CPU_KEY key = { 1, 2, 1, 500 };

    // find root directory

    BOOL bSuccess = FALSE;
    PList Directory;
    ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::ListDirCallback, &Directory );

    ENUMERATE( &Tokens, PString, pToken )
    {
        int found = 0;

        ENUMERATE( &Directory, ReiserFsFileInfo, pFile )
        {
            if( strcmp( pFile->m_strName, *pToken ) == 0 )
            {
                // read file|dir stat

                bSuccess = FALSE;

                key.k_dir_id = pFile->m_deh.deh_dir_id;
                key.k_objectid = pFile->m_deh.deh_objectid;
                key.k_offset = 0;
                key.k_type = 0;

                DEBUGTRACE(("**** BEGIN GETFILESTAT\n" ))
				ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileStat, pFile );
				DEBUGTRACE(("**** END GETFILESTAT\n" ))

                if( S_ISDIR(pFile->m_stat.sd_mode) )
                {
					key.k_dir_id = pFile->m_deh.deh_dir_id;
					key.k_objectid = pFile->m_deh.deh_objectid;
					key.k_offset = 1;
					key.k_type = 500;

					Directory.DeleteContents();
					bSuccess = FALSE;
					ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::ListDirCallback, &Directory );
					found = 1;

					if( pToken->m_pNext == 0 )
					{
                        return false;
					}
					break;
                }
                else if( pFile->isSymlink() ) 
                {
                    PString strData(GetFileAsString(pFile) );
                    if( !IsEmptyString(strData) )
                    {
                        token = strtok( strData, "/" );
                        PString* pInsertPos = pToken;
                        while( token != NULL )
                        {
                            PString* pNewToken = new PString(token);
                            Tokens.InsertAfter( pNewToken, pInsertPos );
                            pInsertPos = pNewToken; 
                            token = strtok( NULL, "/" );
                        }
                        found = 1;
                        break;
                    }
                    else
                    {
                        // this is an empty link

                        pCFI->SetFileSize(0);
                        return true;
                    }
                }
                else if( pToken->m_pNext )
                {
                    printf("ERROR, directory '%s' contains file or link\n", lpszReiserFsName );
                    return false;    
                }
                else
                {
                    GETFILEINDIRECTCONTEXT context;
                    ZeroMemory(&context, sizeof(context));

                    // get indirect parts

                    context.pCFI = pCFI;
                    context.FileSize = pFile->m_stat.sd_size;
                    key.k_dir_id = pFile->m_deh.deh_dir_id;
                    key.k_objectid = pFile->m_deh.deh_objectid;

                    pCFI->SetFileSize(context.FileSize);

                    key.k_offset = 1; // perhaps, file offset ???

                    key.k_type = dwTypeIndirect;
                    ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileIndirect, &context );

                    // get direct parts

                    context.pCFI = pCFI;
                    //context.FileSize = pFile->m_stat.sd_size;

                    key.k_dir_id = pFile->m_deh.deh_dir_id;
                    key.k_objectid = pFile->m_deh.deh_objectid;
                    key.k_offset = 1;
                    key.k_type = dwTypeDirect;
                    ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileDirect, &context );

                    return true;
                }
            }
        }
        if( !found )
        {
            printf("ERROR, directory '%s' not found\n", lpszReiserFsName );
            return false;    
        }
        token = strtok( NULL, "/" );
    }
    return false;    
}


bool ReiserFsPartition::ListDir( PList* pDirectory, LPCSTR lpszDirectory )
{                                        
    PList Tokens;
    PString strTemp(lpszDirectory);
    char* token = strtok( strTemp, "/" );

    while( token != NULL )
    {
        Tokens.AddTail( new PString(token) );
        token = strtok( NULL, "/" );
		
    }

    // find root directory first

    REISERFS_CPU_KEY key = { 1, 2, 1, 500 };

    // find root directory

    BOOL bSuccess = FALSE;

    ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::ListDirCallback, pDirectory );

    ENUMERATE( &Tokens, PString, pToken )
    {
        int found = 0;
        char* token = *pToken;

        ENUMERATE( pDirectory, ReiserFsFileInfo, pFile )
        {
            if( strcmp( pFile->m_strName, token ) == 0 )
            {
                // read file|dir stat


                key.k_dir_id = pFile->m_deh.deh_dir_id;
                key.k_objectid = pFile->m_deh.deh_objectid;
                key.k_offset = 0;
                key.k_type = 0;

                bSuccess = FALSE;	 
                ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileStat, pFile );

                if( S_ISDIR(pFile->m_stat.sd_mode) )
                {
                    key.k_dir_id = pFile->m_deh.deh_dir_id;
                    key.k_objectid = pFile->m_deh.deh_objectid;
                    key.k_offset = 1;
                    key.k_type = 500;

                    pDirectory->DeleteContents();
                    bSuccess = FALSE;

					
                    ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::ListDirCallback, pDirectory );
                    found = 1;
                    break;
                }
                else if( pFile->isSymlink() )
                {
                    PString strData(GetFileAsString(pFile) );
                    if( !IsEmptyString(strData) )
                    {
                        token = strtok( strData, "/" );
                        PString* pInsertPos = pToken;
                        while( token != NULL )
                        {
                            PString* pNewToken = new PString(token);
                            Tokens.InsertAfter( pNewToken, pInsertPos );
                            pInsertPos = pNewToken; 
                            token = strtok( NULL, "/" );
                        }
                        found = 1;
                        break;
                    }
                    else
                    {
                        return false;
                    }
                }
                else 
                {
                    printf("ERROR, Directory '%s' contains file or link '%s'\n", lpszDirectory, token );
                    pDirectory->DeleteContents();
                    return false;    
                }
            }
        }
        if( !found )
        {
            printf("ERROR, directory '%s' not found\n", lpszDirectory );
            pDirectory->DeleteContents();
            return false;    
        }
    }
    if( pDirectory->m_lCount )
    {
        // populate statistics

        int count = 0;
        ENUMERATE( pDirectory, ReiserFsFileInfo, pFile )
        {
            if( count++ < 2 )
                continue;

            BOOL bSuccess = FALSE;
            REISERFS_CPU_KEY key;

            key.k_dir_id = pFile->m_deh.deh_dir_id;
            key.k_objectid = pFile->m_deh.deh_objectid;
            key.k_offset = 0;
            key.k_type = 0;

            ParseTreeRecursive( &bSuccess, &key, m_sb.s_root_block, &::GetFileStat, pFile );
        }
        return true;
    }
    return false;
}

LPBYTE ReiserFsPartition::GetBlock( DWORD BlockNumber )
{
    ENUMERATE(&m_BlockCache,ReiserFsBlock,p)
    {
        if( p->m_dwBlockNumber == BlockNumber ) 
            return p->m_lpbMemory;
    }
    LPBYTE lpbMemory = new BYTE[m_dwBlockSize];
	BOOL bSuccess;
	if( m_Metafile.m_pDataFile )
	{
		bSuccess = m_Metafile.Read( lpbMemory, m_dwBlockSize, BlockNumber );
	}
	else
	{
	    bSuccess = Read( lpbMemory, m_dwBlockSize, BlockNumber );
	}
	if( bSuccess )
	{
		m_BlockCache.AddHead(new ReiserFsBlock(BlockNumber, lpbMemory));
		return lpbMemory;
	}
	printf("*** FATAL ERROR, unable to read block %d\n", BlockNumber );
	return NULL;
}

LPBYTE ReiserFsPartition::GetBlockUncached( DWORD BlockNumber )
{
    ENUMERATE(&m_BlockCache,ReiserFsBlock,p)
    {
        if( p->m_dwBlockNumber == BlockNumber ) 
            return NULL;
    }
    LPBYTE lpbMemory = new BYTE[m_dwBlockSize];
    Read( lpbMemory, m_dwBlockSize, BlockNumber );
    m_BlockCache.AddHead(new ReiserFsBlock(BlockNumber, lpbMemory));
    return lpbMemory;
}

BOOL ReiserFsPartition::PrepareForRestore( const char* pszFilename )
{
	return m_Metafile.Open( this, pszFilename );
}

void ReiserFsPartition::Backup( const char* pszFilename )
{
	FILE* fpData = fopen(pszFilename,"wb");
	if( fpData != NULL )
	{
		PString strIndexName( 0, "%s.index", pszFilename );
		FILE* fpIndex = fopen(strIndexName,"wb");
		if( fpIndex != NULL )
		{
			fwrite(&m_sb,sizeof(REISERFS_SUPER_BLOCK),1,fpIndex);
			BackupTreeRecursive(fpData,fpIndex,m_sb.s_root_block);
			printf("done.\n");
			fclose(fpIndex);
		}
		else printf("ERROR, unable to open file '%s' for writing\n", (char*)strIndexName );

		printf("done.\n");
		fclose(fpData);
	}
	else printf("ERROR, unable to open file '%s' for writing\n", pszFilename );
}

void ReiserFsPartition::BackupTreeRecursive( FILE* fpData, FILE* fpIndex, int nBlock )
{
    BYTE* bMemory = GetBlockUncached( nBlock );
	if( !bMemory )
	{
		// assume this block has already been written

		return;
	}

	// write block header

	fwrite(&nBlock,sizeof(int),1,fpIndex);

	// write block data

	fwrite(bMemory,m_dwBlockSize,1,fpData);
	//putchar('.');


    LPREISERFS_BLOCK_HEAD pH = (LPREISERFS_BLOCK_HEAD)bMemory;
	if( pH->blk_level != 1 )
	{
		LPBYTE lpbHeaderData = bMemory+sizeof(REISERFS_BLOCK_HEAD);
		LPBYTE lpbPointerData = bMemory+sizeof(REISERFS_BLOCK_HEAD)+(pH->blk_nr_item*sizeof(REISERFS_KEY));
		int i;

		for( i = 0; i < pH->blk_nr_item; i++ )
		{
			REISERFS_KEY* key = (REISERFS_KEY*)(lpbHeaderData+i*sizeof(REISERFS_KEY));

            REISERFS_CPU_KEY cpukey;
            cpukey.k_dir_id = key->k_dir_id;
            cpukey.k_objectid = key->k_objectid;
		    cpukey.k_type = (int) key->u.k_offset_v1.k_uniqueness; // WAS: v2

		    cpukey.k_offset = key->u.k_offset_v1.k_offset;

			REISERFS_DISK_KEY* pointer = (REISERFS_DISK_KEY*) (lpbPointerData+i*sizeof(REISERFS_DISK_KEY));
			BackupTreeRecursive( fpData, fpIndex, pointer->dc_block_number );
		}
		REISERFS_DISK_KEY* pointer = (REISERFS_DISK_KEY*) (lpbPointerData+i*sizeof(REISERFS_DISK_KEY));
		BackupTreeRecursive( fpData, fpIndex, pointer->dc_block_number );
	}
}


void ReiserFsPartition::DumpTree()
{
	DumpTreeRecursive(m_sb.s_root_block,0);
}

void ReiserFsPartition::DumpTreeRecursive( int nBlock, int nIndent )
{
	CHAR szIndent[64];
	if( nIndent )
		memset(szIndent,'\t',nIndent);
	szIndent[nIndent] = 0;

    BYTE* bMemory = GetBlock( nBlock );
	if( !bMemory ) return;

	printf("%s---------------- BEGIN BLOCK %d ---------------\n", szIndent, nBlock );

    LPREISERFS_BLOCK_HEAD pH = (LPREISERFS_BLOCK_HEAD)bMemory;
	if( pH->blk_level == 1 )
	{
		printf("%sIS LEAF BLOCK (%d ITEMS)\n", szIndent, pH->blk_nr_item );

		LPBYTE lpbHeaderData = bMemory+sizeof(REISERFS_BLOCK_HEAD);
        bool result = false;
		for( int i = 0; i < pH->blk_nr_item; i++ )
		{
			LPREISERFS_ITEM_HEAD iH = (LPREISERFS_ITEM_HEAD)lpbHeaderData;
            REISERFS_KEY* key = &(iH->ih_key);
            REISERFS_CPU_KEY cpukey;
            cpukey.k_dir_id = key->k_dir_id;
            cpukey.k_objectid = key->k_objectid;
	        if( iH->ih_version == ITEM_VERSION_1 )
	        {
		        cpukey.k_type = iH->ih_key.u.k_offset_v1.k_uniqueness;
		        cpukey.k_offset = iH->ih_key.u.k_offset_v1.k_offset;
	        }
	        else if ( iH->ih_version == ITEM_VERSION_2 )
	        {
		        cpukey.k_type = (int) iH->ih_key.u.k_offset_v2.k_type;
		        cpukey.k_offset = iH->ih_key.u.k_offset_v2.k_offset;
	        }
            else assert(false);


            if( cpukey.k_type == 500 )
            {
			    printf("%s(%d,%d,%"FMT_QWORD",%d) DIRECTORY:\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type );
                // given: 

                // SIZE_OF_BLOCK size of the item on disk.

                // DATA_OF_BLOCK data of the directory item on disk. You should allocate one 

                // byte more and make sure the buffer is zero-terminated for the code below to work.


	            int dh_offset = 0;
	            int dh_strpos = iH->ih_item_len;
	            int i = 0, dh_strlen;
                LPBYTE lpbMemory = bMemory + iH->ih_item_location; 
                               
                CHAR szTempBuffer[512];
	            while( dh_offset < dh_strpos )
	            {
		            REISERFS_DIRECTORY_HEAD* pDH = (REISERFS_DIRECTORY_HEAD*) (lpbMemory+dh_offset);

                    dh_strlen = dh_strpos-pDH->deh_location;
                    memcpy(szTempBuffer, lpbMemory+pDH->deh_location, dh_strlen);
                    szTempBuffer[dh_strlen] = 0;
                    printf("%s\t(%d,%d,%d,%d,%d)=\"%s\"\n", szIndent, 
                        pDH->deh_offset,
                        pDH->deh_dir_id,
                        pDH->deh_objectid,
                        pDH->deh_location,
                        (int) pDH->deh_state,
                        szTempBuffer );
		            dh_strpos = pDH->deh_location;
		            dh_offset += sizeof(REISERFS_DIRECTORY_HEAD);
	            }
            }
            else if( cpukey.k_type == 0 )
            {
                int nSize = iH->ih_item_len;
                LPBYTE lpbMemory = bMemory + iH->ih_item_location; 
                REISERFS_STAT2 m_stat;
	            if( nSize == sizeof(REISERFS_STAT2) )
	            {
                    m_stat = *(REISERFS_STAT2*) lpbMemory;
			        printf("%s(%d,%d,%"FMT_QWORD",%d) STAT2 (%d)\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type, m_stat.sd_size );

	            }
	            else if( nSize == sizeof(REISERFS_STAT1) )
	            {
			        printf("%s(%d,%d,%"FMT_QWORD",%d) STAT1 (%d)\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type, m_stat.sd_size );
		            REISERFS_STAT1* v1 = (REISERFS_STAT1*) lpbMemory;
                    m_stat.sd_mode = v1->sd_mode;
                    m_stat.sd_nlink = v1->sd_nlink;
                    m_stat.sd_uid = v1->sd_uid;
                    m_stat.sd_gid = v1->sd_gid;
                    m_stat.sd_size = v1->sd_size;
                    m_stat.sd_atime = v1->sd_atime;
                    m_stat.sd_mtime = v1->sd_mtime;
                    m_stat.sd_ctime = v1->sd_ctime;
	                m_stat.u.sd_rdev = v1->u.sd_rdev;
	            }
                else
                {
			        printf("%s(%d,%d,%"FMT_QWORD",%d) UNKNOWN ???\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type );
                }

            }
            else if( cpukey.k_type == -1 )
            {
			    printf("%s(%d,%d,%"FMT_QWORD",%d) DIRECT\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type );
            }
            else if( cpukey.k_type == -2 )
            {
			    printf("%s(%d,%d,%"FMT_QWORD",%d) INDIRECT\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type );
            }
            else
            {
			    printf("%s(%d,%d,%"FMT_QWORD",%d) UNKNOWN ???\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type );
            }
			lpbHeaderData += sizeof(REISERFS_ITEM_HEAD);
		}
	}
	else
	{
		int i;
		printf("%sIS DATA BLOCK (%d ITEMS)\n", szIndent, pH->blk_nr_item );

		LPBYTE lpbHeaderData = bMemory+sizeof(REISERFS_BLOCK_HEAD);
		LPBYTE lpbPointerData = bMemory+sizeof(REISERFS_BLOCK_HEAD)+(pH->blk_nr_item*sizeof(REISERFS_KEY));

		for( i = 0; i < pH->blk_nr_item; i++ )
		{
			REISERFS_KEY* key = (REISERFS_KEY*)(lpbHeaderData+i*sizeof(REISERFS_KEY));

            REISERFS_CPU_KEY cpukey;
            cpukey.k_dir_id = key->k_dir_id;
            cpukey.k_objectid = key->k_objectid;
		    cpukey.k_type = (int) key->u.k_offset_v1.k_uniqueness; // WAS: v2

		    cpukey.k_offset = key->u.k_offset_v1.k_offset;

			REISERFS_DISK_KEY* pointer = (REISERFS_DISK_KEY*) (lpbPointerData+i*sizeof(REISERFS_DISK_KEY));
			printf("%s(%d,%d,%"FMT_QWORD",%d)->%d\n", szIndent, cpukey.k_dir_id, cpukey.k_objectid, cpukey.k_offset, cpukey.k_type, pointer->dc_block_number );
			DumpTreeRecursive( pointer->dc_block_number, nIndent+1 );

		}
		REISERFS_DISK_KEY* pointer = (REISERFS_DISK_KEY*) (lpbPointerData+i*sizeof(REISERFS_DISK_KEY));
		printf("%s(right)->%d\n", szIndent, pointer->dc_block_number );
		DumpTreeRecursive( pointer->dc_block_number, nIndent+1 );
	}

	printf("%s---------------- END BLOCK %d ---------------\n", szIndent, nBlock );
}


void ReiserFsPartition::ParseTreeRecursive( BOOL* lpbSuccess, REISERFS_CPU_KEY* lpKeyToFind, int nBlock, LPFNReiserFsSearchCallback lpCallback, void* lpContext )
{
	m_lpKeyToFind = lpKeyToFind;
	m_lpCallback = lpCallback;
	m_lpContext = lpContext;
	m_nIndent = 0;
	DEBUGTRACE(("\nBEGIN ParseTreeRecursive (%ld,%ld,%" FMT_QWORD ",%ld)\n",
    lpKeyToFind->k_dir_id,
    lpKeyToFind->k_objectid,
    lpKeyToFind->k_offset,
    lpKeyToFind->k_type ))

	*lpbSuccess = IParseTreeRecursive( nBlock );
	DEBUGTRACE(("END ParseTreeRecursive\n" ))

}

BOOL ReiserFsPartition::IParseTreeRecursive( int nBlock )
{
	BOOL bResult = FALSE;
#ifdef _DEBUG

	char szIndent[60];
	int nMax = sizeof(szIndent)-1;
	nMax = m_nIndent>nMax?nMax:m_nIndent;
	memset( szIndent, '\t', nMax );
	szIndent[nMax] = 0;
	m_nIndent++;
#endif


	DEBUGTRACE(("%sBEGIN BLOCK %d -----------------------------\n", szIndent, nBlock ))

    if( nBlock == 526541 )
    {
    DEBUGTRACE(("%sBEGIN BLOCK %d -----------------------------\n", szIndent, nBlock ))
    }


    BYTE* bMemory = GetBlock(nBlock);
	if( !bMemory ) 
	{
        bResult = FALSE;
	}
	else
	{
		LPREISERFS_BLOCK_HEAD pH = (LPREISERFS_BLOCK_HEAD)bMemory;
		if( pH->blk_level == 1 )
		{
			DEBUGTRACE(("%sANALYZING LEAF BLOCK\n", szIndent ))
			LPBYTE lpbHeaderData = bMemory+sizeof(REISERFS_BLOCK_HEAD);

			for( int i = 0; i < pH->blk_nr_item; i++ )
			{
				LPREISERFS_ITEM_HEAD iH = (LPREISERFS_ITEM_HEAD)lpbHeaderData;
				REISERFS_KEY* key = &(iH->ih_key);
				REISERFS_CPU_KEY cpukey;
				cpukey.k_dir_id = key->k_dir_id;
				cpukey.k_objectid = key->k_objectid;
				if( iH->ih_version == ITEM_VERSION_1 )
				{
					cpukey.k_type = iH->ih_key.u.k_offset_v1.k_uniqueness;
					cpukey.k_offset = iH->ih_key.u.k_offset_v1.k_offset;
				}
				else if ( iH->ih_version == ITEM_VERSION_2 )
				{
					cpukey.k_type = (int) iH->ih_key.u.k_offset_v2.k_type;
					cpukey.k_offset = iH->ih_key.u.k_offset_v2.k_offset;
				}
				else assert(false);

				int cres = comp_keys_no_offset(m_lpKeyToFind,&cpukey);
				if( cres == 0  )
				{
					DEBUGTRACE(("%s**** CALLBACK ****\n", szIndent ))
					m_lpCallback(this, &cpukey, bMemory + iH->ih_item_location, iH->ih_item_len, m_lpContext);
					bResult = TRUE;
					//goto bailout;

				}
				else if( cres < 0 )
				{
					//DEBUGTRACE(("%s**** QUICK TO BAILOUT ****\n", szIndent ))

					//goto bailout;

				}
				lpbHeaderData += sizeof(REISERFS_ITEM_HEAD);
			}
		}
		else
		{
			DEBUGTRACE(("%sANALYZING TREE BLOCK\n", szIndent ))
			LPBYTE lpbHeaderData = bMemory+sizeof(REISERFS_BLOCK_HEAD);
			LPBYTE lpbPointerData = bMemory+sizeof(REISERFS_BLOCK_HEAD)+(pH->blk_nr_item*sizeof(REISERFS_KEY));

			int i, nLastCres = -1;
			bool oldFound = false;

			for( i = 0; i < pH->blk_nr_item; i++ )
			{
				REISERFS_KEY* key = (REISERFS_KEY*)(lpbHeaderData+i*sizeof(REISERFS_KEY));

				REISERFS_CPU_KEY cpukey;
				cpukey.k_dir_id = key->k_dir_id;
				cpukey.k_objectid = key->k_objectid;
				cpukey.k_type = (int) key->u.k_offset_v2.k_type;
				cpukey.k_offset = (U64) key->u.k_offset_v2.k_offset;

				int cres = comp_keys(&cpukey,m_lpKeyToFind);
				if( cres == 1 || cres == 0 )
				{
					REISERFS_DISK_KEY* pointer = (REISERFS_DISK_KEY*) (lpbPointerData+i*sizeof(REISERFS_DISK_KEY));
					if( IParseTreeRecursive( pointer->dc_block_number ) )
					{
						oldFound = true;
					}
					else
					{
						if( oldFound )
						{
							bResult = TRUE;
							goto bailout;
						}
						else if( nLastCres == 1 && cres == 1 )
						{
							goto bailout;
						}
					}
				}
				nLastCres = cres;
			}
			REISERFS_DISK_KEY* pointer = (REISERFS_DISK_KEY*) (lpbPointerData+i*sizeof(REISERFS_DISK_KEY));
			if( IParseTreeRecursive( pointer->dc_block_number ) )
			{
				bResult = TRUE;
			}
		}
	}

bailout:
	DEBUGTRACE(("%sEND BLOCK %d WITH %s -----------------------------\n", szIndent, nBlock, bResult ? "TRUE" : "FALSE" ))
#ifdef _DEBUG

	m_nIndent--;
#endif

	return bResult;
}

// potential code for bitmap dumiong

//    BYTE* pBitmap = m_lpbBitmap;

//    

//    int BitmapSize = 0x9000;

//    int BitmapOffset = 0;

//    int iLastBitUsed = -1;

//    BYTE bitmap_map[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };

//    int iBit = 0;

//    while( iBit < BitmapSize )

//    {

//        if( pBitmap[iBit/8] & bitmap_map[iBit%8] )

//        {

//            if( iLastBitUsed == -1 )

//                iLastBitUsed = iBit;

//        }

//        else

//        {

//            // bit is not set

//            if( iLastBitUsed != -1 )

//            {

//                fprintf(fp,"used: %d..%d\r\n", BitmapOffset+iLastBitUsed, BitmapOffset+iBit-1 );

//                iLastBitUsed = -1;

//            }

//        }

//        iBit++;

//    }

//    fclose(fp);


ReiserFsMetafile::ReiserFsMetafile()
{
	m_pBlockIndices = NULL;
	m_pDataFile = NULL;
}

ReiserFsMetafile::~ReiserFsMetafile()
{
	delete m_pBlockIndices;
	if( m_pDataFile )
		fclose( m_pDataFile );

}

BOOL ReiserFsMetafile::Open( ReiserFsPartition* partition, const char* pszFilename )
{
	// read index file first

	PString strIndexName( 0, "%s.index", pszFilename );
	FILE* fpIndex = fopen(strIndexName,"rb");
	if( fpIndex != NULL )
	{
		// determine file size

		fseek(fpIndex,0,SEEK_END);
		long filesize = ftell(fpIndex);
		fseek(fpIndex,0,SEEK_SET);

		if( filesize < sizeof(REISERFS_SUPER_BLOCK) )
		{
			printf("ERROR, file '%s' too small to be a valid index file, aborting.\n", (char*) strIndexName );
			fclose(fpIndex);
			return FALSE;
		}
		fread(&m_Superblock,sizeof(REISERFS_SUPER_BLOCK),1,fpIndex);
		m_dwBlocksize = m_Superblock.s_blocksize;
        if( partition )
        {
		    partition->m_sb = m_Superblock;
        }
		long indexsize = filesize - sizeof(REISERFS_SUPER_BLOCK);
		m_iNumberOfIndices = (int)(indexsize / sizeof(int));
		m_pBlockIndices = new int[m_iNumberOfIndices];
		if( !m_pBlockIndices )
		{
			printf("ERROR, not enough memory to allocate %d bytes for indices, aborting\n", indexsize );
			fclose(fpIndex);
			return FALSE;
		}
		fread(m_pBlockIndices,indexsize,1,fpIndex);
		fclose(fpIndex);

		printf("Read %d indices successfully.\n", m_iNumberOfIndices );
	}
	else 
	{
		printf("ERROR, unable to open file '%s' for reading\n", (char*)strIndexName );
		return FALSE;
	}
	m_pDataFile = fopen(pszFilename,"rb");
	if( m_pDataFile == NULL )
	{
		printf("ERROR, unable to open file '%s' for reading\n", pszFilename );
		return FALSE;
	}
	return TRUE;
}

BOOL ReiserFsMetafile::Read( LPBYTE lpbMemory, DWORD dwSize, INT64 BlockNumber )
{
	if( dwSize != m_dwBlocksize )
	{
		printf("ERROR, expected block size %d, got %d\n", m_dwBlocksize, dwSize );
		return FALSE;
	}
	int index, blocknr = (int) BlockNumber;
	for( index = 0; index < m_iNumberOfIndices; index++ )
	{
		if( m_pBlockIndices[index] == blocknr )
		{
			fseek(m_pDataFile,index*m_dwBlocksize,SEEK_SET);
			fread(lpbMemory,m_dwBlocksize,1,m_pDataFile);
			return TRUE;
		}
	}
	return FALSE;
} // Read()



syntax highlighted by Code2HTML, v. 0.9.1