/************************************************************************* * 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 . **************************************************************************/ #ifndef CLIENT_ONLY #include "server_get.h" #include "server_ping.h" #include "database.h" #include "../stringtools.h" #include "fileclient/FileClient.h" #include "../Interface/Server.h" #include "../Interface/ThreadPool.h" #include "fileclient/tcpstack.h" #include "fileclient/data.h" #include "../fsimageplugin/IFSImageFactory.h" #include "../fsimageplugin/IVHDFile.h" #include "server_channel.h" #include "server_log.h" #include "server_writer.h" #include "server_cleanup.h" #include "escape.h" #include "mbr_code.h" #include "zero_hash.h" #include "server_running.h" #include "treediff/TreeDiff.h" #include #include #include extern IFSImageFactory *image_fak; extern std::string server_identity; const unsigned short serviceport=35623; const unsigned int full_backup_construct_timeout=60*60*1000; const unsigned int shadow_copy_timeout=5*60*1000; const unsigned int check_time_intervall=5*60*1000; const unsigned int status_update_intervall=1000; const unsigned int mbr_size=(1024*1024)/4; const size_t minfreespace_image=1000*1024*1024; //1000 MB BackupServerGet::BackupServerGet(IPipe *pPipe, sockaddr_in pAddr, const std::string &pName) { q_update_lastseen=NULL; pipe=pPipe; clientaddr=pAddr; clientaddr_mutex=Server->createMutex(); clientname=pName; clientid=0; hashpipe=Server->createMemoryPipe(); hashpipe_prepare=Server->createMemoryPipe(); exitpipe=Server->createMemoryPipe(); exitpipe_prepare=Server->createMemoryPipe(); do_full_backup_now=false; do_incr_backup_now=false; do_update_settings=false; do_full_image_now=false; do_incr_image_now=false; } BackupServerGet::~BackupServerGet(void) { if(q_update_lastseen!=NULL) unloadSQL(); Server->destroy(clientaddr_mutex); } void BackupServerGet::unloadSQL(void) { db->destroyQuery(q_update_lastseen); db->destroyQuery(q_update_full); db->destroyQuery(q_update_incr); db->destroyQuery(q_create_backup); db->destroyQuery(q_get_last_incremental); db->destroyQuery(q_set_last_backup); db->destroyQuery(q_update_setting); db->destroyQuery(q_insert_setting); db->destroyQuery(q_set_complete); db->destroyQuery(q_update_image_full); db->destroyQuery(q_update_image_incr); db->destroyQuery(q_create_backup_image); db->destroyQuery(q_set_image_complete); db->destroyQuery(q_set_last_image_backup); db->destroyQuery(q_get_last_incremental_image); db->destroyQuery(q_set_image_size); db->destroyQuery(q_update_running_file); db->destroyQuery(q_update_running_image); db->destroyQuery(q_update_images_size); db->destroyQuery(q_set_done); db->destroyQuery(q_save_logdata); db->destroyQuery(q_get_unsent_logdata); db->destroyQuery(q_set_logdata_sent); } void BackupServerGet::operator ()(void) { { bool b=sendClientMessage("ADD IDENTITY", "OK", L"Sending Identity to client failed stopping...", 10000, false); if(!b) { pipe->Write("ok"); Server->Log("server_get Thread for client "+clientname+" finished, because the identity was not recognized", LL_INFO); ServerStatus::setWrongIdent(clientname, true); ServerLogger::reset(clientid); delete this; return; } } if( clientname.find("##restore##")==0 ) { ServerChannelThread channel_thread(this, getClientaddr()); THREADPOOL_TICKET channel_thread_id=Server->getThreadPool()->execute(&channel_thread); while(true) { std::string msg; pipe->Read(&msg); if(msg=="exit" || msg=="exitnow" ) break; } channel_thread.doExit(); Server->getThreadPool()->waitFor(channel_thread_id); pipe->Write("ok"); Server->Log("server_get Thread for client "+clientname+" finished, restore thread"); delete this; return; } db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER); clientid=getClientID(); settings=Server->createDBSettingsReader(db, "settings", "SELECT value FROM settings WHERE key=? AND clientid=0"); settings_client=Server->createDBSettingsReader(db, "settings", "SELECT value FROM settings WHERE key=? AND clientid="+nconvert(clientid)); server_settings=new ServerSettings(db, clientid); std::wstring backupfolder=server_settings->getSettings()->backupfolder; if(!os_create_dir(backupfolder+os_file_sep()+widen(clientname)) && !os_directory_exists(backupfolder+os_file_sep()+widen(clientname)) ) { Server->Log("Could not create or read directory for client \""+clientname+"\"", LL_ERROR); pipe->Write("ok"); delete server_settings; delete this; return; } prepareSQL(); updateLastseen(); status.client=clientname; status.clientid=clientid; ServerStatus::setServerStatus(status); BackupServerHash *bsh=new BackupServerHash(hashpipe, exitpipe, clientid ); BackupServerPrepareHash *bsh_prepare=new BackupServerPrepareHash(hashpipe_prepare, exitpipe_prepare, hashpipe, exitpipe, clientid); Server->getThreadPool()->execute(bsh); Server->getThreadPool()->execute(bsh_prepare); ServerChannelThread channel_thread(this, getClientaddr()); THREADPOOL_TICKET channel_thread_id=Server->getThreadPool()->execute(&channel_thread); sendSettings(); ServerLogger::Log(clientid, "Getting client settings...", LL_DEBUG); if(server_settings->getSettings()->allow_overwrite && !getClientSettings()) { ServerLogger::Log(clientid, "Getting client settings failed -1", LL_ERROR); } ServerLogger::Log(clientid, "Sending backup incr intervall...", LL_DEBUG); sendClientBackupIncrIntervall(); if(server_settings->getSettings()->autoupdate_clients) { checkClientVersion(); } sendClientLogdata(); bool skip_checking=false; if( server_settings->getSettings()->startup_backup_delay>0 ) { pipe->isReadable(server_settings->getSettings()->startup_backup_delay*1000); skip_checking=true; } bool do_exit_now=false; while(true) { if(!skip_checking) { if(do_update_settings) { ServerLogger::Log(clientid, "Getting client settings...", LL_DEBUG); do_update_settings=false; if(server_settings->getSettings()->allow_overwrite && !getClientSettings()) { ServerLogger::Log(clientid, "Getting client settings failed -2", LL_ERROR); } } unsigned int ttime=Server->getTimeMS(); status.starttime=ttime; has_error=false; bool hbu=false; bool r_success=false; bool r_image=false; bool r_incremental=false; pingthread=NULL; pingthread_ticket=ILLEGAL_THREADPOOL_TICKET; status.pcdone=0; status.hashqueuesize=0; status.prepare_hashqueuesize=0; ServerStatus::setServerStatus(status); if(isUpdateFull() || do_full_backup_now) { ScopedActiveThread sat; status.statusaction=sa_full_file; ServerStatus::setServerStatus(status, true); ServerLogger::Log(clientid, "Doing full file backup...", LL_DEBUG); do_full_backup_now=false; hbu=true; if(!constructBackupPath()) { ServerLogger::Log(clientid, "Cannot create Directory for backup (Server error)", LL_ERROR); r_success=false; } else { pingthread=new ServerPingThread(this); pingthread_ticket=Server->getThreadPool()->execute(pingthread); r_success=doFullBackup(); } } else if(isUpdateIncr() || do_incr_backup_now) { ScopedActiveThread sat; status.statusaction=sa_incr_file; ServerStatus::setServerStatus(status, true); ServerLogger::Log(clientid, "Doing incremental file backup...", LL_DEBUG); do_incr_backup_now=false; hbu=true; r_incremental=true; if(!constructBackupPath()) { ServerLogger::Log(clientid, "Cannot create Directory for backup (Server error)", LL_ERROR); r_success=false; } else { pingthread=new ServerPingThread(this); pingthread_ticket=Server->getThreadPool()->execute(pingthread); r_success=doIncrBackup(); } } else if(!server_settings->getSettings()->no_images && (isUpdateFullImage() || do_full_image_now) ) { ScopedActiveThread sat; status.statusaction=sa_full_image; ServerStatus::setServerStatus(status, true); ServerLogger::Log(clientid, "Doing full image backup...", LL_DEBUG); do_full_image_now=false; r_image=true; pingthread=new ServerPingThread(this); pingthread_ticket=Server->getThreadPool()->execute(pingthread); r_success=doImage(L"", 0, 0); } else if(!server_settings->getSettings()->no_images && ( isUpdateIncrImage() || do_incr_image_now) ) { ScopedActiveThread sat; status.statusaction=sa_incr_image; ServerStatus::setServerStatus(status, true); ServerLogger::Log(clientid, "Doing incremental image backup...", LL_DEBUG); do_incr_image_now=false; r_image=true; r_incremental=true; pingthread=new ServerPingThread(this); pingthread_ticket=Server->getThreadPool()->execute(pingthread); SBackup last=getLastIncrementalImage(); if(last.incremental==-2) { ServerLogger::Log(clientid, "Error retrieving last backup.", LL_ERROR); r_success=false; } else { r_success=doImage(last.path, last.incremental+1, last.incremental_ref); } } if(hbu && !has_error) { if(r_success) { notifyClientBackupSuccessfull(); } else { if(pingthread!=NULL) { pingthread->setStop(true); Server->getThreadPool()->waitFor(pingthread_ticket); } pingthread=NULL; } } else if(hbu && has_error) { os_remove_nonempty_dir(backuppath); } status.action_done=false; status.statusaction=sa_none; status.pcdone=100; //Flush buffer before continuing... status.hashqueuesize=(_u32)hashpipe->getNumElements()+(bsh->isWorking()?1:0); status.prepare_hashqueuesize=(_u32)hashpipe_prepare->getNumElements()+(bsh_prepare->isWorking()?1:0); hashpipe->Write("flush"); while(status.hashqueuesize>0 || status.prepare_hashqueuesize>0) { ServerStatus::setServerStatus(status, true); Server->wait(1000); status.hashqueuesize=(_u32)hashpipe->getNumElements()+(bsh->isWorking()?1:0); status.prepare_hashqueuesize=(_u32)hashpipe_prepare->getNumElements()+(bsh_prepare->isWorking()?1:0); } ServerStatus::setServerStatus(status); unsigned int ptime=Server->getTimeMS()-ttime; if(hbu && !has_error) { ServerLogger::Log(clientid, "Time taken for backing up client "+clientname+": "+nconvert(ptime), LL_INFO); if(!r_success) { ServerLogger::Log(clientid, "Backup not complete because of connection problems", LL_ERROR); } else { updateLastBackup(); setBackupComplete(); } status.pcdone=100; ServerStatus::setServerStatus(status, true); } if( r_image ) { ServerLogger::Log(clientid, "Time taken for creating image of client "+clientname+": "+nconvert(ptime), LL_INFO); if(!r_success) { ServerLogger::Log(clientid, "Backup not complete because of connection problems", LL_ERROR); } else { updateLastImageBackup(); } status.pcdone=100; ServerStatus::setServerStatus(status, true); } if(hbu || r_image) { saveClientLogdata(r_image?1:0, r_incremental?1:0); sendClientLogdata(); } if(hbu) { ServerCleanupThread::updateStats(); } if(pingthread!=NULL) { pingthread->setStop(true); Server->getThreadPool()->waitFor(pingthread_ticket); pingthread=NULL; } /* Predict sleep time -- unsigned int wtime; if((unsigned int)update_freq_incr*1000>ptime) { wtime=update_freq_incr*1000-ptime; } else { wtime=0; } wtime+=60000;*/ } std::string msg; pipe->Read(&msg, skip_checking?0:check_time_intervall); skip_checking=false; if(msg=="exit") break; else if(msg=="exitnow") { do_exit_now=true; break; } else if(msg=="START BACKUP INCR") do_incr_backup_now=true; else if(msg=="START BACKUP FULL") do_full_backup_now=true; else if(msg=="UPDATE SETTINGS") do_update_settings=true; else if(msg=="START IMAGE INCR") do_incr_image_now=true; else if(msg=="START IMAGE FULL") do_full_image_now=true; else if(msg.find("address")==0) { IScopedLock lock(clientaddr_mutex); memcpy(&clientaddr, &msg[7], sizeof(sockaddr_in) ); } } //destroy channel { Server->Log("Stopping channel...", LL_DEBUG); channel_thread.doExit(); Server->getThreadPool()->waitFor(channel_thread_id); } if(do_exit_now) { hashpipe_prepare->Write("exitnow"); std::string msg; exitpipe_prepare->Read(&msg); Server->destroy(exitpipe_prepare); } else { hashpipe_prepare->Write("exit"); } Server->destroy(settings); Server->destroy(settings_client); delete server_settings; pipe->Write("ok"); Server->Log("server_get Thread for client "+clientname+" finished"); delete this; } void BackupServerGet::prepareSQL(void) { SSettings *s=server_settings->getSettings(); q_update_lastseen=db->Prepare("UPDATE clients SET lastseen=CURRENT_TIMESTAMP WHERE id=?", false); q_update_full=db->Prepare("SELECT id FROM backups WHERE datetime('now','-"+nconvert(s->update_freq_full)+" seconds')Prepare("SELECT id FROM backups WHERE datetime('now','-"+nconvert(s->update_freq_incr)+" seconds')Prepare("INSERT INTO backups (incremental, clientid, path, complete, running, size_bytes, done) VALUES (?, ?, ?, 0, CURRENT_TIMESTAMP, -1, 0)", false); q_get_last_incremental=db->Prepare("SELECT incremental,path FROM backups WHERE clientid=? AND done=1 ORDER BY backuptime DESC LIMIT 1", false); q_set_last_backup=db->Prepare("UPDATE clients SET lastbackup=CURRENT_TIMESTAMP WHERE id=?", false); q_update_setting=db->Prepare("UPDATE settings SET value=? WHERE key=? AND clientid=?", false); q_insert_setting=db->Prepare("INSERT INTO settings (key, value, clientid) VALUES (?,?,?)", false); q_set_complete=db->Prepare("UPDATE backups SET complete=1 WHERE id=?", false); q_update_image_full=db->Prepare("SELECT id FROM backup_images WHERE datetime('now','-"+nconvert(s->update_freq_image_full)+" seconds')Prepare("SELECT id FROM backup_images WHERE datetime('now','-"+nconvert(s->update_freq_image_incr)+" seconds')Prepare("INSERT INTO backup_images (clientid, path, incremental, incremental_ref, complete, running, size_bytes) VALUES (?, ?, ?, ?, 0, CURRENT_TIMESTAMP, 0)", false); q_set_image_size=db->Prepare("UPDATE backup_images SET size_bytes=? WHERE id=?", false); q_set_image_complete=db->Prepare("UPDATE backup_images SET complete=1 WHERE id=?", false); q_set_last_image_backup=db->Prepare("UPDATE clients SET lastbackup_image=CURRENT_TIMESTAMP WHERE id=?", false); q_get_last_incremental_image=db->Prepare("SELECT id,incremental,path FROM backup_images WHERE clientid=? AND incremental=0 AND complete=1 ORDER BY backuptime DESC LIMIT 1", false); q_update_running_file=db->Prepare("UPDATE backups SET running=CURRENT_TIMESTAMP WHERE id=?", false); q_update_running_image=db->Prepare("UPDATE backup_images SET running=CURRENT_TIMESTAMP WHERE id=?", false); q_update_images_size=db->Prepare("UPDATE clients SET bytes_used_images=(SELECT bytes_used_images FROM clients WHERE id=?)+? WHERE id=?", false); q_set_done=db->Prepare("UPDATE backups SET done=1 WHERE id=?", false); q_save_logdata=db->Prepare("INSERT INTO logs (clientid, logdata, errors, warnings, infos, image, incremental) VALUES (?,?,?,?,?,?,?)", false); q_get_unsent_logdata=db->Prepare("SELECT id, strftime('%s', created) AS created, logdata FROM logs WHERE sent=0 AND clientid=?", false); q_set_logdata_sent=db->Prepare("UPDATE logs SET sent=1 WHERE id=?", false); } int BackupServerGet::getClientID(void) { IQuery *q=db->Prepare("SELECT id FROM clients WHERE name=?",false); q->Bind(clientname); db_results res=q->Read(); db->destroyQuery(q); if(res.size()>0) return watoi(res[0][L"id"]); else { IQuery *q_insert_newclient=db->Prepare("INSERT INTO clients (name, lastseen,bytes_used_files,bytes_used_images) VALUES (?, CURRENT_TIMESTAMP, 0, 0)", false); q_insert_newclient->Bind(clientname); q_insert_newclient->Write(); int rid=(int)db->getLastInsertID(); q_insert_newclient->Reset(); db->destroyQuery(q_insert_newclient); return rid; } } SBackup BackupServerGet::getLastIncremental(void) { q_get_last_incremental->Bind(clientid); db_results res=q_get_last_incremental->Read(); q_get_last_incremental->Reset(); if(res.size()>0) { SBackup b; b.incremental=watoi(res[0][L"incremental"]); b.path=res[0][L"path"]; return b; } else { SBackup b; b.incremental=-2; return b; } } SBackup BackupServerGet::getLastIncrementalImage(void) { q_get_last_incremental_image->Bind(clientid); db_results res=q_get_last_incremental_image->Read(); q_get_last_incremental_image->Reset(); if(res.size()>0) { SBackup b; b.incremental=watoi(res[0][L"incremental"]); b.path=res[0][L"path"]; b.incremental_ref=watoi(res[0][L"id"]); return b; } else { SBackup b; b.incremental=-2; return b; } } int BackupServerGet::createBackupSQL(int incremental, int clientid, std::wstring path) { q_create_backup->Bind(incremental); q_create_backup->Bind(clientid); q_create_backup->Bind(path); q_create_backup->Write(); q_create_backup->Reset(); return (int)db->getLastInsertID(); } int BackupServerGet::createBackupImageSQL(int incremental, int incremental_ref, int clientid, std::wstring path) { q_create_backup_image->Bind(clientid); q_create_backup_image->Bind(path); q_create_backup_image->Bind(incremental); q_create_backup_image->Bind(incremental_ref); q_create_backup_image->Write(); q_create_backup_image->Reset(); return (int)db->getLastInsertID(); } void BackupServerGet::updateLastseen(void) { q_update_lastseen->Bind(clientid); q_update_lastseen->Write(); q_update_lastseen->Reset(); } bool BackupServerGet::isUpdateFull(void) { q_update_full->Bind(clientid); db_results res=q_update_full->Read(); q_update_full->Reset(); return res.empty(); } bool BackupServerGet::isUpdateIncr(void) { q_update_incr->Bind(clientid); db_results res=q_update_incr->Read(); q_update_incr->Reset(); return res.empty(); } bool BackupServerGet::isUpdateFullImage(void) { if(server_settings->getSettings()->update_freq_image_full<0) return false; q_update_image_full->Bind(clientid); db_results res=q_update_image_full->Read(); q_update_image_full->Reset(); return res.empty(); } bool BackupServerGet::isUpdateIncrImage(void) { if(server_settings->getSettings()->update_freq_image_full<=0) return false; q_update_image_incr->Bind(clientid); db_results res=q_update_image_incr->Read(); q_update_image_incr->Reset(); return res.empty(); } void BackupServerGet::updateRunning(bool image) { if(image) { q_update_running_image->Bind(backupid); q_update_running_image->Write(); q_update_running_image->Reset(); } else { q_update_running_file->Bind(backupid); q_update_running_file->Write(); q_update_running_file->Reset(); } } bool BackupServerGet::getNextEntry(char ch, SFile &data) { switch(state) { case 0: if(ch=='f') data.isdir=false; else if(ch=='d') data.isdir=true; else ServerLogger::Log(clientid, "Error parsing file BackupServerGet::getNextEntry - 1", LL_ERROR); state=1; break; case 1: //" state=2; break; case 3: if(ch!='"') { t_name.erase(t_name.size()-1,1); data.name=Server->ConvertToUnicode(t_name); t_name=""; if(data.isdir) { resetEntryState(); return true; } else state=4; } case 2: if(state==2 && ch=='"') state=3; else if(state==3) state=2; t_name+=ch; break; case 4: if(ch!=' ') { t_name+=ch; } else { data.size=os_atoi64(t_name); t_name=""; state=5; } break; case 5: if(ch!='\n') { t_name+=ch; } else { data.last_modified=os_atoi64(t_name); resetEntryState(); return true; } break; } return false; } void BackupServerGet::resetEntryState(void) { t_name=""; state=0; } bool BackupServerGet::request_filelist_construct(bool full) { CTCPStack tcpstack; IPipe *cc=Server->ConnectStream(inet_ntoa(getClientaddr().sin_addr), serviceport, 10000); if(cc==NULL) { ServerLogger::Log(clientid, "Connecting to ClientService of \""+clientname+"\" failed - CONNECT error", LL_ERROR); return false; } if(full) tcpstack.Send(cc, server_identity+"START FULL BACKUP"); else tcpstack.Send(cc, server_identity+"START BACKUP"); std::string ret; unsigned int starttime=Server->getTimeMS(); while(Server->getTimeMS()-starttime<=full_backup_construct_timeout) { size_t rc=cc->Read(&ret, full_backup_construct_timeout); if(rc==0) { ServerLogger::Log(clientid, "Constructing of filelist of \""+clientname+"\" failed - TIMEOUT(1)", LL_ERROR); break; } tcpstack.AddData((char*)ret.c_str(), ret.size()); size_t packetsize; char *pck=tcpstack.getPacket(&packetsize); if(pck!=NULL && packetsize>0) { ret=pck; delete [] pck; if(ret!="DONE") { ServerLogger::Log(clientid, "Constructing of filelist of \""+clientname+"\" failed - TIMEOUT(2)", LL_ERROR); break; } else { Server->destroy(cc); return true; } } } Server->destroy(cc); return false; } bool BackupServerGet::doFullBackup(void) { bool b=request_filelist_construct(true); if(!b) { has_error=true; return false; } FileClient fc; sockaddr_in addr=getClientaddr(); _u32 rc=fc.Connect(&addr); if(rc!=ERR_CONNECTED) { ServerLogger::Log(clientid, "Full Backup of "+clientname+" failed - CONNECT error", LL_ERROR); has_error=true; return false; } IFile *tmp=Server->openTemporaryFile(); rc=fc.GetFile("urbackup/filelist.ub", tmp); if(rc!=ERR_SUCCESS) { ServerLogger::Log(clientid, "Error getting filelist of "+clientname+". Errorcode: "+nconvert(rc), LL_ERROR); has_error=true; return false; } backupid=createBackupSQL(0, clientid, backuppath_single); tmp->Seek(0); resetEntryState(); IFile *clientlist=Server->openFile("urbackup/clientlist_"+nconvert(clientid)+".ub", MODE_WRITE); if(clientlist==NULL ) { ServerLogger::Log(clientid, "Error creating clientlist for client "+clientname, LL_ERROR); has_error=true; return false; } _i64 filelist_size=tmp->Size(); char buffer[4096]; _u32 read; _i64 filelist_currpos=0; std::wstring curr_path; SFile cf; int depth=0; bool r_done=false; unsigned int laststatsupdate=0; ServerStatus::setServerStatus(status, true); ServerRunningUpdater *running_updater=new ServerRunningUpdater(backupid, false); Server->getThreadPool()->execute(running_updater); std::vector diffs; _i64 files_size=getIncrementalSize(tmp, diffs, true); _i64 transferred=0; tmp->Seek(0); while( (read=tmp->Read(buffer, 4096))>0 && r_done==false) { filelist_currpos+=read; for(size_t i=0;igetTimeMS(); if(ctime-laststatsupdate>status_update_intervall) { laststatsupdate=ctime; status.pcdone=(std::min)(100,(int)(((float)transferred)/((float)files_size/100.f)+0.5f)); status.hashqueuesize=(_u32)hashpipe->getNumElements(); status.prepare_hashqueuesize=(_u32)hashpipe_prepare->getNumElements(); ServerStatus::setServerStatus(status, true); } bool b=getNextEntry(buffer[i], cf); if(b) { if(cf.isdir==true) { if(cf.name!=L"..") { curr_path+=L"/"+cf.name; std::wstring os_curr_path=curr_path; if(os_file_sep()!=L"/") { for(size_t i=0;iWrite(buffer, read); if(read<4096) break; } if(r_done==false) { std::wstring backupfolder=server_settings->getSettings()->backupfolder; std::wstring currdir=backupfolder+os_file_sep()+widen(clientname)+os_file_sep()+L"current"; Server->deleteFile(currdir); os_link_symbolic(backuppath, currdir); } running_updater->stop(); updateRunning(false); Server->destroy(clientlist); Server->destroy(tmp); setBackupDone(); return !r_done; } bool BackupServerGet::load_file(const std::wstring &fn, const std::wstring &curr_path, FileClient &fc) { ServerLogger::Log(clientid, L"Loading file \""+fn+L"\"", LL_DEBUG); IFile *fd=NULL; while(fd==NULL) { fd=Server->openTemporaryFile(); if(fd==NULL) { ServerLogger::Log(clientid, "Error opening temporary file. Retrying...", LL_WARNING); Server->wait(500); } } std::wstring cfn=curr_path+L"/"+fn; if(cfn[0]=='/') cfn.erase(0,1); _u32 rc=fc.GetFile(Server->ConvertToUTF8(cfn), fd); if(rc!=ERR_SUCCESS) { ServerLogger::Log(clientid, L"Error getting file \""+cfn+L"\" from "+widen(clientname)+L". Errorcode: "+convert(rc), LL_ERROR); std::wstring temp_fn=fd->getFilenameW(); Server->destroy(fd); Server->deleteFile(temp_fn); if(rc==ERR_TIMEOUT || rc==ERR_ERROR || rc==ERR_BASE_DIR_LOST) return false; } else { std::wstring os_curr_path=curr_path+L"/"+fn; if(os_file_sep()!=L"/") { for(size_t i=0;i &diffs, bool all) { f->Seek(0); _i64 rsize=0; resetEntryState(); SFile cf; bool indirchange=false; size_t read; size_t line=0; char buffer[4096]; int indir_currdepth=0; int depth=0; int indir_curr_depth=0; int changelevel=0; if(all) { indirchange=true; } while( (read=f->Read(buffer, 4096))>0 ) { for(size_t i=0;i0) { --indir_currdepth; } if(cf.name!=L"..") { ++depth; } else { --depth; if(indirchange==true && depth==changelevel) { if(!all) { indirchange=false; } } } } else { if(indirchange==true || hasChange(line, diffs)) { rsize+=cf.size; } } ++line; } } if(read<4096) break; } return rsize; } bool BackupServerGet::doIncrBackup(void) { bool b=request_filelist_construct(false); if(!b) { has_error=true; return false; } Server->Log("Connecting to client...", LL_DEBUG); FileClient fc; sockaddr_in addr=getClientaddr(); _u32 rc=fc.Connect(&addr); if(rc!=ERR_CONNECTED) { ServerLogger::Log(clientid, "Incremental Backup of "+clientname+" failed - CONNECT error", LL_ERROR); has_error=true; return false; } Server->Log("Loading filelist...", LL_DEBUG); IFile *tmp=Server->openTemporaryFile(); rc=fc.GetFile("urbackup/filelist.ub", tmp); if(rc!=ERR_SUCCESS) { ServerLogger::Log(clientid, "Error getting filelist of "+clientname+". Errorcode: "+nconvert(rc), LL_ERROR); has_error=true; return false; } Server->Log("Starting incremental backup...", LL_DEBUG); SBackup last=getLastIncremental(); if(last.incremental==-2) { ServerLogger::Log(clientid, "Error retrieving last backup.", LL_ERROR); has_error=true; return false; } backupid=createBackupSQL(last.incremental+1, clientid, backuppath_single); std::wstring backupfolder=server_settings->getSettings()->backupfolder; std::wstring last_backuppath=backupfolder+os_file_sep()+widen(clientname)+os_file_sep()+last.path; std::wstring tmpfilename=tmp->getFilenameW(); Server->destroy(tmp); bool error=false; std::vector diffs=TreeDiff::diffTrees("urbackup/clientlist_"+nconvert(clientid)+".ub", wnarrow(tmpfilename), error); if(error) { ServerLogger::Log(clientid, "Error while calculating tree diff.", LL_ERROR); has_error=true; return false; } IFile *clientlist=Server->openFile("urbackup/clientlist_"+nconvert(clientid)+"_new.ub", MODE_WRITE); tmp=Server->openFile(tmpfilename, MODE_READ); ServerRunningUpdater *running_updater=new ServerRunningUpdater(backupid, false); Server->getThreadPool()->execute(running_updater); resetEntryState(); char buffer[4096]; _u32 read; std::wstring curr_path; SFile cf; int depth=0; int line=0; link_logcnt=0; bool indirchange=false; int changelevel; bool r_offline=false; _i64 filelist_size=tmp->Size(); _i64 filelist_currpos=0; int indir_currdepth=0; _i64 files_size=getIncrementalSize(tmp, diffs); tmp->Seek(0); _i64 transferred=0; unsigned int laststatsupdate=0; ServerStatus::setServerStatus(status, true); while( (read=tmp->Read(buffer, 4096))>0 ) { filelist_currpos+=read; for(size_t i=0;igetTimeMS(); if(ctime-laststatsupdate>status_update_intervall) { laststatsupdate=ctime; status.pcdone=(std::min)(100,(int)(((float)transferred)/((float)files_size/100.f)+0.5f)); status.hashqueuesize=(_u32)hashpipe->getNumElements(); status.prepare_hashqueuesize=(_u32)hashpipe_prepare->getNumElements(); ServerStatus::setServerStatus(status, true); } if(cf.isdir==true) { if(indirchange==false && hasChange(line, diffs) ) { indirchange=true; changelevel=depth; indir_currdepth=0; } else if(indirchange==true && r_offline==false) { if(cf.name!=L"..") ++indir_currdepth; else --indir_currdepth; } if(indirchange==false || r_offline==false ) { clientlist->Write("d\""+Server->ConvertToUTF8(cf.name)+"\"\n"); } else if(cf.name==L".." && indir_currdepth>0) { --indir_currdepth; clientlist->Write("d\"..\"\n"); } if(cf.name!=L"..") { curr_path+=L"/"+cf.name; std::wstring os_curr_path=curr_path; if(os_file_sep()!=L"/") { for(size_t i=0;iWrite("f\""+Server->ConvertToUTF8(cf.name)+"\" "+nconvert(cf.size)+" "+nconvert(cf.last_modified)+"\n"); } } } else { std::wstring os_curr_path=curr_path+L"/"+cf.name; if(os_file_sep()!=L"/") { for(size_t i=0;iLog(L"Creating hardlink from \""+srcpath+L"\" to \""+backuppath+os_curr_path+L"\" failed. Loading file...", LL_WARNING); } ++link_logcnt; if(r_offline==false) { bool b2=load_file(cf.name, curr_path, fc); if(!b2) { ServerLogger::Log(clientid, "Client "+clientname+" went offline.", LL_ERROR); r_offline=true; f_ok=false; } } else { f_ok=false; } } if(f_ok) { clientlist->Write("f\""+Server->ConvertToUTF8(cf.name)+"\" "+nconvert(cf.size)+" "+nconvert(cf.last_modified)+"\n"); } } } ++line; } } if(read<4096) break; } status.pcdone=100; ServerStatus::setServerStatus(status, true); Server->destroy(clientlist); if(r_offline==false) { Server->Log("Client ok. Copying full file...", LL_DEBUG); IFile *clientlist=Server->openFile("urbackup/clientlist_"+nconvert(clientid)+".ub", MODE_WRITE); bool clientlist_copy_err=false; if(clientlist!=NULL) { tmp->Seek(0); _u32 r=0; char buf[4096]; do { r=tmp->Read(buf, 4096); if(r>0) { _u32 written=0; _u32 rc; do { rc=clientlist->Write(buf+written, r-written); written+=rc; } while(written0); if(rc==0) { ServerLogger::Log(clientid, "Fatal error copying clientlist. Write error.", LL_ERROR); clientlist_copy_err=true; break; } } } while(r>0); Server->Log("Copying done.", LL_DEBUG); Server->Log("Creating symbolic links. -1", LL_DEBUG); std::wstring backupfolder=server_settings->getSettings()->backupfolder; std::wstring currdir=backupfolder+os_file_sep()+widen(clientname)+os_file_sep()+L"current"; Server->deleteFile(currdir); os_link_symbolic(backuppath, currdir); Server->Log("Creating symbolic links. -2", LL_DEBUG); currdir=backupfolder+os_file_sep()+L"clients"; if(!os_create_dir(currdir) && !os_directory_exists(currdir)) { Server->Log("Error creating \"clients\" dir for symbolic links", LL_ERROR); } currdir+=os_file_sep()+widen(clientname); Server->deleteFile(currdir); os_link_symbolic(backuppath, currdir); Server->Log("Symbolic links created.", LL_DEBUG); Server->destroy(clientlist); } else { ServerLogger::Log(clientid, "Fatal error copying clientlist. Open error.", LL_ERROR); } if(!clientlist_copy_err) { Server->deleteFile("urbackup/clientlist_"+nconvert(clientid)+"_new.ub"); } } else { Server->Log("Client disconnected while backing up. Copying partial file...", LL_DEBUG); Server->deleteFile("urbackup/clientlist_"+nconvert(clientid)+".ub"); moveFile(L"urbackup/clientlist_"+convert(clientid)+L"_new.ub", L"urbackup/clientlist_"+convert(clientid)+L".ub"); } running_updater->stop(); updateRunning(false); Server->destroy(tmp); Server->deleteFile(tmpfilename); setBackupDone(); return !r_offline; } bool BackupServerGet::hasChange(size_t line, const std::vector &diffs) { return std::binary_search(diffs.begin(), diffs.end(), line); } void BackupServerGet::hashFile(std::wstring dstpath, IFile *fd) { unsigned int l_backup_id=backupid; CWData data; data.addString(Server->ConvertToUTF8(fd->getFilenameW())); data.addUInt(l_backup_id); data.addString(Server->ConvertToUTF8(dstpath)); ServerLogger::Log(clientid, "GT: Loaded file \""+ExtractFileName(Server->ConvertToUTF8(dstpath))+"\"", LL_DEBUG); Server->destroy(fd); hashpipe_prepare->Write(data.getDataPtr(), data.getDataSize() ); } bool BackupServerGet::constructBackupPath(void) { time_t tt=time(NULL); #ifdef _WIN32 tm lt; tm *t=< localtime_s(t, &tt); #else tm *t=localtime(&tt); #endif char buffer[500]; strftime(buffer, 500, "%y%m%d-%H%M", t); backuppath_single=widen((std::string)buffer); std::wstring backupfolder=server_settings->getSettings()->backupfolder; backuppath=backupfolder+os_file_sep()+widen(clientname)+os_file_sep()+backuppath_single; return os_create_dir(backuppath); } std::wstring BackupServerGet::constructImagePath(const std::wstring &letter) { time_t tt=time(NULL); #ifdef _WIN32 tm lt; tm *t=< localtime_s(t, &tt); #else tm *t=localtime(&tt); #endif char buffer[500]; strftime(buffer, 500, "%y%m%d-%H%M", t); std::wstring backupfolder_uncompr=server_settings->getSettings()->backupfolder_uncompr; return backupfolder_uncompr+os_file_sep()+widen(clientname)+os_file_sep()+L"Image_"+letter+L"_"+widen((std::string)buffer)+L".vhd"; } void BackupServerGet::updateLastBackup(void) { q_set_last_backup->Bind(clientid); q_set_last_backup->Write(); q_set_last_backup->Reset(); } void BackupServerGet::updateLastImageBackup(void) { q_set_last_image_backup->Bind(clientid); q_set_last_image_backup->Write(); q_set_last_image_backup->Reset(); } std::string BackupServerGet::sendClientMessage(const std::string &msg, const std::wstring &errmsg, unsigned int timeout, bool logerr) { CTCPStack tcpstack; IPipe *cc=Server->ConnectStream(inet_ntoa(getClientaddr().sin_addr), serviceport, 10000); if(cc==NULL) { if(logerr) ServerLogger::Log(clientid, "Connecting to ClientService of \""+clientname+"\" failed - CONNECT error", LL_ERROR); else Server->Log("Connecting to ClientService of \""+clientname+"\" failed - CONNECT error", LL_DEBUG); return ""; } tcpstack.Send(cc, server_identity+msg); std::string ret; unsigned int starttime=Server->getTimeMS(); bool ok=false; bool herr=false; while(Server->getTimeMS()-starttime<=timeout) { size_t rc=cc->Read(&ret, timeout); if(rc==0) { ServerLogger::Log(clientid, errmsg, LL_ERROR); break; } tcpstack.AddData((char*)ret.c_str(), ret.size()); size_t packetsize; char *pck=tcpstack.getPacket(&packetsize); if(pck!=NULL && packetsize>0) { ret.resize(packetsize); memcpy(&ret[0], pck, packetsize); delete [] pck; return ret; } } ServerLogger::Log(clientid, L"Timeout: "+errmsg, LL_ERROR); Server->destroy(cc); return ""; } bool BackupServerGet::sendClientMessage(const std::string &msg, const std::string &retok, const std::wstring &errmsg, unsigned int timeout, bool logerr) { CTCPStack tcpstack; IPipe *cc=Server->ConnectStream(inet_ntoa(getClientaddr().sin_addr), serviceport, 10000); if(cc==NULL) { if(logerr) ServerLogger::Log(clientid, "Connecting to ClientService of \""+clientname+"\" failed - CONNECT error", LL_ERROR); else Server->Log("Connecting to ClientService of \""+clientname+"\" failed - CONNECT error", LL_DEBUG); return false; } tcpstack.Send(cc, server_identity+msg); std::string ret; unsigned int starttime=Server->getTimeMS(); bool ok=false; bool herr=false; while(Server->getTimeMS()-starttime<=timeout) { size_t rc=cc->Read(&ret, timeout); if(rc==0) { if(logerr) ServerLogger::Log(clientid, errmsg, LL_ERROR); else Server->Log(errmsg, LL_ERROR); break; } tcpstack.AddData((char*)ret.c_str(), ret.size()); size_t packetsize; char *pck=tcpstack.getPacket(&packetsize); if(pck!=NULL && packetsize>0) { ret=pck; delete [] pck; if(ret!=retok) { herr=true; if(logerr) ServerLogger::Log(clientid, errmsg, LL_ERROR); else Server->Log(errmsg, LL_ERROR); break; } else { ok=true; break; } } } if(!ok && !herr) { if(logerr) ServerLogger::Log(clientid, L"Timeout: "+errmsg, LL_ERROR); else Server->Log(L"Timeout: "+errmsg, LL_ERROR); } Server->destroy(cc); return ok; } void BackupServerGet::start_shadowcopy(const std::string &path) { sendClientMessage("START SC \""+path+"\"", "DONE", L"Activating shadow copy on \""+widen(clientname)+L"\" failed", shadow_copy_timeout); } void BackupServerGet::stop_shadowcopy(const std::string &path) { sendClientMessage("STOP SC \""+path+"\"", "DONE", L"Removing shadow copy on \""+widen(clientname)+L"\" failed", shadow_copy_timeout); } void BackupServerGet::notifyClientBackupSuccessfull(void) { sendClientMessage("DID BACKUP", "OK", L"Sending status to client failed", 10000); } void BackupServerGet::sendClientBackupIncrIntervall(void) { sendClientMessage("INCRINTERVALL \""+nconvert(server_settings->getSettings()->update_freq_incr)+"\"", "OK", L"Sending incrintervall to client failed", 10000); } void BackupServerGet::sendSettings(void) { std::string s_settings; std::vector settings_names=getSettingsList(); for(size_t i=0;igetValue(key, &value) ) if(!settings->getValue(key, &value) ) key=L""; if(!key.empty()) { s_settings+=Server->ConvertToUTF8(key)+"="+Server->ConvertToUTF8(value)+"\n"; } } escapeClientMessage(s_settings); sendClientMessage("SETTINGS "+s_settings, "OK", L"Sending settings to client failed", 10000); } bool BackupServerGet::getClientSettings(void) { FileClient fc; sockaddr_in addr=getClientaddr(); _u32 rc=fc.Connect(&addr); if(rc!=ERR_CONNECTED) { ServerLogger::Log(clientid, "Getting Client settings of "+clientname+" failed - CONNECT error", LL_ERROR); return false; } IFile *tmp=Server->openTemporaryFile(); if(tmp==NULL) { ServerLogger::Log(clientid, "Error creating temporary file in BackupServerGet::getClientSettings", LL_ERROR); return false; } rc=fc.GetFile("urbackup/settings.cfg", tmp); if(rc!=ERR_SUCCESS) { ServerLogger::Log(clientid, "Error getting Client settings of "+clientname+". Errorcode: "+nconvert(rc), LL_ERROR); return false; } ISettingsReader *sr=Server->createFileSettingsReader(tmp->getFilename()); std::vector setting_names=getSettingsList(); bool mod=false; for(size_t i=0;igetValue(key, &value) ) { bool b=updateClientSetting(key, value); if(b) mod=true; } } Server->destroy(sr); bool b=updateClientSetting(L"client_overwrite", L"true"); if(b) mod=true; std::string tmp_fn=tmp->getFilename(); Server->deleteFile(tmp_fn); Server->destroy(tmp); if(mod) { server_settings->update(); unloadSQL(); prepareSQL(); } return true; } bool BackupServerGet::updateClientSetting(const std::wstring &key, const std::wstring &value) { std::wstring tmp; if(settings_client->getValue(key, &tmp)==false ) { q_insert_setting->Bind(key); q_insert_setting->Bind(value); q_insert_setting->Bind(clientid); q_insert_setting->Write(); q_insert_setting->Reset(); return true; } else if(tmp!=value) { q_update_setting->Bind(value); q_update_setting->Bind(key); q_update_setting->Bind(clientid); q_update_setting->Write(); q_update_setting->Reset(); return true; } return false; } void BackupServerGet::setBackupComplete(void) { q_set_complete->Bind(backupid); q_set_complete->Write(); q_set_complete->Reset(); } void BackupServerGet::setBackupDone(void) { q_set_done->Bind(backupid); q_set_done->Write(); q_set_done->Reset(); } void BackupServerGet::setBackupImageComplete(void) { q_set_image_complete->Bind(backupid); q_set_image_complete->Write(); q_set_image_complete->Reset(); } void BackupServerGet::sendToPipe(const std::string &msg) { pipe->Write(msg); } int BackupServerGet::getPCDone(void) { SStatus st=ServerStatus::getStatus(clientname); if(!st.has_status) return 0; else return st.pcdone; } void BackupServerGet::sendClientLogdata(void) { q_get_unsent_logdata->Bind(clientid); db_results res=q_get_unsent_logdata->Read(); q_get_unsent_logdata->Reset(); for(size_t i=0;iConvertToUTF8(res[i][L"logdata"]); escapeClientMessage(logdata); sendClientMessage("2LOGDATA "+wnarrow(res[i][L"created"])+" "+logdata, "OK", L"Sending logdata to client failed", 10000); q_set_logdata_sent->Bind(res[i][L"id"]); q_set_logdata_sent->Write(); q_set_logdata_sent->Reset(); } } void BackupServerGet::saveClientLogdata(int image, int incremental) { int errors=0; int warnings=0; int infos=0; std::wstring logdata=ServerLogger::getLogdata(clientid, errors, warnings, infos); q_save_logdata->Bind(clientid); q_save_logdata->Bind(logdata); q_save_logdata->Bind(errors); q_save_logdata->Bind(warnings); q_save_logdata->Bind(infos); q_save_logdata->Bind(image); q_save_logdata->Bind(incremental); q_save_logdata->Write(); q_save_logdata->Reset(); ServerLogger::reset(clientid); } const unsigned int stat_update_skip=20; const unsigned int sector_size=512; const unsigned int sha_size=32; void writeZeroblockdata(void) { const int64 vhd_blocksize=(1024*1024/4); unsigned char *zeroes=new unsigned char[vhd_blocksize]; memset(zeroes, 0, vhd_blocksize); unsigned char dig[sha_size]; sha256(zeroes, vhd_blocksize, dig); IFile *out=Server->openFile("zero.hash", MODE_WRITE); out->Write((char*)dig, sha_size); Server->destroy(out); delete []zeroes; } bool BackupServerGet::doImage(const std::wstring &pParentvhd, int incremental, int incremental_ref) { CTCPStack tcpstack; IPipe *cc=Server->ConnectStream(inet_ntoa(getClientaddr().sin_addr), serviceport, 10000); if(cc==NULL) { ServerLogger::Log(clientid, "Connecting to ClientService of \""+clientname+"\" failed - CONNECT error", LL_ERROR); return false; } if(pParentvhd.empty()) { tcpstack.Send(cc, server_identity+"FULL IMAGE letter=C:"); } else { IFile *hashfile=Server->openFile(pParentvhd+L".hash"); if(hashfile==NULL) { ServerLogger::Log(clientid, "Error opening hashfile", LL_ERROR); Server->destroy(cc); return false; } std::string ts=server_identity+"INCR IMAGE letter=C:&hashsize="+nconvert(hashfile->Size()); size_t rc=tcpstack.Send(cc, ts); if(rc==0) { ServerLogger::Log(clientid, "Sending 'INCR IMAGE' command failed", LL_ERROR); Server->destroy(cc); Server->destroy(hashfile); return false; } hashfile->Seek(0); char buffer[4096]; for(size_t i=0,hsize=(size_t)hashfile->Size();iRead(buffer, (_u32)tsend)!=tsend) { ServerLogger::Log(clientid, "Reading from hashfile failed", LL_ERROR); Server->destroy(cc); Server->destroy(hashfile); return false; } if(!cc->Write(buffer, tsend)) { ServerLogger::Log(clientid, "Sending hashdata failed", LL_ERROR); Server->destroy(cc); Server->destroy(hashfile); return false; } } Server->destroy(hashfile); } std::wstring imagefn=constructImagePath(L"C"); { std::string mbrd=getMBR(L"C"); if(mbrd.empty()) { ServerLogger::Log(clientid, "Error getting MBR data", LL_ERROR); } else { IFile *mbr_file=Server->openFile(imagefn+L".mbr", MODE_WRITE); mbr_file->Write(mbrd); Server->destroy(mbr_file); } } int64 free_space=os_free_space(ExtractFilePath(imagefn)); if(free_space!=-1 && free_spacegetTimeMS(); bool first=true; char buffer[4096]; unsigned int blocksize; unsigned int blockleft=0; int64 currblock=-1; char *blockdata=NULL; int64 drivesize; ServerVHDWriter *vhdfile=NULL; THREADPOOL_TICKET vhdfile_ticket; IVHDFile *r_vhdfile=NULL; IFile *hashfile=NULL; IFile *parenthashfile=NULL; int64 blockcnt=0; int64 numblocks=0; int64 totalblocks=0; int64 mbr_offset=0; _u32 off=0; std::string shadowdrive; int shadow_id=-1; bool persistent=false; unsigned char *zeroblockdata=NULL; int64 nextblock=0; int64 vhd_blocksize=(1024*1024)/4; ServerRunningUpdater *running_updater=new ServerRunningUpdater(backupid, true); Server->getThreadPool()->execute(running_updater); bool has_parent=false; if(!pParentvhd.empty()) has_parent=true; sha256_ctx shactx; sha256_init(&shactx); unsigned int stat_update_cnt=0; while(Server->getTimeMS()-starttime<=3600000) { size_t r=cc->Read(&buffer[off], 4096-off, 3600000); if(r!=0) r+=off; starttime=Server->getTimeMS(); off=0; if(r==0 ) { if(persistent && nextblock!=0) { while(true) { ServerStatus::setROnline(clientname, false); if(cc!=NULL) Server->destroy(cc); Server->Log("Trying to reconnect in doImage", LL_DEBUG); cc=Server->ConnectStream(inet_ntoa(getClientaddr().sin_addr), serviceport, 10000); if(cc==NULL) { std::string msg; if(pipe->Read(&msg, 0)>0) { if(msg.find("address")==0) { IScopedLock lock(clientaddr_mutex); memcpy(&clientaddr, &msg[7], sizeof(sockaddr_in) ); } else { pipe->Write(msg); } Server->wait(60000); } } else { ServerStatus::setROnline(clientname, true); Server->Log("Reconnected.", LL_DEBUG); break; } } if(pParentvhd.empty()) { tcpstack.Send(cc, server_identity+"FULL IMAGE letter=C:&shadowdrive="+shadowdrive+"&start="+nconvert(nextblock)+"&shadowid="+nconvert(shadow_id)); } else { std::string ts="INCR IMAGE letter=C:&shadowdrive="+shadowdrive+"&start="+nconvert(nextblock)+"&shadowid="+nconvert(shadow_id)+"&hashsize="+nconvert(parenthashfile->Size()); size_t rc=tcpstack.Send(cc, server_identity+ts); if(rc==0) { ServerLogger::Log(clientid, "Sending 'INCR IMAGE' command failed", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } parenthashfile->Seek(0); char buffer[4096]; for(size_t i=0,hsize=(size_t)parenthashfile->Size();iRead(buffer, (_u32)tsend)!=tsend) { ServerLogger::Log(clientid, "Reading from hashfile failed i="+nconvert(i), LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } if(!cc->Write(buffer, tsend)) { ServerLogger::Log(clientid, "Sending hashdata failed", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } } } off=0; starttime=Server->getTimeMS(); blockleft=0; currblock=-1; } else { ServerLogger::Log(clientid, "Pipe to client unexpectedly closed", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } } else { if(first) { first=false; if(r>=sizeof(unsigned int)) { memcpy(&blocksize, buffer, sizeof(unsigned int) ); off+=sizeof(unsigned int); vhd_blocksize/=blocksize; } if(blocksize==0xFFFFFFFF) { off+=sizeof(unsigned int); if(r>sizeof(uint64)) { std::string err; err.resize(r-sizeof(uint64) ); memcpy(&err[0], &buffer[off], r-off); ServerLogger::Log(clientid, "Request of image backup failed. Reason: "+err, LL_ERROR); } else { ServerLogger::Log(clientid, "Error on client. No reason given.", LL_ERROR); } Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } bool issmall=false; if(r>=sizeof(unsigned int)+sizeof(int64)) { memcpy(&drivesize, &buffer[off], sizeof(int64) ); off+=sizeof(int64); blockcnt=drivesize/blocksize; totalblocks=blockcnt; zeroblockdata=new unsigned char[blocksize]; memset(zeroblockdata, 0, blocksize); if(!has_parent) r_vhdfile=image_fak->createVHDFile(imagefn, false, drivesize+(int64)mbr_size, (unsigned int)vhd_blocksize*blocksize); else r_vhdfile=image_fak->createVHDFile(imagefn, pParentvhd, false); if(r_vhdfile==NULL) { ServerLogger::Log(clientid, L"Error opening VHD file \""+imagefn+L"\"", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } vhdfile=new ServerVHDWriter(r_vhdfile, blocksize, 5000, clientid); vhdfile_ticket=Server->getThreadPool()->execute(vhdfile); blockdata=vhdfile->getBuffer(); hashfile=Server->openFile(imagefn+L".hash", MODE_WRITE); if(hashfile==NULL) { ServerLogger::Log(clientid, L"Error opening Hashfile \""+imagefn+L".hash\"", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } if(has_parent) { parenthashfile=Server->openFile(pParentvhd+L".hash", MODE_READ); if(parenthashfile==NULL) { ServerLogger::Log(clientid, L"Error opening Parenthashfile \""+pParentvhd+L".hash\"", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } } mbr_offset=writeMBR(vhdfile, drivesize); } else { issmall=true; } if(r>=sizeof(unsigned int)+sizeof(int64)+sizeof(int64)) { memcpy(&blockcnt, &buffer[off], sizeof(int64) ); off+=sizeof(int64); } else { issmall=true; } if(r>=sizeof(unsigned int)+sizeof(int64)+sizeof(int64)+1) { char c_persistent=buffer[off]; if(c_persistent!=0) persistent=true; ++off; } else { issmall=true; } unsigned int shadowdrive_size=0; if(r>=sizeof(unsigned int)+sizeof(int64)+sizeof(int64)+1+sizeof(unsigned int)) { memcpy(&shadowdrive_size, &buffer[off], sizeof(unsigned int)); off+=sizeof(unsigned int); if(shadowdrive_size>0) { if( r>=sizeof(unsigned int)+sizeof(int64)+sizeof(int64)+1+sizeof(unsigned int)+shadowdrive_size) { shadowdrive.resize(shadowdrive_size); memcpy(&shadowdrive[0], &buffer[off], shadowdrive_size); off+=shadowdrive_size; } else { issmall=true; } } } else { issmall=true; } if(r>=sizeof(unsigned int)+sizeof(int64)+sizeof(int64)+1+sizeof(unsigned int)+shadowdrive_size+sizeof(int)) { memcpy(&shadow_id, &buffer[off], sizeof(int)); off+=sizeof(int); } else { issmall=true; } if(issmall) { ServerLogger::Log(clientid, "First packet to small", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); Server->getThreadPool()->waitFor(vhdfile_ticket); delete vhdfile; vhdfile=NULL; } if(parenthashfile!=NULL) Server->destroy(parenthashfile); if(hashfile!=NULL) Server->destroy(hashfile); running_updater->stop(); return false; } if(r==off) { off=0; continue; } } while(true) { if(blockleft==0) { if(currblock!=-1) // write current block { ++numblocks; ++stat_update_cnt; if(stat_update_cnt%stat_update_skip==0) { stat_update_cnt=0; if(blockcnt!=0) { if(has_parent) { status.pcdone=(int)(((double)currblock/(double)totalblocks)*100.0+0.5); } else { status.pcdone=(int)(((double)numblocks/(double)blockcnt)*100.0+0.5); } ServerStatus::setServerStatus(status, true); } } nextblock=updateNextblock(nextblock, currblock, &shactx, zeroblockdata, has_parent, vhdfile, hashfile, parenthashfile, blocksize, mbr_offset, vhd_blocksize); sha256_update(&shactx, (unsigned char *)blockdata, blocksize); vhdfile->writeBuffer(mbr_offset+currblock*blocksize, blockdata, blocksize); blockdata=vhdfile->getBuffer(); if(nextblock%vhd_blocksize==0 && nextblock!=0) { //Server->Log("Hash written "+nconvert(currblock), LL_DEBUG); unsigned char dig[sha_size]; sha256_final(&shactx, dig); hashfile->Write((char*)dig, sha_size); sha256_init(&shactx); } if(vhdfile->hasError()) { ServerLogger::Log(clientid, "FATAL ERROR: Could not write to VHD-File", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); std::vector wf;wf.push_back(vhdfile_ticket); Server->getThreadPool()->waitFor(wf); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } currblock=-1; } if(r-off>=sizeof(int64) ) { memcpy(&currblock, &buffer[off], sizeof(int64) ); if(currblock==-123) { int64 t_totalblocks=totalblocks; if(t_totalblocks%vhd_blocksize!=0) t_totalblocks+=vhd_blocksize-t_totalblocks%vhd_blocksize; nextblock=updateNextblock(nextblock, t_totalblocks, &shactx, zeroblockdata, has_parent, vhdfile, hashfile, parenthashfile, blocksize, mbr_offset, vhd_blocksize); if(nextblock%vhd_blocksize==0 && nextblock!=0) { //Server->Log("Hash written "+nconvert(nextblock), LL_INFO); unsigned char dig[sha_size]; sha256_final(&shactx, dig); hashfile->Write((char*)dig, sha_size); } Server->destroy(cc); if(hashfile!=NULL) Server->destroy(hashfile); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); } if(parenthashfile!=NULL) Server->destroy(parenthashfile); bool vhdfile_err=false; status.action_done=true; ServerStatus::setServerStatus(status); if(vhdfile!=NULL) { if(pingthread!=NULL) { pingthread->setStop(true); Server->getThreadPool()->waitFor(pingthread_ticket); pingthread=NULL; } vhdfile->doExit(); Server->getThreadPool()->waitFor(vhdfile_ticket); vhdfile_err=vhdfile->hasError(); delete vhdfile; vhdfile=NULL; } IFile *t_file=Server->openFile(imagefn, MODE_READ); db->BeginTransaction(); q_set_image_size->Bind(t_file->Size()); q_set_image_size->Bind(backupid); q_set_image_size->Write(); q_set_image_size->Reset(); q_update_images_size->Bind(clientid); q_update_images_size->Bind(t_file->Size()); q_update_images_size->Bind(clientid); q_update_images_size->Write(); q_update_images_size->Reset(); setBackupImageComplete(); db->EndTransaction(); Server->destroy(t_file); running_updater->stop(); updateRunning(true); return !vhdfile_err; } else if(currblock==-124 || #ifndef _WIN32 currblock==0xFFFFFFFFFFFFFFFFLLU) #else currblock==0xFFFFFFFFFFFFFFFF) #endif { if(r-off>sizeof(int64)) { std::string err; err.resize(r-off-sizeof(int64) ); memcpy(&err[0], &buffer[off+sizeof(int64)], r-off-sizeof(int64)); ServerLogger::Log(clientid, "Error on client occured: "+err, LL_ERROR); } Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); std::vector wf;wf.push_back(vhdfile_ticket); Server->getThreadPool()->waitFor(wf); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } else if(currblock==-125) //ping { off+=sizeof(int64); currblock=-1; } else { off+=sizeof(int64); blockleft=blocksize; } } else if(r-off>0) { char buf2[4096]; memcpy(buf2, &buffer[off], r-off); memcpy(buffer, buf2, r-off); off=(_u32)r-off; break; } else { off=0; break; } } else { unsigned int available=(std::min)(blockleft, (unsigned int)r-off); memcpy(&blockdata[blocksize-blockleft], &buffer[off], available); blockleft-=available; off+=available; if( off>=r ) { off=0; break; } } } } } ServerLogger::Log(clientid, "Timeout while transfering image data", LL_ERROR); Server->destroy(cc); if(vhdfile!=NULL) { vhdfile->freeBuffer(blockdata); vhdfile->doExitNow(); std::vector wf;wf.push_back(vhdfile_ticket); Server->getThreadPool()->waitFor(wf); delete vhdfile; vhdfile=NULL; } if(hashfile!=NULL) Server->destroy(hashfile); if(parenthashfile!=NULL) Server->destroy(parenthashfile); running_updater->stop(); return false; } unsigned int BackupServerGet::writeMBR(ServerVHDWriter *vhdfile, uint64 volsize) { unsigned char *mbr=(unsigned char *)vhdfile->getBuffer(); unsigned char *mptr=mbr; memcpy(mptr, mbr_code, 440); mptr+=440; int sig=rand(); memcpy(mptr, &sig, sizeof(int)); mptr+=sizeof(int); *mptr=0; ++mptr; *mptr=0; ++mptr; unsigned char partition[16]; partition[0]=0x80; partition[1]=0xfe; partition[2]=0xff; partition[3]=0xff; partition[4]=0x07; //ntfs partition[5]=0xfe; partition[6]=0xff; partition[7]=0xff; partition[8]=0x00; partition[9]=0x02; partition[10]=0x00; partition[11]=0x00; unsigned int sectors=(unsigned int)(volsize/((uint64)sector_size)); memcpy(&partition[12], §ors, sizeof(unsigned int) ); memcpy(mptr, partition, 16); mptr+=16; for(int i=0;i<3;++i) { memset(mptr, 0, 16); mptr+=16; } *mptr=0x55; ++mptr; *mptr=0xaa; vhdfile->writeBuffer(0, (char*)mbr, 512); for(int i=0;i<511;++i) { char *buf=vhdfile->getBuffer(); memset(buf, 0, 512); vhdfile->writeBuffer((i+1)*512, buf, 512); } return 512*512; } int64 BackupServerGet::updateNextblock(int64 nextblock, int64 currblock, sha256_ctx *shactx, unsigned char *zeroblockdata, bool parent_fn, ServerVHDWriter *parentfile, IFile *hashfile, IFile *parenthashfile, unsigned int blocksize, int64 mbr_offset, int64 vhd_blocksize) { unsigned char *blockdata=NULL; if(parent_fn) blockdata=new unsigned char[blocksize]; if(currblock-nextblock>vhd_blocksize) { while(true) { if(!parent_fn) { sha256_update(shactx, zeroblockdata, blocksize); } else { { IScopedLock lock(parentfile->getVHDMutex()); IVHDFile *vhd=parentfile->getVHD(); vhd->Seek(mbr_offset+nextblock*blocksize); size_t read; bool b=vhd->Read((char*)blockdata, blocksize, read); if(!b) Server->Log("Reading from VHD failed", LL_ERROR); } sha256_update(shactx, blockdata, blocksize); } ++nextblock; if(nextblock%vhd_blocksize==0) { unsigned char dig[sha_size]; sha256_final(shactx, dig); hashfile->Write((char*)dig, sha_size); sha256_init(shactx); break; } } while(currblock-nextblock>vhd_blocksize) { if(!parent_fn) { hashfile->Write((char*)zero_hash, sha_size); } else { bool b=parenthashfile->Seek((nextblock/vhd_blocksize)*sha_size); if(!b) { Server->Log("Seeking in parenthashfile failed", LL_ERROR); } char dig[sha_size]; _u32 rc=parenthashfile->Read(dig, sha_size); if(rc!=sha_size) Server->Log("Writing to parenthashfile failed", LL_ERROR); hashfile->Write(dig, sha_size); } nextblock+=vhd_blocksize; } } while(nextblockgetVHDMutex()); IVHDFile *vhd=parentfile->getVHD(); vhd->Seek(mbr_offset+nextblock*blocksize); size_t read; bool b=vhd->Read((char*)blockdata, blocksize, read); if(!b) Server->Log("Reading from VHD failed", LL_ERROR); } sha256_update(shactx, blockdata, blocksize); } ++nextblock; if(nextblock%vhd_blocksize==0 && nextblock!=0) { unsigned char dig[sha_size]; sha256_final(shactx, dig); hashfile->Write((char*)dig, sha_size); sha256_init(shactx); } } delete [] blockdata; return nextblock+1; } std::string BackupServerGet::getMBR(const std::wstring &dl) { std::string ret=sendClientMessage("MBR driveletter="+wnarrow(dl), L"Getting MBR for drive "+dl+L" failed", 10000); CRData r(&ret); char b; if(r.getChar(&b) && b==1 ) { char ver; if(r.getChar(&ver) ) { if(ver!=0) { ServerLogger::Log(clientid, L"Server version does not fit", LL_ERROR); } else { return ret; } } else { ServerLogger::Log(clientid, L"Could not read version information in MBR", LL_ERROR); } } else { ServerLogger::Log(clientid, L"Could not read MBR", LL_ERROR); } return ""; } void BackupServerGet::checkClientVersion(void) { std::string version=getFile("urbackup/version.txt"); if(!version.empty()) { std::string r=sendClientMessage("VERSION "+version, L"Sending version to client failed", 10000); if(r=="update") { IFile *sigfile=Server->openFile("urbackup/UrBackupUpdate.sig", MODE_READ); if(sigfile==NULL) { ServerLogger::Log(clientid, "Error opening sigfile", LL_ERROR); return; } IFile *updatefile=Server->openFile("urbackup/UrBackupUpdate.exe", MODE_READ); if(updatefile==NULL) { ServerLogger::Log(clientid, "Error opening updatefile", LL_ERROR); return; } size_t datasize=3*sizeof(unsigned int)+version.size()+(size_t)sigfile->Size()+(size_t)updatefile->Size(); CTCPStack tcpstack; IPipe *cc=Server->ConnectStream(inet_ntoa(getClientaddr().sin_addr), serviceport, 10000); if(cc==NULL) { ServerLogger::Log(clientid, "Connecting to ClientService of \""+clientname+"\" failed - CONNECT error", LL_ERROR); return; } std::string msg="CLIENTUPDATE "+nconvert(datasize); tcpstack.Send(cc, server_identity+msg); int timeout=10000; unsigned int c_size=(unsigned int)version.size(); if(!cc->Write((char*)&c_size, sizeof(unsigned int), timeout) ) { Server->destroy(cc); Server->destroy(sigfile); Server->destroy(updatefile); return; } if(!cc->Write(version, timeout) ) { Server->destroy(cc); Server->destroy(sigfile); Server->destroy(updatefile); return; } c_size=(unsigned int)sigfile->Size(); if(!cc->Write((char*)&c_size, sizeof(unsigned int), timeout) ) { Server->destroy(cc); Server->destroy(sigfile); Server->destroy(updatefile); return; } if(!sendFile(cc, sigfile, timeout) ) { Server->destroy(cc); Server->destroy(sigfile); Server->destroy(updatefile); return; } c_size=(unsigned int)updatefile->Size(); if(!cc->Write((char*)&c_size, sizeof(unsigned int), timeout) ) { Server->destroy(cc); Server->destroy(sigfile); Server->destroy(updatefile); return; } if(!sendFile(cc, updatefile, timeout) ) { Server->destroy(cc); Server->destroy(sigfile); Server->destroy(updatefile); return; } Server->destroy(sigfile); Server->destroy(updatefile); std::string ret; unsigned int starttime=Server->getTimeMS(); bool ok=false; while(Server->getTimeMS()-starttime<=10000) { size_t rc=cc->Read(&ret, timeout); if(rc==0) { ServerLogger::Log(clientid, "Reading from client failed in update", LL_ERROR); break; } tcpstack.AddData((char*)ret.c_str(), ret.size()); size_t packetsize; char *pck=tcpstack.getPacket(&packetsize); if(pck!=NULL && packetsize>0) { ret.resize(packetsize); memcpy(&ret[0], pck, packetsize); delete [] pck; if(ret=="ok") { ok=true; break; } else { ok=false; ServerLogger::Log(clientid, "Error in update: "+ret, LL_ERROR); break; } } } if(!ok) { ServerLogger::Log(clientid, L"Timeout: In client update", LL_ERROR); } Server->destroy(cc); ServerLogger::Log(clientid, L"Updated client successfully", LL_INFO); } } } bool BackupServerGet::sendFile(IPipe *cc, IFile *f, int timeout) { char buf[4096]; _u32 r; while((r=f->Read(buf, 4096))>0) { if(!cc->Write(buf, r, timeout)) return false; } return true; } sockaddr_in BackupServerGet::getClientaddr(void) { IScopedLock lock(clientaddr_mutex); return clientaddr; } #endif //CLIENT_ONLY