mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
152 lines
3.3 KiB
C++
152 lines
3.3 KiB
C++
#include "ChunkPatcher.h"
|
|
#include "../stringtools.h"
|
|
#include <assert.h>
|
|
|
|
ChunkPatcher::ChunkPatcher(void)
|
|
: cb(NULL), require_unchanged(true)
|
|
{
|
|
}
|
|
|
|
void ChunkPatcher::setCallback(IChunkPatcherCallback *pCb)
|
|
{
|
|
cb=pCb;
|
|
}
|
|
|
|
bool ChunkPatcher::ApplyPatch(IFile *file, IFile *patch)
|
|
{
|
|
patch->Seek(0);
|
|
file->Seek(0);
|
|
_i64 patchf_pos=0;
|
|
|
|
const unsigned int buffer_size=32768;
|
|
char buf[buffer_size];
|
|
|
|
if(patch->Read((char*)&filesize, sizeof(_i64))!=sizeof(_i64))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
filesize = little_endian(filesize);
|
|
|
|
patchf_pos+=sizeof(_i64);
|
|
|
|
SPatchHeader next_header;
|
|
next_header.patch_off=-1;
|
|
bool has_header=true;
|
|
for(_i64 file_pos=0,size=file->Size(); (file_pos<size && file_pos<filesize) || has_header;)
|
|
{
|
|
if(has_header && next_header.patch_off==-1)
|
|
{
|
|
has_header=readNextValidPatch(patch, patchf_pos, &next_header);
|
|
}
|
|
|
|
unsigned int tr=buffer_size;
|
|
if(next_header.patch_off!=-1)
|
|
{
|
|
_i64 hoff=next_header.patch_off-file_pos;
|
|
if(hoff>=0 && hoff<tr)
|
|
tr=(unsigned int)hoff;
|
|
|
|
assert(hoff>=0);
|
|
}
|
|
|
|
assert(file_pos<filesize);
|
|
|
|
if(tr==0)
|
|
{
|
|
assert(file_pos==next_header.patch_off);
|
|
|
|
file_pos+=next_header.patch_size;
|
|
|
|
while(next_header.patch_size>0)
|
|
{
|
|
_u32 r=patch->Read((char*)buf, (std::min)((unsigned int)buffer_size, next_header.patch_size));
|
|
patchf_pos+=r;
|
|
cb->next_chunk_patcher_bytes(buf, r, true);
|
|
next_header.patch_size-=r;
|
|
}
|
|
if(require_unchanged)
|
|
{
|
|
file->Seek(file_pos);
|
|
}
|
|
next_header.patch_off=-1;
|
|
}
|
|
else if(file_pos<size && file_pos<filesize)
|
|
{
|
|
while(tr>0 && file_pos<size && file_pos<filesize)
|
|
{
|
|
if(file_pos+tr>filesize)
|
|
{
|
|
tr=static_cast<unsigned int>(filesize-file_pos);
|
|
}
|
|
|
|
if(require_unchanged)
|
|
{
|
|
_u32 r=file->Read((char*)buf, tr);
|
|
file_pos+=r;
|
|
cb->next_chunk_patcher_bytes(buf, r, false);
|
|
tr-=r;
|
|
}
|
|
else
|
|
{
|
|
if(file_pos+tr>size)
|
|
{
|
|
tr=static_cast<unsigned int>(size-file_pos);
|
|
}
|
|
|
|
file_pos+=tr;
|
|
cb->next_chunk_patcher_bytes(NULL, tr, false);
|
|
tr=0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Patch corrupt. file_pos="+nconvert(file_pos)+" next_header.patch_off="+nconvert(next_header.patch_off)+" next_header.patch_size="+nconvert(next_header.patch_size)+" tr="+nconvert(tr)+" size="+nconvert(size)+" filesize="+nconvert(filesize), LL_ERROR);
|
|
assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ChunkPatcher::readNextValidPatch(IFile *patchf, _i64 &patchf_pos, SPatchHeader *patch_header)
|
|
{
|
|
const unsigned int to_read=sizeof(_i64)+sizeof(unsigned int);
|
|
do
|
|
{
|
|
_u32 r=patchf->Read((char*)&patch_header->patch_off, to_read);
|
|
patchf_pos+=r;
|
|
if(r!=to_read)
|
|
{
|
|
patch_header->patch_off=-1;
|
|
patch_header->patch_size=0;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
patch_header->patch_off = little_endian(patch_header->patch_off);
|
|
patch_header->patch_size = little_endian(patch_header->patch_size);
|
|
}
|
|
|
|
if(patch_header->patch_off==-1)
|
|
{
|
|
patchf_pos+=patch_header->patch_size;
|
|
patchf->Seek(patchf_pos);
|
|
}
|
|
}
|
|
while(patch_header->patch_off==-1);
|
|
|
|
return true;
|
|
}
|
|
|
|
_i64 ChunkPatcher::getFilesize(void)
|
|
{
|
|
return filesize;
|
|
}
|
|
|
|
void ChunkPatcher::setRequireUnchanged( bool b )
|
|
{
|
|
require_unchanged=b;
|
|
}
|