urbackup_backend/urbackupserver/server_dir_links.cpp
2014-07-27 10:38:34 +02:00

309 lines
8.3 KiB
C++

#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<ServerBackupDao::DirectoryLinkEntry> entries = backup_dao.getLinksInDirectory(clientid, escaped_target+os_file_sep()+L"*");
for(size_t i=0;i<entries.size();++i)
{
std::wstring subpath = entries[i].target.substr(target.size());
std::wstring new_link_path = new_target + subpath;
backup_dao.addDirectoryLink(clientid, entries[i].name, new_link_path);
}
}
IMutex* dir_link_mutex;
}
std::wstring escape_glob_sql(const std::wstring& glob)
{
std::wstring ret;
ret.reserve(glob.size());
for(size_t i=0;i<glob.size();++i)
{
if(glob[i]=='?')
{
ret+=L"[?]";
}
else if(glob[i]=='[')
{
ret+=L"[[]";
}
else if(glob[i]=='*')
{
ret+=L"[*]";
}
else
{
ret+=glob[i];
}
}
return ret;
}
bool link_directory_pool( ServerBackupDao& backup_dao, int clientid, const std::wstring& target_dir, const std::wstring& src_dir, const std::wstring& pooldir, bool with_transaction )
{
IScopedLock lock(dir_link_mutex);
std::wstring link_src_dir;
std::wstring pool_name;
bool refcount_bigger_one=false;
if(os_is_symlink(os_file_prefix(src_dir)))
{
if(!os_get_symlink_target(os_file_prefix(src_dir), link_src_dir))
{
Server->Log(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<ServerBackupDao::JournalEntry> journal_entries = backup_dao.getDirectoryLinkJournalEntries();
bool has_error=false;
for(size_t i=0;i<journal_entries.size();++i)
{
const ServerBackupDao::JournalEntry& je = journal_entries[i];
std::wstring symlink_real_target;
if(!os_is_symlink(je.linkname)
|| (os_get_symlink_target(je.linkname, symlink_real_target) && symlink_real_target!=je.linktarget) )
{
if(os_directory_exists(je.linktarget))
{
os_remove_symlink_dir(os_file_prefix(je.linkname));
if(!os_link_symbolic(os_file_prefix(je.linktarget), os_file_prefix(je.linkname)))
{
Server->Log(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<SSymlinkCallbackData*>(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);
}