mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
618 lines
17 KiB
C++
618 lines
17 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2016 Martin Raiber
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************************/
|
|
|
|
#include "../../Interface/Server.h"
|
|
#include "../../stringtools.h"
|
|
#include "ntfs.h"
|
|
#include <math.h>
|
|
#include <memory.h>
|
|
|
|
#ifndef _WIN32
|
|
#define UD_UINT64 0xFFFFFFFFFFFFFFFFULL
|
|
#else
|
|
#define UD_UINT64 0xFFFFFFFFFFFFFFFF
|
|
#endif
|
|
|
|
class MemFree
|
|
{
|
|
public:
|
|
MemFree(char *buf) : buf(buf) {}
|
|
~MemFree(void) { delete []buf; }
|
|
|
|
private:
|
|
char* buf;
|
|
};
|
|
|
|
FSNTFS::FSNTFS(const std::string &pDev, IFSImageFactory::EReadaheadMode read_ahead, bool background_priority, IFsNextBlockCallback* next_block_callback, bool check_mft_mirror, bool fix)
|
|
: Filesystem(pDev, read_ahead, next_block_callback), bitmap(NULL)
|
|
{
|
|
init(check_mft_mirror, fix);
|
|
initReadahead(read_ahead, background_priority);
|
|
}
|
|
|
|
FSNTFS::FSNTFS(IFile *pDev, IFSImageFactory::EReadaheadMode read_ahead, bool background_priority, IFsNextBlockCallback* next_block_callback, bool check_mft_mirror, bool fix)
|
|
: Filesystem(pDev, next_block_callback), bitmap(NULL)
|
|
{
|
|
init(check_mft_mirror, fix);
|
|
initReadahead(read_ahead, background_priority);
|
|
}
|
|
|
|
void FSNTFS::init(bool check_mft_mirror, bool fix)
|
|
{
|
|
if(has_error)
|
|
return;
|
|
|
|
bitmap=NULL;
|
|
sectorsize=512;
|
|
NTFSBootRecord br;
|
|
_u32 rc=sectorRead(0, (char*)&br, sizeof(NTFSBootRecord) );
|
|
if(rc!=sizeof(NTFSBootRecord) )
|
|
{
|
|
has_error=true;
|
|
Server->Log("Error reading boot record", LL_ERROR);
|
|
return;
|
|
}
|
|
if( br.magic[0]!='N' || br.magic[1]!='T' || br.magic[2]!='F' || br.magic[3]!='S' )
|
|
{
|
|
has_error=true;
|
|
Server->Log("NTFS magic wrong", LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
sectorsize=br.bytespersector;
|
|
clustersize=sectorsize*br.sectorspercluster;
|
|
drivesize=br.numberofsectors*sectorsize;
|
|
|
|
Server->Log("Sectorsize: "+convert(sectorsize), LL_DEBUG);
|
|
Server->Log("Clustersize: "+convert(clustersize), LL_DEBUG);
|
|
Server->Log("ClustersPerMFTNode Offset: "+convert((uint64)&br.mftlcn-(uint64)&br), LL_DEBUG);
|
|
|
|
unsigned int mftrecordsize;
|
|
if(br.clusterspermftrecord<0)
|
|
{
|
|
mftrecordsize=1 << (-br.clusterspermftrecord);
|
|
}
|
|
else
|
|
{
|
|
mftrecordsize=br.clusterspermftrecord*clustersize;
|
|
}
|
|
|
|
uint64 mftstart=br.mftlcn*clustersize;
|
|
Server->Log("MFTStart: "+convert(br.mftlcn), LL_DEBUG);
|
|
|
|
char *mftrecord=new char[mftrecordsize];
|
|
MemFree mftrecord_free(mftrecord);
|
|
|
|
rc=sectorRead(mftstart, mftrecord, mftrecordsize);
|
|
if(rc!=mftrecordsize )
|
|
{
|
|
has_error=true;
|
|
Server->Log("Error reading MFTRecord", LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
NTFSFileRecord mft;
|
|
memcpy((char*)&mft, mftrecord, sizeof(NTFSFileRecord) );
|
|
|
|
if(!applyFixups(mftrecord, mftrecordsize, mftrecord+mft.sequence_offset, mft.sequence_size*2 ) )
|
|
{
|
|
Server->Log("Applying fixups failed", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
if(mft.magic[0]!='F' || mft.magic[1]!='I' || mft.magic[2]!='L' || mft.magic[3]!='E' )
|
|
{
|
|
has_error=true;
|
|
Server->Log("NTFSFileRecord magic wrong", LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
_u32 currpos=0;
|
|
MFTAttribute attr;
|
|
attr.length=mft.attribute_offset;
|
|
do
|
|
{
|
|
currpos+=attr.length;
|
|
memcpy((char*)&attr, mftrecord+currpos, sizeof(MFTAttribute) );
|
|
if(attr.type==0x30 && attr.nonresident==0) //FILENAME
|
|
{
|
|
MFTAttributeFilename fn;
|
|
memcpy((char*)&fn, mftrecord+currpos+attr.attribute_offset, sizeof(MFTAttributeFilename) );
|
|
std::string fn_uc;
|
|
fn_uc.resize(fn.filename_length*2);
|
|
memcpy(&fn_uc[0], mftrecord+currpos+attr.attribute_offset+sizeof(MFTAttributeFilename), fn.filename_length*2);
|
|
Server->Log("Filename="+Server->ConvertFromUTF16(fn_uc) , LL_DEBUG);
|
|
}
|
|
Server->Log("Attribute Type: "+convert(attr.type)+" nonresident="+convert(attr.nonresident)+" length="+convert(attr.length), LL_DEBUG);
|
|
}while( attr.type!=0xFFFFFFFF && attr.type!=0x80);
|
|
|
|
if(attr.type==0xFFFFFFFF )
|
|
{
|
|
has_error=true;
|
|
Server->Log("Data attribute not found", LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
if(attr.nonresident!=1)
|
|
{
|
|
Server->Log("DATA is resident!! - unexpected", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
MFTAttributeNonResident datastream;
|
|
memcpy((char*)&datastream, mftrecord+currpos, sizeof(MFTAttributeNonResident) );
|
|
if(datastream.compression_size!=0)
|
|
{
|
|
Server->Log("MFT Rundata is compressed. Can't handle that", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
Runlist mftrunlist(mftrecord+currpos+datastream.run_offset );
|
|
|
|
unsigned int bitmap_vcn=(6*mftrecordsize)/clustersize;
|
|
uint64 bitmap_lcn=mftrunlist.getLCN(bitmap_vcn);
|
|
if(bitmap_lcn==UD_UINT64)
|
|
{
|
|
Server->Log("Error mapping VCN to LCN", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
uint64 bitmap_pos=bitmap_lcn*clustersize+(6*mftrecordsize)%clustersize;
|
|
char *bitmaprecord=new char[mftrecordsize];
|
|
MemFree bitmaprecord_free(bitmaprecord);
|
|
|
|
rc=sectorRead(bitmap_pos, bitmaprecord, mftrecordsize);
|
|
if(rc==0)
|
|
{
|
|
Server->Log("Error reading bitmap MFT entry", LL_DEBUG);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
NTFSFileRecord bitmapf;
|
|
memcpy((char*)&bitmapf, bitmaprecord, sizeof(NTFSFileRecord) );
|
|
|
|
if(!applyFixups(bitmaprecord, mftrecordsize, bitmaprecord+bitmapf.sequence_offset, bitmapf.sequence_size*2 ) )
|
|
{
|
|
Server->Log("Applying fixups failed", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
if(bitmapf.magic[0]!='F' || bitmapf.magic[1]!='I' || bitmapf.magic[2]!='L' || bitmapf.magic[3]!='E' )
|
|
{
|
|
has_error=true;
|
|
Server->Log("NTFSFileRecord magic wrong -2", LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
currpos=0;
|
|
attr.length=mft.attribute_offset;
|
|
bool is_bitmap=false;
|
|
do
|
|
{
|
|
currpos+=attr.length;
|
|
memcpy((char*)&attr, bitmaprecord+currpos, sizeof(MFTAttribute) );
|
|
if(attr.type==0x30 && attr.nonresident==0) //FILENAME
|
|
{
|
|
MFTAttributeFilename fn;
|
|
memcpy((char*)&fn, bitmaprecord+currpos+attr.attribute_offset, sizeof(MFTAttributeFilename) );
|
|
std::string fn_uc;
|
|
fn_uc.resize(fn.filename_length*2);
|
|
memcpy(&fn_uc[0], bitmaprecord+currpos+attr.attribute_offset+sizeof(MFTAttributeFilename), fn.filename_length*2);
|
|
Server->Log("Filename="+Server->ConvertFromUTF16(fn_uc) , LL_DEBUG);
|
|
if(Server->ConvertFromUTF16(fn_uc)=="$Bitmap")
|
|
{
|
|
is_bitmap=true;
|
|
}
|
|
}
|
|
Server->Log("Attribute Type: "+convert(attr.type)+" nonresident="+convert(attr.nonresident)+" length="+convert(attr.length), LL_DEBUG);
|
|
}while( attr.type!=0xFFFFFFFF && attr.type!=0x80);
|
|
|
|
if(!is_bitmap)
|
|
{
|
|
Server->Log("Filename attribute not found or filename wrong", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
if(attr.type!=0x80)
|
|
{
|
|
Server->Log("Data Attribute of Bitmap not found", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
if(attr.nonresident!=1)
|
|
{
|
|
Server->Log("DATA is resident!! - unexpected -2", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
MFTAttributeNonResident bitmapstream;
|
|
memcpy((char*)&bitmapstream, bitmaprecord+currpos, sizeof(MFTAttributeNonResident) );
|
|
if(bitmapstream.compression_size!=0)
|
|
{
|
|
Server->Log("MFT Rundata is compressed. Can't handle that. -2", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
|
|
Runlist bitmaprunlist(bitmaprecord+currpos+bitmapstream.run_offset );
|
|
|
|
Server->Log("Bitmap size="+convert(bitmapstream.real_size), LL_DEBUG);
|
|
bitmap=new unsigned char[(unsigned int)bitmapstream.real_size];
|
|
char *buffer=new char[clustersize];
|
|
MemFree buffer_free(buffer);
|
|
bitmap_pos=0;
|
|
for(uint64 i=bitmapstream.starting_vnc;i<=bitmapstream.last_vnc;++i)
|
|
{
|
|
uint64 lcn=bitmaprunlist.getLCN(i);
|
|
if(lcn==UD_UINT64)
|
|
{
|
|
Server->Log("Error mapping VCN->LCN. -2", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
dev->Seek(lcn*clustersize);
|
|
rc=dev->Read(buffer, clustersize);
|
|
if(rc!=clustersize)
|
|
{
|
|
Server->Log("Error reading cluster "+convert(lcn)+" code: 529", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
memcpy(&bitmap[bitmap_pos], buffer, (size_t)(std::min)(bitmapstream.real_size-bitmap_pos, (uint64)clustersize) );
|
|
bitmap_pos+=(std::min)(bitmapstream.real_size-bitmap_pos, (uint64)clustersize);
|
|
}
|
|
|
|
if(check_mft_mirror)
|
|
{
|
|
if(!checkMFTMirror(mftrecordsize, mftrunlist, mft, true) )
|
|
{
|
|
Server->Log("MFT mirror check failed", LL_ERROR);
|
|
has_error=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
FSNTFS::~FSNTFS(void)
|
|
{
|
|
delete [] bitmap;
|
|
}
|
|
|
|
_u32 FSNTFS::sectorRead(int64 pos, char *buffer, _u32 bsize)
|
|
{
|
|
int64 rpos=pos-pos%sectorsize;
|
|
dev->Seek(rpos);
|
|
_u32 rbsize=(_u32)(pos-rpos)+bsize;
|
|
rbsize=rbsize+(sectorsize-rbsize%sectorsize);
|
|
char *rbuf=new char[rbsize];
|
|
_u32 read=dev->Read(rbuf, rbsize);
|
|
if(read!=rbsize && read<(pos-rpos)+bsize)
|
|
{
|
|
return 0;
|
|
}
|
|
memcpy(buffer, &rbuf[pos-rpos], bsize);
|
|
delete [] rbuf;
|
|
return bsize;
|
|
}
|
|
|
|
bool FSNTFS::applyFixups(char *data, size_t datasize, char* fixups, size_t fixups_size)
|
|
{
|
|
unsigned int num_fixups=(unsigned int)datasize/sectorsize;
|
|
if(num_fixups>(fixups_size-2)/2)
|
|
{
|
|
Server->Log("Number of fixups wrong!", LL_ERROR);
|
|
return false;
|
|
}
|
|
char seq_number[2];
|
|
memcpy(seq_number, fixups, 2);
|
|
|
|
size_t t=0;
|
|
for(size_t i=2;i<fixups_size;i+=2,++t)
|
|
{
|
|
if( data[(t+1)*sectorsize-2]!=seq_number[0] || data[(t+1)*sectorsize-1]!=seq_number[1] )
|
|
{
|
|
Server->Log("Cluster corrupted. Stopping. (Testing fixup failed)", LL_ERROR);
|
|
return false;
|
|
}
|
|
data[(t+1)*sectorsize-2]=fixups[i];
|
|
data[(t+1)*sectorsize-1]=fixups[i+1];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int64 FSNTFS::getBlocksize(void)
|
|
{
|
|
return clustersize;
|
|
}
|
|
|
|
int64 FSNTFS::getSize(void)
|
|
{
|
|
return drivesize;
|
|
}
|
|
|
|
const unsigned char * FSNTFS::getBitmap(void)
|
|
{
|
|
return bitmap;
|
|
}
|
|
|
|
void FSNTFS::logFileChanges(std::string volpath, int64 min_size, char * fc_bitmap)
|
|
{
|
|
}
|
|
|
|
bool FSNTFS::checkMFTMirror(unsigned int mftrecordsize, Runlist &mftrunlist, NTFSFileRecord &mft, bool fix)
|
|
{
|
|
unsigned int mirr_vcn=(1*mftrecordsize)/clustersize;
|
|
uint64 mirr_lcn=mftrunlist.getLCN(mirr_vcn);
|
|
if(mirr_lcn==UD_UINT64)
|
|
{
|
|
Server->Log("Error mapping VCN to LCN", LL_ERROR);
|
|
return false;
|
|
}
|
|
uint64 mirr_pos=mirr_lcn*clustersize+(1*mftrecordsize)%clustersize;
|
|
char *mirrrecord=new char[mftrecordsize];
|
|
MemFree mirrrecord_free(mirrrecord);
|
|
|
|
_u32 rc=sectorRead(mirr_pos, mirrrecord, mftrecordsize);
|
|
if(rc==0)
|
|
{
|
|
Server->Log("Error reading bitmap MFT entry", LL_DEBUG);
|
|
return false;
|
|
}
|
|
|
|
NTFSFileRecord mftmirr;
|
|
memcpy((char*)&mftmirr, mirrrecord, sizeof(NTFSFileRecord) );
|
|
|
|
if(!applyFixups(mirrrecord, mftrecordsize, mirrrecord+mftmirr.sequence_offset, mftmirr.sequence_size*2 ) )
|
|
{
|
|
Server->Log("Applying fixups failed", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(mftmirr.magic[0]!='F' || mftmirr.magic[1]!='I' || mftmirr.magic[2]!='L' || mftmirr.magic[3]!='E' )
|
|
{
|
|
has_error=true;
|
|
return false;
|
|
}
|
|
|
|
_u32 currpos=0;
|
|
MFTAttribute attr;
|
|
attr.length=mft.attribute_offset;
|
|
bool is_mftmirr=false;
|
|
do
|
|
{
|
|
currpos+=attr.length;
|
|
memcpy((char*)&attr, mirrrecord+currpos, sizeof(MFTAttribute) );
|
|
if(attr.type==0x30 && attr.nonresident==0) //FILENAME
|
|
{
|
|
MFTAttributeFilename fn;
|
|
memcpy((char*)&fn, mirrrecord+currpos+attr.attribute_offset, sizeof(MFTAttributeFilename) );
|
|
std::string fn_uc;
|
|
fn_uc.resize(fn.filename_length*2);
|
|
memcpy(&fn_uc[0], mirrrecord+currpos+attr.attribute_offset+sizeof(MFTAttributeFilename), fn.filename_length*2);
|
|
Server->Log("Filename="+Server->ConvertFromUTF16(fn_uc) , LL_DEBUG);
|
|
if(Server->ConvertFromUTF16(fn_uc)=="$MFTMirr")
|
|
{
|
|
is_mftmirr=true;
|
|
}
|
|
}
|
|
Server->Log("Attribute Type: "+convert(attr.type)+" nonresident="+convert(attr.nonresident)+" length="+convert(attr.length), LL_DEBUG);
|
|
}while( attr.type!=0xFFFFFFFF && attr.type!=0x80);
|
|
|
|
if(!is_mftmirr)
|
|
{
|
|
Server->Log("Filename attribute not found or filename wrong", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(attr.type!=0x80)
|
|
{
|
|
Server->Log("Data Attribute of MftMirr not found", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(attr.nonresident!=1)
|
|
{
|
|
Server->Log("DATA is resident!! - unexpected -2", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
MFTAttributeNonResident mftmirrstream;
|
|
memcpy((char*)&mftmirrstream, mirrrecord+currpos, sizeof(MFTAttributeNonResident) );
|
|
if(mftmirrstream.compression_size!=0)
|
|
{
|
|
Server->Log("MFT Rundata is compressed. Can't handle that. -2", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
Runlist mirrrunlist(mirrrecord+currpos+mftmirrstream.run_offset );
|
|
|
|
unsigned char *mftmirr_data=new unsigned char[(unsigned int)mftmirrstream.real_size];
|
|
MemFree mftmirr_data_free(reinterpret_cast<char*>(mftmirr_data));
|
|
char *buffer=new char[clustersize];
|
|
MemFree buffer_free(buffer);
|
|
mirr_pos=0;
|
|
for(uint64 i=mftmirrstream.starting_vnc;i<=mftmirrstream.last_vnc;++i)
|
|
{
|
|
uint64 lcn=mirrrunlist.getLCN(i);
|
|
if(lcn==UD_UINT64)
|
|
{
|
|
Server->Log("Error mapping VCN->LCN. -2", LL_ERROR);
|
|
return false;
|
|
}
|
|
dev->Seek(lcn*clustersize);
|
|
rc=dev->Read(buffer, clustersize);
|
|
if(rc!=clustersize)
|
|
{
|
|
Server->Log("Error reading cluster "+convert(lcn)+" code: 529", LL_ERROR);
|
|
return false;
|
|
}
|
|
memcpy(&mftmirr_data[mirr_pos], buffer, (size_t)(std::min)(mftmirrstream.real_size-mirr_pos, (uint64)clustersize) );
|
|
mirr_pos+=(std::min)(mftmirrstream.real_size-mirr_pos, (uint64)clustersize);
|
|
}
|
|
|
|
bool has_fix=false;
|
|
|
|
for(uint64 i=0;i<mftmirrstream.real_size/mftrecordsize;++i)
|
|
{
|
|
uint64 currfile_vcn=(i*mftrecordsize)/clustersize;
|
|
uint64 currfile_lcn=mftrunlist.getLCN(currfile_vcn);
|
|
if(currfile_lcn==UD_UINT64)
|
|
{
|
|
Server->Log("Error mapping VCN to LCN", LL_ERROR);
|
|
return false;
|
|
}
|
|
uint64 currfile_pos=currfile_lcn*clustersize+(1*mftrecordsize)%clustersize;
|
|
char *currfilerecord=new char[mftrecordsize];
|
|
MemFree currfilerecord_free(currfilerecord);
|
|
|
|
_u32 rc=sectorRead(currfile_pos, currfilerecord, mftrecordsize);
|
|
if(rc==0)
|
|
{
|
|
Server->Log("Error reading currfile MFT entry", LL_DEBUG);
|
|
return false;
|
|
}
|
|
|
|
NTFSFileRecord *A=(NTFSFileRecord *)currfilerecord;
|
|
NTFSFileRecord *B=(NTFSFileRecord *)(mftmirr_data+i*mftrecordsize);
|
|
|
|
if(A->lsn!=B->lsn)
|
|
{
|
|
Server->Log("MFT file record in MFT mirror differs in file record "+convert(i)+": Logfile sequence number differs", LL_WARNING);
|
|
}
|
|
|
|
if(memcmp(currfilerecord, mftmirr_data+i*mftrecordsize, mftrecordsize)!=0)
|
|
{
|
|
Server->Log("MFT file record in MFT mirror differs in file record "+convert(i), LL_WARNING);
|
|
if(fix)
|
|
{
|
|
memcpy(mftmirr_data+i*mftrecordsize, currfilerecord, mftrecordsize);
|
|
has_fix=true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(has_fix)
|
|
{
|
|
mirr_pos=0;
|
|
for(uint64 i=mftmirrstream.starting_vnc;i<=mftmirrstream.last_vnc;++i)
|
|
{
|
|
uint64 lcn=mirrrunlist.getLCN(i);
|
|
if(lcn==UD_UINT64)
|
|
{
|
|
Server->Log("Error mapping VCN->LCN. -2", LL_ERROR);
|
|
return false;
|
|
}
|
|
dev->Seek(lcn*clustersize);
|
|
rc=dev->Write((const char*)(mftmirr_data+i*clustersize), clustersize);
|
|
if(rc!=clustersize)
|
|
{
|
|
Server->Log("Error writing cluster "+convert(lcn)+" code: 652", LL_WARNING);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Server->Log("Fixed MFT mirror", LL_ERROR);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-------------- RUNLIST -----------------
|
|
|
|
Runlist::Runlist(char *pData) : data(pData)
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void Runlist::reset(void)
|
|
{
|
|
pos=data;
|
|
}
|
|
|
|
bool Runlist::getNext(RunlistItem &item)
|
|
{
|
|
char f=*pos;
|
|
if(f==0)
|
|
return false;
|
|
|
|
char offset_size=f >> 4;
|
|
char length_size=f & 0x0F;
|
|
item.length=0;
|
|
item.offset=0;
|
|
memcpy(&item.length, pos+1, length_size);
|
|
|
|
bool is_signed=(*(pos+1+length_size+offset_size-1) & 0x80)>0;
|
|
memcpy(&item.offset, pos+1+length_size, offset_size);
|
|
|
|
if(is_signed)
|
|
{
|
|
char * ar=(char*)&item.offset;
|
|
ar[offset_size-1]=ar[offset_size-1] & 0x7F;
|
|
item.offset*=-1;
|
|
}
|
|
|
|
pos+=1+offset_size+length_size;
|
|
return true;
|
|
}
|
|
|
|
uint64 Runlist::getSizeInClusters(void)
|
|
{
|
|
reset();
|
|
RunlistItem item;
|
|
uint64 size=0;
|
|
while(getNext(item))
|
|
{
|
|
size+=item.length;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
uint64 Runlist::getLCN(uint64 vcn)
|
|
{
|
|
reset();
|
|
RunlistItem item;
|
|
uint64 lcn=0;
|
|
uint64 coffset=0;
|
|
while(getNext(item))
|
|
{
|
|
lcn+=item.offset;
|
|
|
|
if(coffset<=vcn && coffset+item.length>vcn )
|
|
{
|
|
return lcn+(vcn-coffset);
|
|
}
|
|
|
|
coffset+=item.length;
|
|
}
|
|
return UD_UINT64;
|
|
}
|
|
|