/*************************************************************************
* UrBackup - Client/Server backup system
* Copyright (C) 2011-2014 Martin Raiber
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
**************************************************************************/
#ifndef CLIENT_ONLY
#include "server_prepare_hash.h"
#include "server_hash.h"
#include "../common/data.h"
#include "../Interface/Server.h"
#include "../stringtools.h"
#include "server_log.h"
#include "../urbackupcommon/os_functions.h"
#include "../fileservplugin/chunk_settings.h"
#include "../md5.h"
#include
#include "../common/adler32.h"
BackupServerPrepareHash::BackupServerPrepareHash(IPipe *pPipe, IPipe *pOutput, int pClientid)
{
pipe=pPipe;
output=pOutput;
clientid=pClientid;
working=false;
chunk_patcher.setCallback(this);
has_error=false;
}
BackupServerPrepareHash::~BackupServerPrepareHash(void)
{
Server->destroy(pipe);
}
void BackupServerPrepareHash::operator()(void)
{
while(true)
{
working=false;
std::string data;
size_t rc=pipe->Read(&data);
if(data=="exit")
{
output->Write("exit");
Server->Log("server_prepare_hash Thread finished (exit)");
delete this;
return;
}
else if(data=="flush")
{
continue;
}
if(rc>0)
{
working=true;
CRData rd(&data);
std::string temp_fn;
rd.getStr(&temp_fn);
int backupid;
rd.getInt(&backupid);
char incremental;
rd.getChar(&incremental);
std::string tfn;
rd.getStr(&tfn);
std::string hashpath;
rd.getStr(&hashpath);
std::string hashoutput_fn;
rd.getStr(&hashoutput_fn);
bool diff_file=!hashoutput_fn.empty();
std::string old_file_fn;
rd.getStr(&old_file_fn);
int64 t_filesize;
rd.getInt64(&t_filesize);
IFile *tf=Server->openFile(os_file_prefix(Server->ConvertToUnicode(temp_fn)), MODE_READ);
IFile *old_file=NULL;
if(diff_file)
{
old_file=Server->openFile(os_file_prefix(Server->ConvertToUnicode(old_file_fn)), MODE_READ);
if(old_file==NULL)
{
ServerLogger::Log(clientid, "Error opening file \""+old_file_fn+"\" from pipe for reading. File: old_file ec="+nconvert(os_last_error()), LL_ERROR);
has_error=true;
if(tf!=NULL) Server->destroy(tf);
continue;
}
}
if(tf==NULL)
{
ServerLogger::Log(clientid, "Error opening file \""+temp_fn+"\" from pipe for reading file. File: temp_fn ec="+nconvert(os_last_error()), LL_ERROR);
has_error=true;
if(old_file!=NULL)
{
Server->destroy(old_file);
}
}
else
{
ServerLogger::Log(clientid, "PT: Hashing file \""+ExtractFileName(tfn)+"\"", LL_DEBUG);
std::string h;
if(!diff_file)
{
h=hash_sha512(tf);
}
else
{
h=hash_with_patch(old_file, tf);
}
Server->destroy(tf);
if(old_file!=NULL)
{
Server->destroy(old_file);
}
CWData data;
data.addInt(BackupServerHash::EAction_LinkOrCopy);
data.addString(temp_fn);
data.addInt(backupid);
data.addChar(incremental);
data.addString(tfn);
data.addString(hashpath);
data.addString(h);
data.addString(hashoutput_fn);
data.addString(old_file_fn);
data.addInt64(t_filesize);
output->Write(data.getDataPtr(), data.getDataSize() );
}
}
}
}
std::string BackupServerPrepareHash::hash_sha512(IFile *f)
{
f->Seek(0);
unsigned char buf[32768];
_u32 rc;
sha512_ctx local_ctx;
sha512_init(&local_ctx);
do
{
rc=f->Read((char*)buf, 32768);
if(rc>0)
sha512_update(&local_ctx, buf, rc);
}
while(rc>0);
std::string ret;
ret.resize(64);
sha512_final(&local_ctx, (unsigned char*)&ret[0]);
return ret;
}
std::string BackupServerPrepareHash::hash_with_patch(IFile *f, IFile *patch)
{
sha512_init(&ctx);
chunk_patcher.ApplyPatch(f, patch);
std::string ret;
ret.resize(64);
sha512_final(&ctx, (unsigned char*)&ret[0]);
return ret;
}
void BackupServerPrepareHash::next_chunk_patcher_bytes(const char *buf, size_t bsize, bool changed)
{
sha512_update(&ctx, (const unsigned char*)buf, (unsigned int)bsize);
}
bool BackupServerPrepareHash::isWorking(void)
{
return working;
}
std::string BackupServerPrepareHash::build_chunk_hashs(IFile *f, IFile *hashoutput, INotEnoughSpaceCallback *cb, bool ret_sha2, IFile *copy, bool modify_inplace)
{
f->Seek(0);
hashoutput->Seek(0);
_i64 fsize=f->Size();
if(!writeRepeatFreeSpace(hashoutput, (char*)&fsize, sizeof(_i64), cb))
return "";
sha512_ctx ctx;
if(ret_sha2)
sha512_init(&ctx);
_i64 n_chunks=c_checkpoint_dist/c_small_hash_dist;
char buf[c_small_hash_dist];
char copy_buf[c_small_hash_dist];
_i64 copy_write_pos=0;
char zbuf[big_hash_size]={};
_i64 hashoutputpos=sizeof(_i64);
for(_i64 pos=0;posRead(buf, c_small_hash_dist);
_u32 small_hash=urb_adler32(urb_adler32(0, NULL, 0), buf, r);
big_hash.update((unsigned char*)buf, r);
if(!writeRepeatFreeSpace(hashoutput, (char*)&small_hash, small_hash_size, cb))
return "";
hashoutputpos+=small_hash_size;
if(ret_sha2)
{
sha512_update(&ctx, (unsigned char*)buf, r);
}
if(copy!=NULL)
{
if(modify_inplace)
{
_u32 copy_r=copy->Read(copy_buf, c_small_hash_dist);
if(copy_r!=r || memcmp(copy_buf, buf, r)!=0)
{
copy->Seek(copy_write_pos);
if(!writeRepeatFreeSpace(copy, buf, r, cb) )
return "";
}
copy_write_pos+=r;
}
else
{
if(!writeRepeatFreeSpace(copy, buf, r, cb) )
return "";
}
}
}
hashoutput->Seek(hashoutputpos_start);
big_hash.finalize();
if(!writeRepeatFreeSpace(hashoutput, (const char*)big_hash.raw_digest_int(), big_hash_size, cb))
return "";
hashoutput->Seek(hashoutputpos);
}
if(ret_sha2)
{
std::string ret;
ret.resize(64);
sha512_final(&ctx, (unsigned char*)&ret[0]);
return ret;
}
else
{
return "k";
}
}
bool BackupServerPrepareHash::writeRepeatFreeSpace(IFile *f, const char *buf, size_t bsize, INotEnoughSpaceCallback *cb)
{
if( cb==NULL)
return writeFileRepeat(f, buf, bsize);
int rc=f->Write(buf, (_u32)bsize);
if(rc!=bsize)
{
if(cb!=NULL && cb->handle_not_enough_space(f->getFilenameW()) )
{
_u32 written=rc;
do
{
rc=f->Write(buf+written, (_u32)bsize-written);
written+=rc;
}
while(written0);
if(rc==0) return false;
}
else
{
return false;
}
}
return true;
}
bool BackupServerPrepareHash::writeFileRepeat(IFile *f, const char *buf, size_t bsize)
{
_u32 written=0;
_u32 rc;
int tries=50;
do
{
rc=f->Write(buf+written, (_u32)(bsize-written));
written+=rc;
if(rc==0)
{
Server->wait(10000);
--tries;
}
}
while(written0 || tries>0) );
if(rc==0)
{
return false;
}
return true;
}
bool BackupServerPrepareHash::hasError(void)
{
volatile bool r=has_error;
has_error=false;
return r;
}
#endif //CLIENT_ONLY