/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2011 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 . **************************************************************************/ #include "ClientService.h" #include "client.h" #include "../Interface/Server.h" #include "../Interface/Database.h" #include "../Interface/Query.h" #include "../Interface/SettingsReader.h" #include "../Interface/File.h" #include "../Interface/ThreadPool.h" #include "../stringtools.h" #include "../urbackupcommon/escape.h" #include "database.h" #include "../urbackupcommon/fileclient/data.h" #include "../fsimageplugin/IFSImageFactory.h" #include "../fsimageplugin/IFilesystem.h" #include "../cryptoplugin/ICryptoFactory.h" #include "../urbackupcommon/sha2/sha2.h" #include "ClientSend.h" #include "ServerIdentityMgr.h" #include "../urbackupcommon/settings.h" #include "../urbackupcommon/capa_bits.h" #ifdef _WIN32 #include "win_sysvol.h" #else std::wstring getSysVolume(std::wstring &mpath){ return L""; } #endif #include #include #include #ifndef _WIN32 #define _atoi64 atoll #endif extern IFSImageFactory *image_fak; extern ICryptoFactory *crypto_fak; extern unsigned char *zero_hash; extern std::string time_format_str; ICustomClient* ClientService::createClient() { return new ClientConnector(); } void ClientService::destroyClient( ICustomClient * pClient) { delete ((ClientConnector*)pClient); } RunningAction ClientConnector::backup_running=RUNNING_NONE; volatile bool ClientConnector::backup_done=false; IMutex *ClientConnector::backup_mutex=NULL; unsigned int ClientConnector::incr_update_intervall=0; unsigned int ClientConnector::last_pingtime=0; IPipe *ClientConnector::channel_pipe=NULL; int ClientConnector::pcdone=0; int ClientConnector::pcdone2=0; std::vector ClientConnector::channel_pipes; std::vector ClientConnector::channel_exit; std::vector ClientConnector::channel_ping; std::vector ClientConnector::channel_capa; IMutex *ClientConnector::progress_mutex=NULL; volatile bool ClientConnector::img_download_running=false; db_results ClientConnector::cached_status; std::string ClientConnector::backup_source_token; std::map ClientConnector::last_token_times; int ClientConnector::last_capa=0; const unsigned int x_pingtimeout=180000; #ifdef _WIN32 const std::string pw_file="pw.txt"; #else const std::string pw_file="urbackup/pw.txt"; #endif void ClientConnector::init_mutex(void) { if(backup_mutex==NULL) { backup_mutex=Server->createMutex(); progress_mutex=Server->createMutex(); } IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); db_results res=db->Read("SELECT tvalue FROM misc WHERE tkey='last_capa'"); if(!res.empty()) { last_capa=watoi(res[0][L"last_capa"]); } else { db->Write("INSERT INTO misc (tkey, tvalue) VALUES ('last_capa', '0');"); last_capa=0; } } bool ClientConnector::wantReceive(void) { return want_receive; } void ClientConnector::Init(THREAD_ID pTID, IPipe *pPipe) { tid=pTID; pipe=pPipe; state=CCSTATE_NORMAL; thread_action=TA_NONE; mempipe=Server->createMemoryPipe(); lasttime=Server->getTimeMS(); do_quit=false; is_channel=false; want_receive=true; last_channel_ping=0; file_version=1; } ClientConnector::~ClientConnector(void) { mempipe->Write("exit"); } bool ClientConnector::Run(void) { if(do_quit) { if(is_channel) { IScopedLock lock(backup_mutex); if(channel_pipe==pipe) { channel_pipe=NULL; } for(size_t i=0;iwait(10); do_quit=true; return true; } return false; } switch(state) { case CCSTATE_NORMAL: if(Server->getTimeMS()-lasttime>10000) { Server->Log("Client timeout in ClientConnector::Run", LL_DEBUG); if(waitForThread()) { do_quit=true; return true; } return false; } return true; case CCSTATE_START_FILEBACKUP: { std::string msg; mempipe->Read(&msg, 0); if(msg=="exit") { mempipe->Write(msg); if(waitForThread()) { do_quit=true; return true; } IScopedLock lock(backup_mutex); backup_running=RUNNING_NONE; backup_done=true; return false; } else if(msg=="done") { tcpstack.Send(pipe, "DONE"); lasttime=Server->getTimeMS(); state=CCSTATE_NORMAL; } else if(!msg.empty()) { tcpstack.Send(pipe, msg); lasttime=Server->getTimeMS(); state=CCSTATE_NORMAL; IScopedLock lock(backup_mutex); backup_running=RUNNING_NONE; backup_done=true; } else if(file_version>1 && Server->getTimeMS()-last_update_time>30000) { last_update_time=Server->getTimeMS(); tcpstack.Send(pipe, "BUSY"); } }break; case CCSTATE_SHADOWCOPY: { std::string msg; mempipe->Read(&msg, 0); if(msg=="exit") { mempipe->Write(msg); if(waitForThread()) { do_quit=true; return true; } return false; } else if(msg.find("done")==0) { tcpstack.Send(pipe, "DONE"); lasttime=Server->getTimeMS(); state=CCSTATE_NORMAL; } else if(msg.find("failed")==0) { tcpstack.Send(pipe, "FAILED"); lasttime=Server->getTimeMS(); state=CCSTATE_NORMAL; } }break; case CCSTATE_CHANNEL: //Channel { IScopedLock lock(backup_mutex); if(Server->getTimeMS()-lasttime>180000) { Server->Log("Client timeout in ClientConnector::Run - Channel", LL_DEBUG); { if(channel_pipe==pipe) channel_pipe=NULL; for(size_t i=0;igetTimeMS()-last_channel_ping>60000) { bool found=false; for(size_t i=0;igetTimeMS(); } }break; case CCSTATE_IMAGE: case CCSTATE_IMAGE_HASHDATA: { if(Server->getThreadPool()->isRunning(thread_ticket)==false ) { return false; } }break; case CCSTATE_UPDATE_DATA: case CCSTATE_UPDATE_FINISH: { if(Server->getTimeMS()-lasttime>10000) { Server->Log("Client timeout in ClientConnector::Run-update(state6|7)", LL_DEBUG); if(waitForThread()) { do_quit=true; return true; } return false; } if(state==CCSTATE_UPDATE_FINISH) { if(hashdataok) { hashdatafile->Seek(0); writeUpdateFile(hashdatafile, "version_new.txt"); writeUpdateFile(hashdatafile, "UrBackupUpdate.sig"); writeUpdateFile(hashdatafile, "UrBackupUpdate_untested.exe"); if(crypto_fak!=NULL) { IFile *updatefile=Server->openFile("UrBackupUpdate_untested.exe"); if(updatefile!=NULL) { if(checkHash(getSha512Hash(updatefile))) { Server->destroy(updatefile); if(crypto_fak->verifyFile("urbackup_dsa.pub", "UrBackupUpdate_untested.exe", "UrBackupUpdate.sig")) { Server->deleteFile("version.txt"); Server->deleteFile("UrBackupUpdate.exe"); moveFile(L"UrBackupUpdate_untested.exe", L"UrBackupUpdate.exe"); moveFile(L"version_new.txt", L"version.txt"); tcpstack.Send(pipe, "ok"); } else { Server->Log("Verifying update file failed. Signature did not match", LL_ERROR); tcpstack.Send(pipe, "verify_sig_err"); } } else { Server->destroy(updatefile); Server->Log("Verifing update file failed. Update was installed previously", LL_ERROR); tcpstack.Send(pipe, "verify_sig_already_used_err"); } } } else { Server->Log("Verifing update file failed. Cryptomodule not present", LL_ERROR); tcpstack.Send(pipe, "verify_cryptmodule_err"); } } else { do_quit=true; } } return true; }break; case CCSTATE_WAIT_FOR_CONTRACTORS: // wait for contractors { for(size_t i=0;iRead(&resp, 0); if(!resp.empty()) { contractors[i]->Write("exit"); contractors.erase(contractors.begin()+i); break; } } if(contractors.empty()) { return false; } }break; } return true; } std::string ClientConnector::getSha512Hash(IFile *fn) { sha512_ctx ctx; char buf[4096]; _u32 r; sha512_init(&ctx); while((r=fn->Read(buf, 4096))!=0) { sha512_update(&ctx, (unsigned char*)buf, r); } unsigned char digest[64]; sha512_final(&ctx,digest); return bytesToHex(digest, 64); } bool ClientConnector::checkHash(std::string shah) { std::string prev_h=getFile("updates_h.dat"); int lc=linecount(prev_h); for(int i=0;iRead((char*)&size, sizeof(unsigned int))!=sizeof(unsigned int)) return false; IFile *out=Server->openFile(outfn, MODE_WRITE); if(out==NULL) return false; size_t read=0; char buf[4096]; while(true) { size_t tr=(std::min)((size_t)4096, size-read); if(tr==0) break; if(datafile->Read(buf, (_u32)tr)!=tr) { Server->destroy(out); return false; } if(out->Write(buf, (_u32)tr)!=tr) { Server->destroy(out); return false; } read+=tr; } Server->destroy(out); return true; } void ClientConnector::ReceivePackets(void) { if(state==CCSTATE_WAIT_FOR_CONTRACTORS) { return; } IMutex *l_mutex=NULL; if(is_channel) { l_mutex=backup_mutex; } IScopedLock g_lock(l_mutex); std::string cmd; size_t rc=pipe->Read(&cmd, is_channel?0:-1); if(rc==0 ) { Server->Log("rc=0 hasError="+nconvert(pipe->hasError())+" state="+nconvert(state), LL_DEBUG); #ifdef _WIN32 #ifdef _DEBUG Server->Log("Err: "+nconvert((int)GetLastError()), LL_DEBUG); #endif #endif if(is_channel && pipe->hasError()) { do_quit=true; } else if(!is_channel) { do_quit=true; } else { lasttime=Server->getTimeMS(); } return; } if(state==CCSTATE_IMAGE_HASHDATA || state==CCSTATE_UPDATE_DATA) { lasttime=Server->getTimeMS(); if(hashdatafile->Write(cmd)!=cmd.size()) { Server->Log("Error writing to hashdata temporary file", LL_ERROR); do_quit=true; return; } if(hashdataleft>=cmd.size()) { hashdataleft-=(_u32)cmd.size(); Server->Log("Hashdataleft: "+nconvert(hashdataleft), LL_DEBUG); } else { Server->Log("Too much hashdata - error", LL_ERROR); } if(hashdataleft==0) { hashdataok=true; if(state==CCSTATE_IMAGE_HASHDATA) state=CCSTATE_IMAGE; else if(state==CCSTATE_UPDATE_DATA) state=CCSTATE_UPDATE_FINISH; return; } } tcpstack.AddData((char*)cmd.c_str(), cmd.size()); size_t packetsize; char *ccstr; while( (ccstr=tcpstack.getPacket(&packetsize))!=NULL) { cmd.resize(packetsize); if(packetsize>0) { memcpy(&cmd[0], ccstr, packetsize); } delete [] ccstr; Server->Log("ClientService cmd: "+cmd, LL_DEBUG); bool pw_ok=false; std::string identity; bool ident_ok=false; str_map params; size_t hashpos; if(cmd.size()>3 && cmd[0]=='#' && cmd[1]=='I' ) { identity=getbetween("#I", "#", cmd); cmd.erase(0, identity.size()+3); size_t tp=cmd.find("#token="); if(tp!=std::string::npos) { server_token=cmd.substr(tp+7); cmd.erase(tp, cmd.size()-tp); } } else if((hashpos=cmd.find("#"))!=std::string::npos) { ParseParamStr(getafter("#", cmd), ¶ms); cmd.erase(hashpos, cmd.size()-hashpos); if(!checkPassword(params[L"pw"])) { Server->Log("Password wrong!", LL_ERROR); break; } else { pw_ok=true; } } if(!identity.empty() && ServerIdentityMgr::checkServerIdentity(identity)==true) { ident_ok=true; } if(cmd=="ADD IDENTITY") { if(identity.empty()) { tcpstack.Send(pipe, "Identity empty"); } else { if(Server->getServerParameter("restore_mode")=="true" && !ident_ok ) { ServerIdentityMgr::addServerIdentity(identity); tcpstack.Send(pipe, "OK"); } else if( ident_ok ) { tcpstack.Send(pipe, "OK"); } else { if( ServerIdentityMgr::numServerIdentities()==0 ) { ServerIdentityMgr::addServerIdentity(identity); tcpstack.Send(pipe, "OK"); } else { tcpstack.Send(pipe, "failed"); } } } } else if( (cmd=="START BACKUP" || cmd=="2START BACKUP") && ident_ok==true) { if(cmd=="2START BACKUP") file_version=2; state=CCSTATE_START_FILEBACKUP; CWData data; data.addChar(0); data.addVoidPtr(mempipe); data.addString(server_token); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); lasttime=Server->getTimeMS(); IScopedLock lock(backup_mutex); backup_running=RUNNING_INCR_FILE; last_pingtime=Server->getTimeMS(); pcdone=0; backup_source_token=server_token; } else if( (cmd=="START FULL BACKUP" || cmd=="2START FULL BACKUP") && ident_ok==true) { if(cmd=="2START FULL BACKUP") file_version=2; state=CCSTATE_START_FILEBACKUP; CWData data; data.addChar(1); data.addVoidPtr(mempipe); data.addString(server_token); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); lasttime=Server->getTimeMS(); IScopedLock lock(backup_mutex); backup_running=RUNNING_FULL_FILE; last_pingtime=Server->getTimeMS(); pcdone=0; backup_source_token=server_token; } else if(cmd.find("START SC \"")!=std::string::npos && ident_ok==true) { #ifdef _WIN32 if(cmd[cmd.size()-1]=='"') { state=CCSTATE_SHADOWCOPY; std::string dir=cmd.substr(10, cmd.size()-11); CWData data; data.addChar(2); data.addVoidPtr(mempipe); data.addString(dir); data.addString(server_token); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); lasttime=Server->getTimeMS(); } else { Server->Log("Invalid command", LL_ERROR); } #else tcpstack.Send(pipe, "DONE"); #endif } else if(cmd.find("STOP SC \"")!=std::string::npos && ident_ok==true) { #ifdef _WIN32 if(cmd[cmd.size()-1]=='"') { state=CCSTATE_SHADOWCOPY; std::string dir=cmd.substr(9, cmd.size()-10); CWData data; data.addChar(3); data.addVoidPtr(mempipe); data.addString(dir); data.addString(server_token); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); lasttime=Server->getTimeMS(); } else { Server->Log("Invalid command", LL_ERROR); } #else tcpstack.Send(pipe, "DONE"); #endif } else if(cmd.find("INCRINTERVALL \"")!=std::string::npos && ident_ok==true) { if(cmd[cmd.size()-1]=='"') { std::string intervall=cmd.substr(15, cmd.size()-16); incr_update_intervall=atoi(intervall.c_str()); tcpstack.Send(pipe, "OK"); lasttime=Server->getTimeMS(); } else { Server->Log("Invalid command", LL_ERROR); } } else if(cmd.find("1GET BACKUP DIRS")==0 && pw_ok==true) { getBackupDirs(1); lasttime=Server->getTimeMS(); } else if(cmd.find("GET BACKUP DIRS")==0 && pw_ok==true) { getBackupDirs(); lasttime=Server->getTimeMS(); } else if(cmd.find("SAVE BACKUP DIRS")==0 && pw_ok==true) { if(saveBackupDirs(params)) { tcpstack.Send(pipe, "OK"); } lasttime=Server->getTimeMS(); } else if(cmd.find("GET INCRINTERVALL")==0 && pw_ok==true) { if(incr_update_intervall==0 ) { tcpstack.Send(pipe, nconvert(0)); } else { tcpstack.Send(pipe, nconvert(incr_update_intervall+10*60) ); } lasttime=Server->getTimeMS(); } else if(cmd=="DID BACKUP" && ident_ok==true) { updateLastBackup(); tcpstack.Send(pipe, "OK"); { IScopedLock lock(backup_mutex); if(backup_running==RUNNING_INCR_FILE || backup_running==RUNNING_FULL_FILE) { backup_running=RUNNING_NONE; backup_done=true; } lasttime=Server->getTimeMS(); } IndexThread::execute_postbackup_hook(); } else if(cmd.find("STATUS")==0 && pw_ok==true) { getBackupStatus(); lasttime=Server->getTimeMS(); } else if(cmd.find("SETTINGS ")==0 && ident_ok==true) { std::string s_settings=cmd.substr(9); unescapeMessage(s_settings); updateSettings( s_settings ); tcpstack.Send(pipe, "OK"); lasttime=Server->getTimeMS(); } else if(cmd.find("PING RUNNING")==0 && ident_ok==true) { tcpstack.Send(pipe, "OK"); IScopedLock lock(backup_mutex); lasttime=Server->getTimeMS(); last_pingtime=Server->getTimeMS(); int pcdone_new=atoi(getbetween("-","-", cmd).c_str()); if(backup_source_token.empty() || backup_source_token==server_token ) { pcdone=pcdone_new; } last_token_times[server_token]=Server->getTimeSeconds(); #ifdef _WIN32 SetThreadExecutionState(ES_SYSTEM_REQUIRED); #endif } else if( (cmd=="CHANNEL" || cmd.find("1CHANNEL")==0 ) && ident_ok==true) { if(!img_download_running) { g_lock.relock(backup_mutex); channel_pipe=pipe; channel_pipes.push_back(pipe); is_channel=true; state=CCSTATE_CHANNEL; last_channel_ping=Server->getTimeMS(); lasttime=Server->getTimeMS(); Server->Log("New channel: Number of Channels: "+nconvert((int)channel_pipes.size()), LL_DEBUG); int capa=0; if(cmd.find("1CHANNEL ")==0) { std::string s_params=cmd.substr(9); str_map params; ParseParamStr(s_params, ¶ms); capa=watoi(params[L"capa"]); } channel_capa.push_back(capa); } } else if(cmd=="PONG" && is_channel==true ) { lasttime=Server->getTimeMS(); IScopedLock lock(backup_mutex); for(size_t i=0;igetTimeMS(); if(tcpstack.Send(pipe, "PONG")==0) { do_quit=true; } } else if(cmd.find("START BACKUP INCR")==0 && pw_ok==true ) { IScopedLock lock(backup_mutex); lasttime=Server->getTimeMS(); if(backup_running!=RUNNING_NONE && Server->getTimeMS()-last_pingtimegetTimeMS(); if(backup_running!=RUNNING_NONE && Server->getTimeMS()-last_pingtimegetTimeMS(); if(backup_running!=RUNNING_NONE && Server->getTimeMS()-last_pingtimegetTimeMS(); if(backup_running!=RUNNING_NONE && Server->getTimeMS()-last_pingtimegetTimeMS(); unescapeMessage(s_settings); replaceSettings( s_settings ); IScopedLock lock(backup_mutex); bool ok=false; for(size_t o=0;ogetTimeMS(); saveLogdata(created, getafter(" ",ldata)); tcpstack.Send(pipe, "OK"); } else if(cmd.find("PAUSE ")==0 && pw_ok==true) { lasttime=Server->getTimeMS(); std::string b=cmd.substr(6); bool ok=false; if(b=="true") { ok=true; IdleCheckerThread::setPause(true); IndexThread::getFileSrv()->setPause(true); } else if(b=="false") { ok=true; IdleCheckerThread::setPause(false); IndexThread::getFileSrv()->setPause(false); } if(ok) tcpstack.Send(pipe, "OK"); else tcpstack.Send(pipe, "FAILED"); } else if(cmd.find("GET LOGPOINTS")==0 && pw_ok==true) { lasttime=Server->getTimeMS(); tcpstack.Send(pipe, getLogpoints() ); } else if(cmd.find("GET LOGDATA")==0 && pw_ok==true ) { lasttime=Server->getTimeMS(); int logid=watoi(params[L"logid"]); int loglevel=watoi(params[L"loglevel"]); std::string ret; getLogLevel(logid, loglevel, ret); tcpstack.Send(pipe, ret); } else if(cmd.find("FULL IMAGE ")==0) { if(ident_ok==true) { lasttime=Server->getTimeMS(); std::string s_params=cmd.substr(11); str_map params; ParseParamStr(s_params, ¶ms); server_token=Server->ConvertToUTF8(params[L"token"]); image_letter=Server->ConvertToUTF8(params[L"letter"]); shadowdrive=Server->ConvertToUTF8(params[L"shadowdrive"]); if(params.find(L"start")!=params.end()) { startpos=(uint64)_atoi64(Server->ConvertToUTF8(params[L"start"]).c_str()); } else { startpos=0; } if(params.find(L"shadowid")!=params.end()) { shadow_id=watoi(params[L"shadowid"]); } else { shadow_id=-1; } no_shadowcopy=false; if(image_letter=="SYSVOL") { std::wstring mpath; std::wstring sysvol=getSysVolume(mpath); if(!mpath.empty()) { image_letter=Server->ConvertToUTF8(mpath); } else { image_letter=Server->ConvertToUTF8(sysvol); no_shadowcopy=true; } } if(startpos==0 && !no_shadowcopy) { CWData data; data.addChar(2); data.addVoidPtr(mempipe); data.addString(image_letter); data.addString(server_token); data.addUChar(1); //image backup data.addUChar(0); //filesrv IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); } else if(shadow_id!=-1) { shadowdrive.clear(); CWData data; data.addChar(4); data.addVoidPtr(mempipe); data.addInt(shadow_id); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); } if(no_shadowcopy) { shadowdrive=image_letter; if(!shadowdrive.empty() && shadowdrive[0]!='\\') { shadowdrive="\\\\.\\"+image_letter; } } lasttime=Server->getTimeMS(); sendFullImage(); } else { ImageErr("Ident reset (1)"); } } else if(cmd.find("INCR IMAGE ")==0 && ident_ok==true) { if(ident_ok==true) { lasttime=Server->getTimeMS(); std::string s_params=cmd.substr(11); str_map params; ParseParamStr(s_params, ¶ms); server_token=Server->ConvertToUTF8(params[L"token"]); str_map::iterator f_hashsize=params.find(L"hashsize"); if(f_hashsize!=params.end()) { hashdataok=false; hashdataleft=watoi(f_hashsize->second); image_letter=Server->ConvertToUTF8(params[L"letter"]); shadowdrive=Server->ConvertToUTF8(params[L"shadowdrive"]); if(params.find(L"start")!=params.end()) { startpos=(uint64)_atoi64(Server->ConvertToUTF8(params[L"start"]).c_str()); } else { startpos=0; } if(params.find(L"shadowid")!=params.end()) { shadow_id=watoi(params[L"shadowid"]); } else { shadow_id=-1; } no_shadowcopy=false; if(startpos==0) { CWData data; data.addChar(2); data.addVoidPtr(mempipe); data.addString(image_letter); data.addString(server_token); data.addUChar(1); //image backup data.addUChar(0); //file serv? IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); } else if(shadow_id!=-1) { shadowdrive.clear(); CWData data; data.addChar(4); data.addVoidPtr(mempipe); data.addInt(shadow_id); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); } hashdatafile=Server->openTemporaryFile(); if(hashdatafile==NULL) continue; if(tcpstack.getBuffersize()>0) { if(hashdatafile->Write(tcpstack.getBuffer(), (_u32)tcpstack.getBuffersize())!=tcpstack.getBuffersize()) { Server->Log("Error writing to hashdata temporary file -1", LL_ERROR); do_quit=true; return; } if(hashdataleft>=tcpstack.getBuffersize()) { hashdataleft-=(_u32)tcpstack.getBuffersize(); //Server->Log("Hashdataleft: "+nconvert(hashdataleft), LL_DEBUG); } else { Server->Log("Too much hashdata - error -1", LL_ERROR); } if(hashdataleft==0) { hashdataok=true; state=CCSTATE_IMAGE; return; } } lasttime=Server->getTimeMS(); sendIncrImage(); } else { ImageErr("Ident reset (2)"); } } } else if( cmd.find("MBR ")==0 && ident_ok==true ) { lasttime=Server->getTimeMS(); std::string s_params=cmd.substr(4); str_map params; ParseParamStr(s_params, ¶ms); std::wstring dl=params[L"driveletter"]; if(dl==L"SYSVOL") { std::wstring mpath; dl=getSysVolume(mpath); } bool b=false; if(!dl.empty()) { b=sendMBR(dl); } if(!b) { CWData r;r.addChar(0); tcpstack.Send(pipe, r); } } else if( cmd.find("GET BACKUPCLIENTS")==0 && pw_ok==true ) { lasttime=Server->getTimeMS(); IScopedLock lock(backup_mutex); waitForPings(&lock); if(channel_pipes.size()==0) { tcpstack.Send(pipe, "0"); } else { std::string clients; for(size_t i=0;ihasError()) Server->Log("Channel has error after request -1", LL_DEBUG); std::string nc=receivePacket(channel_pipes[i]); if(channel_pipes[i]->hasError()) Server->Log("Channel has error after read -1", LL_DEBUG); Server->Log("Client "+nconvert(i)+"/"+nconvert(channel_pipes.size())+": --"+nc+"--", LL_DEBUG); if(!nc.empty()) { clients+=nc+"\n"; } } tcpstack.Send(pipe, "1"+clients); } } else if( cmd.find("GET BACKUPIMAGES ")==0 && pw_ok==true ) { lasttime=Server->getTimeMS(); IScopedLock lock(backup_mutex); waitForPings(&lock); if(channel_pipes.size()==0) { tcpstack.Send(pipe, "0"); } else { std::string imgs; for(size_t i=0;igetTimeMS(); Server->Log("Downloading image...", LL_DEBUG); IScopedLock lock(backup_mutex); waitForPings(&lock); Server->Log("In mutex...",LL_DEBUG); img_download_running=true; downloadImage(params); img_download_running=false; Server->Log("Donwload done -2", LL_DEBUG); do_quit=true; } else if( cmd.find("GET DOWNLOADPROGRESS")==0 && pw_ok==true) { Server->Log("Sending progress...", LL_DEBUG); lasttime=Server->getTimeMS(); if(img_download_running==false) { pipe->Write("100"); do_quit=true; } else { while(img_download_running) { { int progress=0; { IScopedLock lock(progress_mutex); progress=pcdone; } if(!pipe->Write(nconvert(progress)+"\n", 10000)) { break; } } { Server->wait(1000); } } do_quit=true; } } else if( cmd.find("VERSION ")==0 && ident_ok==true ) { int n_version=atoi(cmd.substr(8).c_str()); std::string version_1=getFile("version.txt"); std::string version_2=getFile("curr_version.txt"); if(version_1.empty() ) version_1="0"; if(version_2.empty() ) version_2="0"; #ifdef _WIN32 if( atoi(version_1.c_str())openTemporaryFile(); if(hashdatafile==NULL) continue; hashdataleft=atoi(cmd.substr(13).c_str()); hashdataok=false; state=CCSTATE_UPDATE_DATA; if(tcpstack.getBuffersize()>0) { if(hashdatafile->Write(tcpstack.getBuffer(), (_u32)tcpstack.getBuffersize())!=tcpstack.getBuffersize()) { Server->Log("Error writing to hashdata temporary file -1update", LL_ERROR); do_quit=true; return; } if(hashdataleft>=tcpstack.getBuffersize()) { hashdataleft-=(_u32)tcpstack.getBuffersize(); //Server->Log("Hashdataleft: "+nconvert(hashdataleft), LL_DEBUG); } else { Server->Log("Too much hashdata - error -1update", LL_ERROR); } if(hashdataleft==0) { hashdataok=true; state=CCSTATE_UPDATE_FINISH; return; } } return; } else if(cmd.find("CAPA")==0 && ident_ok==true) { #ifdef _WIN32 tcpstack.Send(pipe, "FILE=2&IMAGE=1&UPDATE=1&MBR=1&FILESRV=1"); #else tcpstack.Send(pipe, "FILE=2&FILESRV=1"); #endif } else { tcpstack.Send(pipe, "ERR"); } } } bool ClientConnector::checkPassword(const std::wstring &pw) { static std::string stored_pw=getFile(pw_file); return stored_pw==Server->ConvertToUTF8(pw); } void ClientConnector::getBackupDirs(int version) { IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); IQuery *q=db->Prepare("SELECT id,name,path FROM backupdirs"); int timeoutms=300; db_results res=q->Read(&timeoutms); if(timeoutms==0) { std::string msg; for(size_t i=0;iConvertToUTF8(res[i][L"id"])+"\n"; if(version!=0) { msg+=Server->ConvertToUTF8(res[i][L"name"])+"|"; } msg+=Server->ConvertToUTF8(res[i][L"path"]); if(i+1shutdown(); } db->destroyAllQueries(); } std::wstring removeChars(std::wstring in) { wchar_t illegalchars[] = {'*', ':', '/' , '\\'}; std::wstring ret; for(size_t i=0;igetDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); db->BeginTransaction(); db_results backupdirs=db->Prepare("SELECT name, path FROM backupdirs")->Read(); db->Prepare("DELETE FROM backupdirs")->Write(); IQuery *q=db->Prepare("INSERT INTO backupdirs (name, path, server_default) VALUES (?, ? ,"+nconvert(server_default?1:0)+")"); if(server_default==false) { q->Bind(L"*"); q->Bind(L"*");; q->Write(); q->Reset(); } IQuery *q2=db->Prepare("SELECT id FROM backupdirs WHERE name=?"); std::wstring dir; size_t i=0; std::vector new_watchdirs; do { dir=args[L"dir_"+convert(i)]; if(!dir.empty()) { std::wstring name; str_map::iterator name_arg=args.find(L"dir_"+convert(i)+L"_name"); if(name_arg!=args.end() && !name_arg->second.empty()) name=name_arg->second; else name=ExtractFileName(dir); name=removeChars(name); if(dir[dir.size()-1]=='\\' || dir[dir.size()-1]=='/' ) dir.erase(dir.size()-1,1); q2->Bind(name); if(q2->Read().empty()==false) { for(int k=0;k<100;++k) { q2->Reset(); q2->Bind(name+L"_"+convert(k)); if(q2->Read().empty()==true) { name+=L"_"+convert(k); break; } } } q2->Reset(); bool found=false; for(size_t i=0;iBind(name); q->Bind(dir); q->Write(); q->Reset(); } ++i; } while(!dir.empty()); db->EndTransaction(); #ifdef _WIN32 for(size_t i=0;icreateMemoryPipe(); CWData data; data.addChar(5); data.addVoidPtr(contractor); data.addString(Server->ConvertToUTF8(new_watchdirs[i])); IndexThread::stopIndex(); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); contractors.push_back(contractor); state=CCSTATE_WAIT_FOR_CONTRACTORS; want_receive=false; } for(size_t i=0;icreateMemoryPipe(); CWData data; data.addChar(6); data.addVoidPtr(contractor); data.addString(Server->ConvertToUTF8(backupdirs[i][L"path"])); IndexThread::stopIndex(); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); contractors.push_back(contractor); state=CCSTATE_WAIT_FOR_CONTRACTORS; want_receive=false; } } #endif db->destroyAllQueries(); return true; } std::string ClientConnector::replaceChars(std::string in) { char legalchars[] = {'_', '-'}; for(size_t i=0;igetDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); IQuery *q=db->Prepare("UPDATE status SET last_backup=CURRENT_TIMESTAMP WHERE id=1"); IQuery *q2=db->Prepare("SELECT last_backup FROM status WHERE id=1"); if(q!=NULL && q2!=NULL) { if(q2->Read().size()>0) { q->Write(); } else { q=db->Prepare("INSERT INTO status (last_backup, id) VALUES (CURRENT_TIMESTAMP, 1)"); if(q!=NULL) q->Write(); } } db->destroyAllQueries(); } void ClientConnector::getBackupStatus(void) { IScopedLock lock(backup_mutex); IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); IQuery *q=db->Prepare("SELECT strftime('"+time_format_str+"',last_backup, 'localtime') AS last_backup FROM status"); if(q==NULL) return; int timeoutms=300; db_results res=q->Read(&timeoutms); if(timeoutms==1) { res=cached_status; } else { cached_status=res; } std::string ret; if(res.size()>0) { ret+=Server->ConvertToUTF8(res[0][L"last_backup"]); } if(backup_running==RUNNING_NONE) { if(backup_done) ret+="#DONE"; else ret+="#NOA"; backup_done=false; } else if(backup_running==RUNNING_INCR_FILE) { if(last_pingtime!=0 && Server->getTimeMS()-last_pingtime>x_pingtimeout ) ret+="#NOA"; else ret+="#INCR"; } else if(backup_running==RUNNING_FULL_FILE) { if(last_pingtime!=0 && Server->getTimeMS()-last_pingtime>x_pingtimeout ) ret+="#NOA"; else ret+="#FULL"; } else if(backup_running==RUNNING_FULL_IMAGE) { if(last_pingtime!=0 && Server->getTimeMS()-last_pingtime>x_pingtimeout ) ret+="#NOA"; else ret+="#FULLI"; } else if(backup_running==RUNNING_INCR_IMAGE) { if(last_pingtime!=0 && Server->getTimeMS()-last_pingtime>x_pingtimeout ) ret+="#NOA"; else ret+="#INCRI"; } if(backup_running!=RUNNING_INCR_IMAGE) ret+="#"+nconvert(pcdone); else ret+="#"+nconvert(pcdone2); if(IdleCheckerThread::getPause()) { ret+="#P"; } else { ret+="#NP"; } int capa=0; if(channel_capa.size()==0) { capa=last_capa; capa|=DONT_ALLOW_STARTING_FILE_BACKUPS; capa|=DONT_ALLOW_STARTING_IMAGE_BACKUPS; } else { capa=INT_MAX; for(size_t i=0;iPrepare("UPDATE misc SET tvalue=? WHERE tkey='last_capa'"); if(cq!=NULL) { cq->Bind(capa); cq->Write(); cq->Reset(); last_capa=capa; } } } ret+="#capa="+nconvert(capa); tcpstack.Send(pipe, ret); db->destroyAllQueries(); } std::vector getSettingsList(void); void ClientConnector::updateSettings(const std::string &pData) { ISettingsReader *curr_settings=Server->createFileSettingsReader("urbackup/data/settings.cfg"); ISettingsReader *new_settings=Server->createMemorySettingsReader(pData); std::vector settings_names=getSettingsList(); std::wstring new_settings_str=L""; bool mod=false; for(size_t i=0;igetValue(key, &v) ) { std::wstring nv; if(new_settings->getValue(key, &nv) ) { new_settings_str+=key+L"="+nv+L"\n"; mod=true; } else if(new_settings->getValue(key+L"_def", &nv) ) { new_settings_str+=key+L"_def="+nv+L"\n"; mod=true; } } else { new_settings_str+=key+L"="+v+L"\n"; } } IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); IQuery *q=db->Prepare("SELECT id FROM backupdirs WHERE server_default=0", false); db_results res=q->Read(); db->destroyQuery(q); if(res.empty()) { std::wstring default_dirs; if(!new_settings->getValue(L"default_dirs", &default_dirs) ) new_settings->getValue(L"default_dirs_def", &default_dirs); if(!default_dirs.empty()) { std::vector def_dirs_toks; Tokenize(default_dirs, def_dirs_toks, L";"); str_map args; for(size_t i=0;idestroy(curr_settings); Server->destroy(new_settings); if(mod) { IFile *sf=Server->openFile("urbackup/data/settings.cfg", MODE_WRITE ); if(sf==NULL) { Server->Log("Error opening settings file!", LL_ERROR); return; } sf->Write(Server->ConvertToUTF8(new_settings_str)); Server->destroy(sf); } } void ClientConnector::replaceSettings(const std::string &pData) { ISettingsReader *new_settings=Server->createMemorySettingsReader(pData); std::wstring ncname=new_settings->getValue(L"computername", L""); if(!ncname.empty() && ncname!=IndexThread::getFileSrv()->getServerName()) { CWData data; data.addChar(7); data.addVoidPtr(NULL); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); } Server->destroy(new_settings); IFile *sf=Server->openFile("urbackup/data/settings.cfg", MODE_WRITE); if(sf==NULL) { Server->Log("Error opening settings file!", LL_ERROR); return; } sf->Write(pData); Server->destroy(sf); } void ClientConnector::saveLogdata(const std::string &created, const std::string &pData) { IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); IQuery *q_p=db->Prepare("INSERT INTO logs (ttime) VALUES (datetime(?, 'unixepoch'))"); q_p->Bind(created); q_p->Write(); _i64 logid=db->getLastInsertID(); while(!db->Write("BEGIN IMMEDIATE;")) Server->wait(500); IQuery *q=db->Prepare("INSERT INTO logdata (logid, loglevel, message, idx, ltime) VALUES (?, ?, ?, ?, datetime(?, 'unixepoch'))"); std::vector lines; TokenizeMail(pData, lines, "\n"); for(size_t i=0,lc=lines.size();iConvertToUnicode(u_msg); q->Bind(logid); q->Bind(loglevel); q->Bind(msg); q->Bind(i); q->Bind(ltime); q->Write(); q->Reset(); } db->EndTransaction(); db->destroyAllQueries(); } std::string ClientConnector::getLogpoints(void) { IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); int timeoutms=300; IQuery *q=db->Prepare("SELECT id, strftime('"+time_format_str+"',ttime, 'localtime') AS ltime FROM logs ORDER BY ttime DESC LIMIT 100"); db_results res=q->Read(&timeoutms); std::string ret; for(size_t i=0;iConvertToUTF8(res[i][L"id"])+"-"; ret+=Server->ConvertToUTF8(res[i][L"ltime"])+"\n"; } db->destroyAllQueries(); return ret; } void ClientConnector::getLogLevel(int logid, int loglevel, std::string &data) { IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT); IQuery *q=db->Prepare("SELECT loglevel, message FROM logdata WHERE logid=? AND loglevel>=? ORDER BY idx ASC"); q->Bind(logid); q->Bind(loglevel); int timeoutms=300; db_results res=q->Read(&timeoutms); for(size_t i=0;iConvertToUTF8(res[i][L"loglevel"])+"-"; data+=Server->ConvertToUTF8(res[i][L"message"])+"\n"; } db->destroyAllQueries(); } bool ClientConnector::sendFullImage(void) { thread_action=TA_FULL_IMAGE; thread_ticket=Server->getThreadPool()->execute(this); state=CCSTATE_IMAGE; IScopedLock lock(backup_mutex); backup_running=RUNNING_FULL_IMAGE; pcdone=0; backup_source_token=server_token; return true; } bool ClientConnector::sendIncrImage(void) { thread_action=TA_INCR_IMAGE; thread_ticket=Server->getThreadPool()->execute(this); state=CCSTATE_IMAGE_HASHDATA; IScopedLock lock(backup_mutex); backup_running=RUNNING_INCR_IMAGE; pcdone=0; pcdone2=0; backup_source_token=server_token; return true; } void ClientConnector::operator ()(void) { #ifdef _WIN32 #ifdef THREAD_MODE_BACKGROUND_BEGIN SetThreadPriority( GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); #endif #endif if(thread_action==TA_FULL_IMAGE) { sendFullImageThread(); } else if(thread_action==TA_INCR_IMAGE) { int timeouts=1800; while(hashdataok==false) { Server->wait(1000); --timeouts; if(timeouts<=0 || do_quit==true ) { break; } } if(timeouts>0 || do_quit==true ) { sendIncrImageThread(); } } { IScopedLock lock(backup_mutex); if(backup_running==RUNNING_FULL_IMAGE || backup_running==RUNNING_INCR_IMAGE) { backup_running=RUNNING_NONE; } } #ifdef _WIN32 #ifdef THREAD_MODE_BACKGROUND_END SetThreadPriority( GetCurrentThread(), THREAD_MODE_BACKGROUND_END); #endif #endif } bool ClientConnector::waitForThread(void) { if(thread_action!=TA_NONE && Server->getThreadPool()->isRunning(thread_ticket ) ) return true; else return false; } void ClientConnector::ImageErr(const std::string &msg) { /*Server->Log(msg, LL_ERROR); unsigned int bs=0xFFFFFFFF; char *buffer=new char[sizeof(unsigned int)+msg.size()]; memcpy(buffer, &bs, sizeof(unsigned int) ); memcpy(&buffer[sizeof(unsigned int)], msg.c_str(), msg.size()); pipe->Write(buffer, sizeof(unsigned int)+msg.size()); delete [] buffer;*/ Server->Log(msg, LL_ERROR); #ifdef _WIN32 uint64 bs=0xFFFFFFFFFFFFFFFF; #else uint64 bs=0xFFFFFFFFFFFFFFFFLLU; #endif char *buffer=new char[sizeof(uint64)+msg.size()]; memcpy(buffer, &bs, sizeof(uint64) ); memcpy(&buffer[sizeof(uint64)], msg.c_str(), msg.size()); pipe->Write(buffer, sizeof(uint64)+msg.size()); delete [] buffer; } void ClientConnector::ImageErrRunning(const std::string &msg) { Server->Log(msg, LL_ERROR); int64 bs=-124; char *buffer=new char[sizeof(int64)+msg.size()]; memcpy(buffer, &bs, sizeof(int64) ); memcpy(&buffer[sizeof(int64)], msg.c_str(), msg.size()); pipe->Write(buffer, sizeof(int64)+msg.size()); delete [] buffer; } void ClientConnector::sendFullImageThread(void) { bool has_error=true; int save_id=-1; bool run=true; while(run) { if(shadowdrive.empty() && !no_shadowcopy) { std::string msg; mempipe->Read(&msg); if(msg.find("done")==0) { shadowdrive=getafter("-", msg); save_id=atoi(getuntil("-", shadowdrive).c_str()); shadowdrive=getafter("-", shadowdrive); if(shadowdrive.size()>0 && shadowdrive[shadowdrive.size()-1]=='\\') shadowdrive.erase(shadowdrive.begin()+shadowdrive.size()-1); if(shadowdrive.empty()) { ImageErr("Creating Shadow drive failed. Stopping. -2"); run=false; } } else { ImageErr("Creating Shadow drive failed. Stopping."); run=false; } mempipe->Write("exit"); mempipe=Server->createMemoryPipe(); } else { IFilesystem *fs=image_fak->createFilesystem(Server->ConvertToUnicode(shadowdrive)); if(fs==NULL) { ImageErr("Opening Shadow drive filesystem failed. Stopping."); run=false; break; } unsigned int blocksize=(unsigned int)fs->getBlocksize(); int64 drivesize=fs->getSize(); int64 blockcnt=fs->calculateUsedSpace()/blocksize; int64 ncurrblocks=0; if(startpos==0) { char *buffer=new char[sizeof(unsigned int)+sizeof(int64)*2+1+sizeof(unsigned int)+shadowdrive.size()+sizeof(int)]; char *cptr=buffer; memcpy(cptr, &blocksize, sizeof(unsigned int)); cptr+=sizeof(unsigned int); memcpy(cptr, &drivesize, sizeof(int64) ); cptr+=sizeof(int64); memcpy(cptr, &blockcnt, sizeof(int64) ); cptr+=sizeof(int64); #ifndef VSS_XP //persistence #ifndef VSS_S03 *cptr=1; #else *cptr=0; #endif #else *cptr=0; #endif if(no_shadowcopy) { *cptr=0; } ++cptr; unsigned int shadowdrive_size=(unsigned int)shadowdrive.size(); memcpy(cptr, &shadowdrive_size, sizeof(unsigned int)); cptr+=sizeof(unsigned int); memcpy(cptr, &shadowdrive[0], shadowdrive.size()); cptr+=shadowdrive.size(); memcpy(cptr, &save_id, sizeof(int)); cptr+=sizeof(int); bool b=pipe->Write(buffer, sizeof(unsigned int)+sizeof(int64)*2+1+sizeof(unsigned int)+shadowdrive.size()+sizeof(int) ); if(!b) { Server->Log("Pipe broken -1", LL_ERROR); run=false; break; } } ClientSend *cs=new ClientSend(pipe, blocksize+sizeof(int64), 2000); THREADPOOL_TICKET send_ticket=Server->getThreadPool()->execute(cs); unsigned int needed_bufs=64; std::vector bufs; for(int64 i=startpos,blocks=drivesize/blocksize;i n_bufs=cs->getBuffers(needed_bufs); bufs.insert(bufs.end(), n_bufs.begin(), n_bufs.end() ); needed_bufs=0; unsigned int n=(unsigned int)(std::min)(blocks-i, (int64)64); std::vector secs=fs->readBlocks(i, n, bufs, sizeof(int64)); needed_bufs+=(_u32)secs.size(); for(size_t j=0;jsendBuffer(bufs[j], sizeof(int64)+blocksize); } if(!secs.empty()) bufs.erase(bufs.begin(), bufs.begin()+secs.size() ); if(cs->hasError() ) { Server->Log("Pipe broken -2", LL_ERROR); run=false; has_error=true; break; } ncurrblocks+=secs.size(); if(IdleCheckerThread::getPause()) { Server->wait(5000); } } for(size_t i=0;ifreeBuffer(bufs[i]); } cs->doExit(); std::vector wf; wf.push_back(send_ticket); Server->getThreadPool()->waitFor(wf); delete cs; if(!run)break; char lastbuffer[sizeof(int64)]; int64 lastn=-123; memcpy(lastbuffer,&lastn, sizeof(int64)); bool b=pipe->Write(lastbuffer, sizeof(int64)); if(!b) { Server->Log("Pipe broken -3", LL_ERROR); run=false; has_error=true; break; } has_error=false; run=false; } } Server->Log("Sending full image done", LL_INFO); #ifdef VSS_XP //persistence has_error=false; #endif #ifdef VSS_S03 has_error=false; #endif if(!has_error && !no_shadowcopy) { removeShadowCopyThread(save_id); std::string msg; mempipe->Read(&msg); if(msg.find("done")!=0) { Server->Log("Removing shadow copy failed in image streaming: "+msg, LL_ERROR); } mempipe->Write("exit"); mempipe=Server->createMemoryPipe(); } do_quit=true; } const unsigned int c_vhdblocksize=(1024*1024/2); const unsigned int c_hashsize=32; void ClientConnector::sendIncrImageThread(void) { char *blockbuf=NULL; char *blockdatabuf=NULL; bool *has_blocks=NULL; char *zeroblockbuf=NULL; bool has_error=true; int save_id=-1; int update_cnt=0; unsigned int lastsendtime=Server->getTimeMS(); bool run=true; while(run) { if(shadowdrive.empty()) { std::string msg; mempipe->Read(&msg); if(msg.find("done")==0) { shadowdrive=getafter("-", msg); save_id=atoi(getuntil("-", shadowdrive).c_str()); shadowdrive=getafter("-", shadowdrive); if(shadowdrive.size()>0 && shadowdrive[shadowdrive.size()-1]=='\\') shadowdrive.erase(shadowdrive.begin()+shadowdrive.size()-1); if(shadowdrive.empty()) { ImageErr("Creating Shadow drive failed. Stopping. -2"); run=false; } } else { ImageErr("Creating Shadow drive failed. Stopping."); run=false; } mempipe->Write("exit"); mempipe=Server->createMemoryPipe(); } else { IFilesystem *fs=image_fak->createFilesystem(Server->ConvertToUnicode(shadowdrive)); if(fs==NULL) { ImageErr("Opening Shadow drive filesystem failed. Stopping."); run=false; break; } sha256_ctx shactx; unsigned int vhdblocks; unsigned int blocksize=(unsigned int)fs->getBlocksize(); int64 drivesize=fs->getSize(); int64 blockcnt=fs->calculateUsedSpace()/blocksize; vhdblocks=c_vhdblocksize/blocksize; int64 currvhdblock=0; int64 numblocks=drivesize/blocksize; if(startpos==0) { char *buffer=new char[sizeof(unsigned int)+sizeof(int64)*2+1+sizeof(unsigned int)+shadowdrive.size()+sizeof(int)]; char *cptr=buffer; memcpy(cptr, &blocksize, sizeof(unsigned int)); cptr+=sizeof(unsigned int); memcpy(cptr, &drivesize, sizeof(int64) ); cptr+=sizeof(int64); memcpy(cptr, &blockcnt, sizeof(int64) ); cptr+=sizeof(int64); #ifndef VSS_XP //persistence #ifndef VSS_S03 *cptr=1; #else *cptr=0; #endif #else *cptr=0; #endif ++cptr; unsigned int shadowdrive_size=(unsigned int)shadowdrive.size(); memcpy(cptr, &shadowdrive_size, sizeof(unsigned int)); cptr+=sizeof(unsigned int); memcpy(cptr, &shadowdrive[0], shadowdrive.size()); cptr+=shadowdrive.size(); memcpy(cptr, &save_id, sizeof(int)); cptr+=sizeof(int); bool b=pipe->Write(buffer, sizeof(unsigned int)+sizeof(int64)*2+1+sizeof(unsigned int)+shadowdrive.size()+sizeof(int) ); if(!b) { Server->Log("Pipe broken -1", LL_ERROR); run=false; break; } } delete []blockbuf; blockbuf=new char[vhdblocks*blocksize]; delete []has_blocks; has_blocks=new bool[vhdblocks]; delete []zeroblockbuf; zeroblockbuf=new char[blocksize]; memset(zeroblockbuf, 0, blocksize); ClientSend *cs=new ClientSend(pipe, blocksize+sizeof(int64), 2000); THREADPOOL_TICKET send_ticket=Server->getThreadPool()->execute(cs); for(int64 i=startpos,blocks=drivesize/blocksize;i10) { IScopedLock lock(backup_mutex); pcdone2=(int)(((float)i/(float)blocks)*100.f+0.5f); update_cnt=0; } currvhdblock=i/vhdblocks; bool has_data=false; for(int64 j=i;jreadBlock(j, NULL) ) { has_data=true; break; } } if(has_data) { sha256_init(&shactx); bool mixed=false; for(int64 j=i;jreadBlock(j, dpos) ) { if(IdleCheckerThread::getPause()) { Server->wait(5000); } sha256_update(&shactx, (unsigned char*)dpos, blocksize); has_blocks[j-i]=true; } else { sha256_update(&shactx, (unsigned char*)zeroblockbuf, blocksize); has_blocks[j-i]=false; mixed=true; } } unsigned char digest[c_hashsize]; sha256_final(&shactx, digest); if(hashdatafile->Size()>=(currvhdblock+1)*c_hashsize) { hashdatafile->Seek(currvhdblock*c_hashsize); char hashdata_buf[c_hashsize]; if( hashdatafile->Read(hashdata_buf, c_hashsize)!=c_hashsize ) { Server->Log("Reading hashdata failed!", LL_ERROR); } if(memcmp(hashdata_buf, digest, c_hashsize)!=0) { //Server->Log("Block did change: "+nconvert(i)+" mixed="+nconvert(mixed), LL_DEBUG); for(int64 j=i;jgetBuffer(); memcpy(&cb[sizeof(int64)], blockbuf+(j-i)*blocksize, blocksize); memcpy(cb, &j, sizeof(int64) ); cs->sendBuffer(cb, sizeof(int64)+blocksize); lastsendtime=Server->getTimeMS(); if(cs->hasError()) { Server->Log("Pipe broken -2", LL_ERROR); run=false; break; } } } } else { //Server->Log("Block didn't change: "+nconvert(i), LL_DEBUG); unsigned int tt=Server->getTimeMS(); if(tt-lastsendtime>10000) { int64 bs=-125; char* buffer=cs->getBuffer(); memcpy(buffer, &bs, sizeof(int64) ); cs->sendBuffer(buffer, sizeof(int64)); lastsendtime=tt; } } } else { ImageErrRunning("Hashdata from server too small"); run=false; break; } if(!run)break; } else { unsigned int tt=Server->getTimeMS(); if(tt-lastsendtime>10000) { int64 bs=-125; char* buffer=cs->getBuffer(); memcpy(buffer, &bs, sizeof(int64) ); cs->sendBuffer(buffer, sizeof(int64)); lastsendtime=tt; } } } cs->doExit(); std::vector wf; wf.push_back(send_ticket); Server->getThreadPool()->waitFor(wf); delete cs; if(!run)break; char lastbuffer[sizeof(int64)]; int64 lastn=-123; memcpy(lastbuffer,&lastn, sizeof(int64)); bool b=pipe->Write(lastbuffer, sizeof(int64)); if(!b) { Server->Log("Pipe broken -3", LL_ERROR); run=false; break; } has_error=false; run=false; } } delete [] blockbuf; delete [] has_blocks; delete [] zeroblockbuf; Server->destroy(hashdatafile); Server->Log("Sending image done", LL_INFO); #ifdef VSS_XP //persistence has_error=false; #endif #ifdef VSS_S03 has_error=false; #endif if(!has_error) { removeShadowCopyThread(save_id); std::string msg; mempipe->Read(&msg); if(msg.find("done")!=0) { Server->Log("Removing shadow copy failed in image streaming: "+msg, LL_ERROR); } mempipe->Write("exit"); mempipe=Server->createMemoryPipe(); } do_quit=true; } void ClientConnector::removeShadowCopyThread(int save_id) { if(!no_shadowcopy) { CWData data; data.addChar(3); data.addVoidPtr(mempipe); data.addString(image_letter); data.addString(server_token); data.addUChar(1); data.addInt(save_id); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); } } bool ClientConnector::sendMBR(std::wstring dl) { #ifdef _WIN32 std::wstring vpath=dl; if(!vpath.empty() && vpath[0]!='\\') { dl+=L":"; vpath=L"\\\\.\\"+dl; } HANDLE hVolume=CreateFileW(vpath.c_str(), GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hVolume==INVALID_HANDLE_VALUE) { Server->Log(L"CreateFile of volume '"+dl+L"' failed. - sendMBR", LL_ERROR); return false; } STORAGE_DEVICE_NUMBER dev_num; DWORD ret_bytes; BOOL b=DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &dev_num, sizeof(STORAGE_DEVICE_NUMBER), &ret_bytes, NULL); CloseHandle(hVolume); if(b==0) { Server->Log(L"DeviceIoControl IOCTL_STORAGE_GET_DEVICE_NUMBER failed. Volume: '"+dl+L"'", LL_ERROR); return false; } wchar_t voln[MAX_PATH+1]; DWORD voln_size=MAX_PATH+1; DWORD voln_sern; wchar_t fsn[MAX_PATH+1]; DWORD fsn_size=MAX_PATH+1; b=GetVolumeInformationW((dl+L"\\").c_str(), voln, voln_size, &voln_sern, NULL, NULL, fsn, fsn_size); if(b==0) { Server->Log(L"GetVolumeInformationW failed. Volume: '"+dl+L"'", LL_ERROR); return false; } CWData mbr; mbr.addChar(1); mbr.addChar(0); mbr.addInt(dev_num.DeviceNumber); mbr.addInt(dev_num.PartitionNumber); mbr.addString(nconvert((_i64)voln_sern)); mbr.addString(Server->ConvertToUTF8((std::wstring)voln)); mbr.addString(Server->ConvertToUTF8((std::wstring)fsn)); IFile *dev=Server->openFile(L"\\\\.\\PhysicalDrive"+convert((int)dev_num.DeviceNumber), MODE_READ); if(dev==NULL) { Server->Log(L"Error opening Device "+convert((int)dev_num.DeviceNumber), LL_ERROR); return false; } std::string mbr_bytes=dev->Read(512); mbr.addString(mbr_bytes); tcpstack.Send(pipe, mbr); return true; #endif //WIN_32 } const unsigned int receive_timeouttime=60000; std::string ClientConnector::receivePacket(IPipe *p) { unsigned int starttime=Server->getTimeMS(); while(Server->getTimeMS()-starttime<=receive_timeouttime) { std::string ret; size_t rc=p->Read(&ret, 10000); if(rc==0) { return ""; } tcpstack.AddData((char*)ret.c_str(), ret.size()); size_t packetsize; char *pck=tcpstack.getPacket(&packetsize); if(pck!=NULL) { ret.resize(packetsize); if(packetsize>0) { memcpy(&ret[0], pck, packetsize); } delete [] pck; return ret; } } return ""; } void ClientConnector::removeChannelpipe(IPipe *cp) { channel_exit.push_back(cp); } void ClientConnector::downloadImage(str_map params) { _i64 imgsize=-1; if(channel_pipes.size()==0) { pipe->Write((char*)&imgsize, sizeof(_i64), (int)receive_timeouttime); Server->Log("No channel found!", LL_DEBUG); return; } int l_pcdone=0; { IScopedLock lock(progress_mutex); pcdone=0; } for(size_t i=0;iLog("Downloading from channel "+nconvert((int)i), LL_DEBUG); _i64 imgsize=-1; c->Read((char*)&imgsize, sizeof(_i64), 60000); Server->Log("Imagesize "+nconvert(imgsize), LL_DEBUG); if(imgsize==-1) { Server->Log("Error reading size", LL_ERROR); continue; } if(!pipe->Write((char*)&imgsize, sizeof(_i64), (int)receive_timeouttime)) { Server->Log("Could not write to pipe! downloadImage-1", LL_ERROR); return; } char buf[4096]; _i64 read=0; if(params[L"mbr"]==L"true") { Server->Log("Downloading MBR...", LL_DEBUG); while(readRead(buf, 4096, 60000); if(c_read==0) { Server->Log("Read Timeout", LL_ERROR); removeChannelpipe(c); return; } if(!pipe->Write(buf, (_u32)c_read, (int)receive_timeouttime*5)) { Server->Log("Could not write to pipe! downloadImage-5", LL_ERROR); removeChannelpipe(c); return; } read+=c_read; } Server->Log("Downloading MBR done"); return; } unsigned int blockleft=0; unsigned int off=0; _i64 pos=0; while(posRead(&buf[off], 4096-off, 180000); if( r==0 ) { Server->Log("Read Timeout -2 CS", LL_ERROR); removeChannelpipe(c); return; } if(!pipe->Write(&buf[off], r, (int)receive_timeouttime*5)) { Server->Log("Could not write to pipe! downloadImage-3 size "+nconvert(r)+" off "+nconvert(off), LL_ERROR); removeChannelpipe(c); return; } if(r!=0) r+=off; off=0; while(true) { if( blockleft==0 ) { if(r-off>=sizeof(_i64) ) { blockleft=4096; _i64 s; memcpy((char*)&s, &buf[off], sizeof(_i64) ); if(s>imgsize) { Server->Log("invalid seek value: "+nconvert(s), LL_ERROR); } off+=sizeof(_i64); pos=s; } else if(r-off>0) { char buf2[4096]; memcpy(buf2, &buf[off], r-off); memcpy(buf, buf2, r-off); off=(_u32)r-off; break; } else { off=0; break; } } else { unsigned int available=(std::min)((unsigned int)r-off, blockleft); read+=available; blockleft-=available; off+=available; if(off>=r) { off=0; break; } } } int t_pcdone=(int)(((float)pos/(float)imgsize)*100.f+0.5f); if(t_pcdone!=l_pcdone) { l_pcdone=t_pcdone; IScopedLock lock(progress_mutex); pcdone=l_pcdone; } lasttime=Server->getTimeMS(); } Server->Log("Downloading image done", LL_DEBUG); return; } imgsize=-1; pipe->Write((char*)&imgsize, sizeof(_i64), (int)receive_timeouttime); } void ClientConnector::waitForPings(IScopedLock *lock) { Server->Log("Waiting for pings...", LL_DEBUG); while(!channel_ping.empty()) { lock->relock(NULL); Server->wait(10); lock->relock(backup_mutex); } Server->Log("done. (Waiting for pings)", LL_DEBUG); } unsigned int ClientConnector::getLastTokenTime(const std::string & tok) { IScopedLock lock(backup_mutex); std::map::iterator it=last_token_times.find(tok); if(it!=last_token_times.end()) { return it->second; } else { return 0; } }