#pragma once #include "ClientMain.h" #include "../Interface/Thread.h" #include #include #include #include "../Interface/Mutex.h" #include "../Interface/Server.h" #include "../Interface/Condition.h" #include "../common/data.h" #include "../urbackupcommon/change_ids.h" #include #include "../stringtools.h" #include "server_settings.h" #include "database.h" #include "ServerDownloadThreadGroup.h" #include "server_log.h" #include "dao/ServerBackupDao.h" #include "dao/ServerFilesDao.h" #include "FileIndex.h" #include "create_files_index.h" #include "FileBackup.h" extern std::string server_identity; extern std::string server_token; class BackupServerContinuous : public IThread, public FileClient::QueueCallback { public: struct SSequence { int64 id; int64 next; bool updated; bool operator==(const SSequence& other) { return id==other.id && next==other.next; } }; struct SChange { SChange() { } SChange(char action, std::string fn1) : action(action), fn1(fn1) { } char action; std::string fn1; std::string fn2; bool operator==(const SChange& other) { return action==other.action && fn1==other.fn1 && fn2==other.fn2; } }; BackupServerContinuous(ClientMain* client_main, const std::string& continuous_path, const std::string& continuous_hash_path, const std::string& continuous_path_backup, const std::string& tmpfile_path, bool use_tmpfiles, int clientid, const std::string& clientname, int backupid, bool use_snapshots, bool use_reflink, IPipe* hashpipe_prepare) : client_main(client_main), collect_only(true), first_compaction(true), stop(false), continuous_path(continuous_path), continuous_hash_path(continuous_hash_path), continuous_path_backup(continuous_path_backup), tmpfile_path(tmpfile_path), use_tmpfiles(use_tmpfiles), clientid(clientid), clientname(clientname), backupid(backupid), use_snapshots(use_snapshots), use_reflink(use_reflink), hashpipe_prepare(hashpipe_prepare), has_fullpath_entryid_mapping_table(false) { mutex = Server->createMutex(); cond = Server->createCondition(); logid = ServerLogger::getLogId(clientid); local_hash.reset(new BackupServerHash(NULL, clientid, use_snapshots, use_reflink, use_tmpfiles, logid, use_snapshots, max_file_id)); } ~BackupServerContinuous() { Server->destroy(mutex); } void operator()() { server_settings.reset(new ServerSettings(Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER))); backupdao.reset(new ServerBackupDao(Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER))); filesdao.reset(new ServerFilesDao(Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER_FILES))); fileindex.reset(create_lmdb_files_index()); hashed_transfer_full = true; hashed_transfer_incr = true; transfer_incr_blockdiff = true; if(client_main->isOnInternetConnection()) { hashed_transfer_full = server_settings->getSettings()->internet_full_file_transfer_mode!="raw"; hashed_transfer_incr = server_settings->getSettings()->internet_incr_file_transfer_mode!="raw"; transfer_incr_blockdiff = server_settings->getSettings()->internet_incr_file_transfer_mode=="blockhash"; } else { hashed_transfer_full = server_settings->getSettings()->local_full_file_transfer_mode!="raw"; hashed_transfer_incr = server_settings->getSettings()->local_incr_file_transfer_mode!="raw"; transfer_incr_blockdiff = server_settings->getSettings()->local_incr_file_transfer_mode=="blockhash"; } while(true) { { IScopedLock lock(mutex); if(stop) { return; } size_t n_changes=changes.size(); bool curr_collect_only=collect_only; do { cond->wait(&lock); if(stop) { return; } } while(n_changes==changes.size() && curr_collect_only==collect_only); if(!collect_only && !compactChanges()) { client_main->sendToPipe("RESYNC"); return; } } if(!collect_only && !has_fullpath_entryid_mapping_table) { filesdao->createTemporaryPathLookupTable(); filesdao->populateTemporaryPathLookupTable(backupid); filesdao->createTemporaryPathLookupIndex(); has_fullpath_entryid_mapping_table=true; } if(!compacted_changes.empty()) { for(size_t i=0;iqueueStop(); server_download->join(-1); } if(has_fullpath_entryid_mapping_table) { filesdao->dropTemporaryPathLookupTable(); } delete this; } void addChanges(const std::string& change_data) { IScopedLock lock(mutex); changes.push_back(change_data); cond->notify_all(); } void setSequences(std::vector new_sequences) { IScopedLock lock(mutex); sequences=new_sequences; } void startExecuting() { IScopedLock lock(mutex); collect_only=false; cond->notify_all(); } void doStop() { IScopedLock lock(mutex); stop=true; cond->notify_all(); } void updateSettings(int tbackupid, const std::string& tcontinuous_path, const std::string& tcontinuous_hash_path, const std::string& tcontinuous_path_backup) { backupid = tbackupid; continuous_path = tcontinuous_path; continuous_hash_path = tcontinuous_hash_path; continuous_path_backup = tcontinuous_path_backup; } private: struct SQueueItem { bool queued_metdata; SChange change; }; bool compactChanges() { bool ret=true; for(size_t i=0;i::iterator it=std::find( compacted_changes.begin(), compacted_changes.end(), change); if(it==compacted_changes.end()) { compacted_changes.push_back(change); } } void addChange(SChange& change, char del_action, char mod_action) { for(size_t i=0;i tokens; Tokenize(fn, tokens, "/"); std::string fp; for(size_t i=0;ideleteFile(getFullpath(change.fn1)) || !Server->deleteFile(getFullHashpath(change.fn1)) ) { Server->Log("Error deleting file \""+change.fn1+"\"", LL_ERROR); return false; } else { return true; } } bool execDelDir(SChange& change) { if(!backupDir(change.fn1)) { return false; } if(!os_remove_nonempty_dir(getFullpath(change.fn1)) || !os_remove_nonempty_dir(getFullHashpath(change.fn1)) ) { Server->Log("Error deleting file \""+change.fn1+"\"", LL_ERROR); return false; } else { return true; } } bool execAddFile(SChange& change) { std::unique_ptr f(Server->openFile(getFullpath(change.fn1), MODE_WRITE)); if(!f.get()) { Server->Log("Error creating file \""+change.fn1+"\"", LL_ERROR); return false; } else { return true; } } bool execAddDir(SChange& change) { if(!os_create_dir(getFullpath(change.fn1)) || !os_create_dir(getFullHashpath(change.fn1)) ) { Server->Log("Error creating fir \""+change.fn1+"\"", LL_ERROR); return false; } else { return true; } } bool execRen(SChange& change) { std::string fn2=getFullpath(change.fn2); if(Server->fileExists(fn2)) { if(backupFile((change.fn2))==std::string()) { return false; } } if(!os_rename_file(os_file_prefix(getFullpath(change.fn1)), os_file_prefix(getFullpath(change.fn2))) || !os_rename_file(os_file_prefix(getFullHashpath(change.fn1)), os_file_prefix(getFullHashpath(change.fn2))) ) { Server->Log("Error renaming \""+change.fn1+"\" to \""+change.fn2+"\"", LL_ERROR); return false; } else { return true; } } bool constructFileClient(std::unique_ptr& new_fc) { new_fc.reset(new FileClient(false, server_identity, client_main->getProtocolVersions().file_protocol_version, client_main->isOnInternetConnection(), client_main, client_main)); _u32 rc = client_main->getClientFilesrvConnection(new_fc.get(), server_settings.get()); if(rc!=ERR_CONNECTED) { Server->Log("Could not connect to client in continous backup thread", LL_ERROR); return false; } return true; } void constructServerDownloadThread() { std::vector shares_without_snapshot; server_download.reset(new ServerDownloadThreadGroup(*fileclient.get(), fileclient_chunked.get(), continuous_path, continuous_hash_path, continuous_path, std::string(), hashed_transfer_full, false, clientid, clientname, std::string(), use_tmpfiles, tmpfile_path, server_token, use_reflink, backupid, true, hashpipe_prepare, client_main, client_main->getProtocolVersions().file_protocol_version, 0, logid, true, shares_without_snapshot, true, NULL, false, 1, server_settings.get(), true, filepath_corrections, max_file_id)); } bool execMod(SChange& change) { std::unique_ptr f(Server->openFile(getFullpath(change.fn1))); if(!fileclient.get()) { if(!constructFileClient(fileclient)) { return false; } } if(!fileclient_metadata.get()) { if(!constructFileClient(fileclient_metadata)) { return false; } } if(!fileclient_chunked.get()) { if(!client_main->getClientChunkedFilesrvConnection(fileclient_chunked, server_settings.get(), NULL)) { ServerLogger::Log(logid, "Connect error during continuous backup (fileclient_chunked-1)", LL_ERROR); return false; } else { fileclient_chunked->setDestroyPipe(true); if(fileclient_chunked->hasError()) { ServerLogger::Log(logid, "Connect error during continuous backup (fileclient_chunked)", LL_ERROR); return false; } } } std::string hash; std::string permissions; int64 filesize; int64 created; int64 modified; int64 accessed=0; //TODO _u32 rc = ERR_ERROR; // fileclient_metadata->GetFileHashAndMetadata(change.fn1, hash, permissions, filesize, created, modified); if(rc!=ERR_SUCCESS) { ServerLogger::Log(logid, "Error getting file hash and metadata for \""+(change.fn1)+"\" from "+clientname+". Errorcode: "+fileclient->getErrorString(rc)+" ("+convert(rc)+")", LL_ERROR); return false; } { ServerFilesDao::CondInt64 entryid = filesdao->lookupEntryIdByPath(getFullpath(change.fn1)); if(entryid.exists) { ServerFilesDao::SFindFileEntry fentry = filesdao->getFileEntry(entryid.value); if(fentry.exists) { local_hash->deleteFileSQL(*filesdao, *fileindex, reinterpret_cast(fentry.shahash.c_str()), fentry.filesize, fentry.rsize, fentry.clientid, fentry.backupid, fentry.incremental, fentry.id, fentry.prev_entry, fentry.next_entry, fentry.pointed_to, true, true, true, false, NULL); } } } bool tries_once; std::string ff_last; bool hardlink_limit = false; bool copied_file=false; int64 entryid=0; int entryclientid=0; int64 next_entryid=0; int64 rsize = -1; FileMetadata metadata(permissions, modified, created, accessed, std::string()); if(local_hash->findFileAndLink(getFullpath(change.fn1), NULL, getFullHashpath(change.fn1), hash, filesize, std::string(), true, tries_once, ff_last, hardlink_limit, copied_file, entryid, entryclientid, rsize, next_entryid, metadata, true, NULL)) { local_hash->addFileSQL(backupid, clientid, 1, getFullpath(change.fn1), getFullHashpath(change.fn1), hash, filesize, rsize, entryid, entryclientid, next_entryid, copied_file); return true; } if(!server_download.get()) { constructServerDownloadThread(); } std::string fn = (ExtractFileName(change.fn1)); std::string fpath = (ExtractFilePath(change.fn1)); if(f->Size()==0 || !transfer_incr_blockdiff) { server_download->addToQueueFull(0, fn, fn, fpath, fpath, filesize, metadata, false, false, 0, hash); } else { server_download->addToQueueChunked(0, fn, fn, fpath, fpath, filesize, metadata, false, hash); } return true; } std::string backupFile(const std::string& fn) { std::string filePath = continuous_path + fn; 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.%S", t); std::string backupPath = continuous_path_backup + buffer+"-"+convert(Server->getTimeSeconds())+os_file_sep()+fn; if(!os_create_dir_recursive(os_file_prefix(ExtractFilePath(backupPath)))) { return std::string(); } if(!os_rename_file(os_file_prefix(filePath), os_file_prefix(backupPath))) { return std::string(); } return backupPath; } bool backupDir(const std::string& dir) { return false; } virtual std::string getQueuedFileFull(FileClient::MetadataQueue& metadata, size_t& folder_items, bool& finish_script, int64& file_id) { for(std::deque::iterator it=dl_queue.begin(); it!=dl_queue.end();++it) { if(!it->queued_metdata && (it->change.action==CHANGE_ADD_FILE || it->change.action==CHANGE_MOD) ) { metadata=FileClient::MetadataQueue_MetadataAndHash; it->queued_metdata=true; folder_items=0; file_id=0; finish_script=false; return it->change.fn1; } } return std::string(); } virtual void unqueueFileFull(const std::string& fn, bool finish_script) { for(std::deque::iterator it=dl_queue.begin(); it!=dl_queue.end();++it) { if(it->change.fn1==fn) { it->queued_metdata=false; return; } } } virtual void resetQueueFull() { for(std::deque::iterator it=dl_queue.begin(); it!=dl_queue.end();++it) { it->queued_metdata=false; } } std::vector changes; std::vector sequences; std::vector compacted_changes; IMutex* mutex; ICondition* cond; ClientMain* client_main; bool collect_only; bool first_compaction; bool stop; std::string continuous_path; std::string continuous_hash_path; std::string continuous_path_backup; std::string tmpfile_path; bool use_tmpfiles; int clientid; bool use_snapshots; bool use_reflink; std::string clientname; int backupid; IPipe* hashpipe_prepare; std::unique_ptr local_hash; std::unique_ptr fileclient_chunked; std::unique_ptr fileclient; std::unique_ptr fileclient_metadata; std::unique_ptr server_settings; std::unique_ptr server_download; std::deque dl_queue; bool hashed_transfer_full; bool hashed_transfer_incr; bool transfer_incr_blockdiff; bool has_fullpath_entryid_mapping_table; std::unique_ptr backupdao; std::unique_ptr filesdao; std::unique_ptr fileindex; FilePathCorrections filepath_corrections; MaxFileId max_file_id; logid_t logid; };