#include "server_dir_links.h" #include "../urbackupcommon/os_functions.h" #include "../Interface/Server.h" #include "../stringtools.h" #include "server_settings.h" #include "../Interface/Mutex.h" namespace { void reference_all_sublinks(ServerBackupDao& backup_dao, int clientid, const std::wstring& target, const std::wstring& new_target) { std::wstring escaped_target = escape_glob_sql(target); std::vector entries = backup_dao.getLinksInDirectory(clientid, escaped_target+os_file_sep()+L"*"); for(size_t i=0;iLog(L"Could not get symlink target of source directory \""+src_dir+L"\".", LL_ERROR); return false; } pool_name = ExtractFileName(link_src_dir); if(pool_name.empty()) { Server->Log(L"Error extracting pool name from link source \""+link_src_dir+L"\"", LL_ERROR); return false; } backup_dao.addDirectoryLink(clientid, pool_name, target_dir); reference_all_sublinks(backup_dao, clientid, src_dir, target_dir); backup_dao.commit(); refcount_bigger_one=true; } else if(os_directory_exists(os_file_prefix(src_dir))) { std::wstring parent_src_dir; do { pool_name = widen(ServerSettings::generateRandomAuthKey(10))+convert(Server->getTimeSeconds())+convert(Server->getTimeMS()); parent_src_dir = pooldir + os_file_sep() + pool_name.substr(0, 2); link_src_dir = parent_src_dir + os_file_sep() + pool_name; } while (os_directory_exists(os_file_prefix(link_src_dir))); if(!os_directory_exists(os_file_prefix(parent_src_dir)) && !os_create_dir_recursive(os_file_prefix(parent_src_dir))) { Server->Log(L"Could not create directory for pool directory: \""+parent_src_dir+L"\"", LL_ERROR); return false; } backup_dao.addDirectoryLink(clientid, pool_name, src_dir); reference_all_sublinks(backup_dao, clientid, src_dir, target_dir); backup_dao.addDirectoryLink(clientid, pool_name, target_dir); int64 replay_entry_id; if(!with_transaction) { backup_dao.addDirectoryLinkJournalEntry(src_dir, link_src_dir); replay_entry_id = backup_dao.getLastId(); } backup_dao.commit(); void* transaction=NULL; if(with_transaction) { transaction=os_start_transaction(); if(!transaction) { Server->Log("Error starting filesystem transaction", LL_ERROR); backup_dao.removeDirectoryLink(clientid, src_dir); backup_dao.removeDirectoryLink(clientid, target_dir); return false; } } if(!os_rename_file(os_file_prefix(src_dir), os_file_prefix(link_src_dir), transaction)) { Server->Log(L"Could not rename folder \""+src_dir+L"\" to \""+link_src_dir+L"\"", LL_ERROR); os_finish_transaction(transaction); backup_dao.removeDirectoryLink(clientid, src_dir); backup_dao.removeDirectoryLink(clientid, target_dir); return false; } if(!os_link_symbolic(os_file_prefix(link_src_dir), os_file_prefix(src_dir), transaction)) { Server->Log(L"Could not create a symbolic link at \""+src_dir+L"\" to \""+link_src_dir+L"\"", LL_ERROR); os_rename_file(link_src_dir, src_dir, transaction); os_finish_transaction(transaction); backup_dao.removeDirectoryLink(clientid, src_dir); backup_dao.removeDirectoryLink(clientid, target_dir); return false; } if(with_transaction) { if(!os_finish_transaction(transaction)) { Server->Log("Error finishing filesystem transaction", LL_ERROR); backup_dao.removeDirectoryLink(clientid, src_dir); backup_dao.removeDirectoryLink(clientid, target_dir); return false; } } else { backup_dao.removeDirectoryLinkJournalEntry(replay_entry_id); } } else { return false; } if(!os_link_symbolic(os_file_prefix(link_src_dir), os_file_prefix(target_dir))) { Server->Log(L"Error creating symbolic link from \"" + link_src_dir +L"\" to \"" + target_dir+L"\" -2", LL_ERROR); backup_dao.removeDirectoryLink(clientid, target_dir); if(refcount_bigger_one) { backup_dao.removeDirectoryLinkGlob(clientid, escape_glob_sql(target_dir)+os_file_sep()+L"*"); } return false; } return true; } bool replay_directory_link_journal( ServerBackupDao& backup_dao ) { IScopedLock lock(dir_link_mutex); std::vector journal_entries = backup_dao.getDirectoryLinkJournalEntries(); bool has_error=false; for(size_t i=0;iLog(L"Error replaying symlink journal: Could create link at \""+je.linkname+L"\" to \""+je.linktarget+L"\"", LL_ERROR); has_error=true; } } } } backup_dao.removeDirectoryLinkJournalEntries(); return has_error; } namespace { struct SSymlinkCallbackData { SSymlinkCallbackData(ServerBackupDao* backup_dao, int clientid) : backup_dao(backup_dao), clientid(clientid) { } ServerBackupDao* backup_dao; int clientid; }; bool symlink_callback(const std::wstring &path, void* userdata) { SSymlinkCallbackData* data = reinterpret_cast(userdata); std::wstring pool_path; if(!os_get_symlink_target(path, pool_path)) { Server->Log(L"Error getting symlink path in pool of \""+path+L"\"", LL_ERROR); return false; } std::wstring pool_name = ExtractFileName(pool_path); if(pool_name.empty()) { Server->Log(L"Error extracting pool name from pool path \""+pool_path+L"\"", LL_ERROR); return false; } std::wstring target_raw; if(next(path,0, os_file_prefix(L""))) { target_raw = path.substr(os_file_prefix(L"").size()); } else { target_raw = path; } data->backup_dao->beginTransaction(); data->backup_dao->removeDirectoryLink(data->clientid, target_raw); bool ret = true; if(data->backup_dao->getDirectoryRefcount(data->clientid, pool_name)==0) { ret = remove_directory_link_dir(path, *data->backup_dao, data->clientid, false); ret = ret && os_remove_dir(os_file_prefix(pool_path)); if(!ret) { Server->Log(L"Error removing directory link \""+path+L"\" with pool path \""+pool_path+L"\"", LL_ERROR); } } else { data->backup_dao->removeDirectoryLinkGlob(data->clientid, escape_glob_sql(target_raw)+os_file_sep()+L"*"); } if(!os_remove_symlink_dir(os_file_prefix(path))) { Server->Log(L"Error removing symlink dir \""+path+L"\"", LL_ERROR); } data->backup_dao->endTransaction(); return true; } } bool remove_directory_link_dir(const std::wstring &path, ServerBackupDao& backup_dao, int clientid, bool delete_root) { IScopedLock lock(dir_link_mutex); SSymlinkCallbackData userdata(&backup_dao, clientid); return os_remove_nonempty_dir(os_file_prefix(path), symlink_callback, &userdata, delete_root); } void init_dir_link_mutex() { dir_link_mutex=Server->createMutex(); } void destroy_dir_link_mutex() { Server->destroy(dir_link_mutex); }