urbackup_backend/urbackupserver/copy_storage.cpp

844 lines
26 KiB
C++

/*************************************************************************
* UrBackup - Client/Server backup system
* Copyright (C) 2011-2017 Martin Raiber
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
#include "copy_storage.h"
#include "dao/ServerBackupDao.h"
#include "dao/ServerCleanupDao.h"
#include "../stringtools.h"
#include "../urbackupcommon/os_functions.h"
#ifdef NO_EMBEDDED_LMDB
#include <lmdb.h>
#else
#include "lmdb/lmdb.h"
#endif
#include "../Interface/Server.h"
#include "../Interface/Mutex.h"
#include "database.h"
#include "server_dir_links.h"
#include "server_log.h"
#include "server_status.h"
#if defined(_WIN32) || defined(__APPLE__) || defined(__FreeBSD__)
#define stat64 stat
#endif
#ifndef _WIN32
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#else
#include <Windows.h>
#endif
namespace
{
std::string getBackupfolder(IDatabase *db)
{
db_results res = db->Read("SELECT value FROM settings_db.settings WHERE key='backupfolder' AND clientid=0");
if (!res.empty())
{
return res[0]["value"];
}
return std::string();
}
void deleteIncomplete(const std::string& dest_folder)
{
std::vector<SFile> clients = getFiles(dest_folder);
for (size_t i = 0; i < clients.size(); ++i)
{
if (!clients[i].isdir)
continue;
std::vector<SFile> backups = getFiles(dest_folder + os_file_sep() + clients[i].name);
for (size_t j = 0; j < backups.size(); ++j)
{
if (backups[j].name.find("_incomplete") != std::string::npos)
{
if (backups[j].isdir)
{
Server->Log("Deleting incomplete folder \"" + dest_folder + os_file_sep() + clients[i].name + os_file_sep() + backups[j].name + "\"...");
os_remove_nonempty_dir(os_file_prefix(dest_folder + os_file_sep() + clients[i].name + os_file_sep() + backups[j].name));
}
else
{
Server->Log("Deleting incomplete file \"" + dest_folder + os_file_sep() + clients[i].name + os_file_sep() + backups[j].name+"\"...");
Server->deleteFile(os_file_prefix(dest_folder + os_file_sep() + clients[i].name + os_file_sep() + backups[j].name));
}
}
if (backups[j].name == ".directory_pool")
{
std::vector<SFile> pool1 = getFiles(dest_folder + os_file_sep() + clients[i].name+os_file_sep()+ backups[j].name);
for (size_t k = 0; k < pool1.size(); ++k)
{
std::vector<SFile> pool2 = getFiles(dest_folder + os_file_sep() + clients[i].name + os_file_sep() + backups[j].name
+os_file_sep()+pool1[k].name);
for (size_t l = 0; l < pool2.size(); ++l)
{
if (pool2[l].name.find("_incomplete") != std::string::npos)
{
Server->Log("Deleting incomplete symlink pool folder \"" + dest_folder + os_file_sep() + clients[i].name + os_file_sep() + backups[j].name
+ os_file_sep() + pool1[k].name + os_file_sep() + pool2[l].name + "\"...");
os_remove_nonempty_dir(os_file_prefix(dest_folder + os_file_sep() + clients[i].name + os_file_sep() + backups[j].name
+ os_file_sep() + pool1[k].name + os_file_sep() + pool2[l].name));
}
}
}
}
}
}
}
bool openMdb(const std::string& dst_folder, MDB_env*& env)
{
if (!os_directory_exists(dst_folder + os_file_sep() + "inode_db"))
{
if (!os_create_dir(dst_folder + os_file_sep() + "inode_db"))
{
Server->Log("Error creating directory \"" + dst_folder + os_file_sep() + "inode_db" + "\". " + os_last_error_str(), LL_ERROR);
return false;
}
}
int rc = mdb_env_create(&env);
if(rc!=0)
{
Server->Log("Error creating mdb env (" + (std::string)mdb_strerror(rc)+")", LL_ERROR);
return false;
}
rc = mdb_env_set_maxreaders(env, 4094);
if (rc)
{
Server->Log("LMDB: Failed to set max readers (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
mdb_env_close(env);
return false;
}
uint64 envsize = 1ULL * 1024 * 1024 * 1024 * 1024; //1TB
int64 freespace = os_free_space(dst_folder);
if (freespace > 0
&& static_cast<uint64>(freespace)<envsize)
{
envsize = static_cast<uint64>(0.9*freespace);
}
rc = mdb_env_set_mapsize(env, envsize);
if (rc)
{
Server->Log("LMDB: Failed to set map size (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
mdb_env_close(env);
return false;
}
unsigned int flags = MDB_NOSUBDIR | MDB_NOMETASYNC;
rc = mdb_env_open(env, (dst_folder + os_file_sep() + "inode_db" + os_file_sep() + "inode_db.lmdb").c_str(), flags, 0664);
if (rc)
{
Server->Log("LMDB: Failed to open LMDB database file (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
mdb_env_close(env);
return false;
}
return true;
}
class ScopedCloseLmdbEnv
{
public:
ScopedCloseLmdbEnv(MDB_env* env)
: env(env)
{}
~ScopedCloseLmdbEnv() {
mdb_env_close(env);
}
private:
MDB_env* env;
};
bool getFileInode(const std::string& fpath, int64& inode)
{
#ifndef _WIN32
struct stat64 statbuf;
int rc = stat64(fpath.c_str(), &statbuf);
if (rc != 0)
{
Server->Log("Error with stat of " + fpath + " errorcode: " + convert(errno), LL_ERROR);
return false;
}
inode = statbuf.st_ino;
return true;
#else
HANDLE hFile = CreateFileW(Server->ConvertToWchar(os_file_prefix(fpath)).c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
Server->Log("Error opening file " + fpath + ". "+os_last_error_str(), LL_ERROR);
return false;
}
BY_HANDLE_FILE_INFORMATION fileInformation;
BOOL b = GetFileInformationByHandle(hFile, &fileInformation);
CloseHandle(hFile);
if(!b)
{
Server->Log("Error getting file information of " + fpath + ". " + os_last_error_str(), LL_ERROR);
return false;
}
LARGE_INTEGER li;
li.HighPart = fileInformation.nFileIndexHigh;
li.LowPart = fileInformation.nFileIndexLow;
inode = li.QuadPart;
return true;
#endif
}
std::string remove_incomplete_folder(const std::string& path)
{
std::vector<std::string> toks;
Tokenize(path, toks, os_file_sep());
std::string ret;
std::string inc_str = "_incomplete";
for (size_t i = 0; i < toks.size(); ++i)
{
if (i != 0)
{
ret += os_file_sep();
}
if (toks[i].size() > inc_str.size() + 1
&& toks[i].find(inc_str) == toks[i].size() - inc_str.size())
{
ret += toks[i].substr(0, toks[i].size() - inc_str.size());
}
else
{
ret += toks[i];
}
}
return ret;
}
bool copy_filebackup(const std::string& src_folder, const std::string& dst_folder, const std::string& pool_dest, MDB_txn* txn, MDB_dbi dbi, bool ignore_copy_errors)
{
bool has_error = false;
std::vector<SFile> files = getFiles(os_file_prefix(src_folder), &has_error);
if (has_error)
{
Server->Log("Error listing files is directory \"" + src_folder + "\". " + os_last_error_str(), LL_ERROR);
return false;
}
for (size_t i = 0; i < files.size(); ++i)
{
if (files[i].issym)
{
std::string sym_target;
if (!os_get_symlink_target(os_file_prefix(src_folder + os_file_sep() + files[i].name), sym_target))
{
Server->Log("Error getting symlink target of \"" + src_folder + os_file_sep() + files[i].name + "\". " + os_last_error_str(), LL_ERROR);
return false;
}
std::string directory_pool = ExtractFileName(ExtractFilePath(ExtractFilePath(sym_target, os_file_sep()), os_file_sep()), os_file_sep());
if (directory_pool == ".directory_pool")
{
std::string pool_path = pool_dest + os_file_sep() + ExtractFileName(ExtractFilePath(sym_target, os_file_sep()), os_file_sep())
+ os_file_sep() + ExtractFileName(sym_target, os_file_sep());
if (!os_directory_exists(pool_path)
&& !os_directory_exists(pool_path + "_incomplete"))
{
if (!os_create_dir_recursive(pool_path + "_incomplete"))
{
Server->Log("Error creating pool path \"" + pool_path + "_incomplete\". "+os_last_error_str(), LL_ERROR);
return false;
}
if (!os_directory_exists(os_file_prefix(sym_target)))
{
Server->Log("Directory pool path target \"" + sym_target + "\" does not exist", LL_ERROR);
if (!ignore_copy_errors)
{
return false;
}
}
else
{
if (!copy_filebackup(sym_target, pool_path + "_incomplete", pool_dest, txn, dbi, ignore_copy_errors))
{
return false;
}
//os_sync(pool_path + "_incomplete");
if (!os_rename_file(pool_path + "_incomplete", pool_path, NULL))
{
Server->Log("Error renaming to \"" + pool_path + "\". " + os_last_error_str(), LL_ERROR);
return false;
}
}
}
}
bool isdir = files[i].isdir;
if (!os_link_symbolic(sym_target, os_file_prefix(dst_folder + os_file_sep() + files[i].name), NULL, &isdir))
{
Server->Log("Error creating symlink at \"" + dst_folder + os_file_sep() + files[i].name + "\". " + os_last_error_str(), LL_ERROR);
return false;
}
}
else if (files[i].isdir)
{
if (!os_create_dir(os_file_prefix(dst_folder + os_file_sep() + files[i].name)))
{
Server->Log("Error creating folder \"" + dst_folder + os_file_sep() + files[i].name + "\". "+os_last_error_str(), LL_ERROR);
return false;
}
if (!copy_filebackup(src_folder + os_file_sep() + files[i].name, dst_folder + os_file_sep() + files[i].name, pool_dest, txn, dbi, ignore_copy_errors))
{
return false;
}
}
else
{
int64 inode;
if (!getFileInode(src_folder + os_file_sep() + files[i].name, inode))
{
Server->Log("Error getting inode of file " + src_folder + os_file_sep() + files[i].name, LL_ERROR);
return false;
}
MDB_val mdb_tkey;
mdb_tkey.mv_data = static_cast<void*>(&inode);
mdb_tkey.mv_size = sizeof(inode);
MDB_val mdb_tval;
int rc = mdb_get(txn, dbi, &mdb_tkey, &mdb_tval);
if (rc == 0)
{
std::string hl_source(reinterpret_cast<char*>(mdb_tval.mv_data), mdb_tval.mv_size);
if (os_get_file_type(os_file_prefix(hl_source))==0)
{
hl_source = remove_incomplete_folder(hl_source);
}
if (!os_create_hardlink(os_file_prefix(dst_folder + os_file_sep() + files[i].name),
os_file_prefix(hl_source), false, NULL))
{
Server->Log("Error creating hard link at \"" + src_folder + os_file_sep() + files[i].name+"\" to \""+ hl_source+"\"", LL_ERROR);
return false;
}
}
else if (rc == MDB_NOTFOUND)
{
std::string hl_source = dst_folder + os_file_sep() + files[i].name;
std::string error_str;
if (!copy_file(os_file_prefix(src_folder + os_file_sep() + files[i].name), os_file_prefix(hl_source), false, &error_str))
{
Server->Log("Error copying file from \"" + src_folder + os_file_sep() + files[i].name + "\" to \"" + dst_folder + os_file_sep() + files[i].name + "\". " + error_str, LL_ERROR);
if (!ignore_copy_errors)
{
return false;
}
}
mdb_tval.mv_data = &hl_source[0];
mdb_tval.mv_size = hl_source.size();
rc = mdb_put(txn, dbi, &mdb_tkey, &mdb_tval, 0);
if (rc != 0)
{
Server->Log("LMDB: mdb_put failed (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
return false;
}
}
else
{
Server->Log("LMDB: mdb_get failed (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
return false;
}
}
}
return true;
}
bool copy_folder_contents(const std::string& src, const std::string& dst)
{
std::vector<SFile> files = getFiles(src);
for (size_t i = 0; i < files.size(); ++i)
{
if (files[i].isdir)
{
Server->Log("Found folder " + src + os_file_sep() + files[i].name, LL_ERROR);
return false;
}
std::string error_str;
if (!copy_file(src + os_file_sep() + files[i].name,
dst + os_file_sep() + files[i].name, false, &error_str))
{
Server->Log("Error copying \"" + src + os_file_sep() + files[i].name + "\" to \"" + dst + os_file_sep() + files[i].name + "\"", LL_ERROR);
return false;
}
}
return true;
}
bool copy_with_ext(std::string src, std::string dst, std::string ext)
{
if (!FileExists(src + ext))
{
return true;
}
std::string error_str;
if (!copy_file(src + ext,
dst + ext, false, &error_str))
{
Server->Log("Error copying \"" + src +ext + "\" to \"" + dst +ext + "\"", LL_ERROR);
return false;
}
return true;
}
bool rename_with_ext(std::string dst_incomplete, std::string dst, std::string ext)
{
Server->deleteFile(dst + ext);
if(!os_rename_file(dst_incomplete +ext, dst+ext))
{
Server->Log("Error renaming \"" + dst_incomplete + ext + "\" to \"" + dst + ext + "\"", LL_ERROR);
return false;
}
return true;
}
bool copy_image_backup(const std::string& src, const std::string& dst_folder)
{
std::string src_name_incomplete = ExtractFileName(src)+"_incomplete";
if (!copy_with_ext(src, dst_folder + os_file_sep() + src_name_incomplete, ""))
{
return false;
}
if (!copy_with_ext(src, dst_folder + os_file_sep() + src_name_incomplete, ".mbr"))
{
return false;
}
if (!copy_with_ext(src, dst_folder + os_file_sep() + src_name_incomplete, ".hash"))
{
return false;
}
if (!copy_with_ext(src, dst_folder + os_file_sep() + src_name_incomplete, ".bitmap"))
{
return false;
}
if (!copy_with_ext(src, dst_folder + os_file_sep() + src_name_incomplete, ".cbitmap"))
{
return false;
}
if (!copy_with_ext(src, dst_folder + os_file_sep() + src_name_incomplete, ".sync"))
{
return false;
}
//os_sync(dst_folder);
std::string src_name = ExtractFileName(src);
if (FileExists(dst_folder + os_file_sep() + src_name_incomplete)
&& !rename_with_ext(dst_folder + os_file_sep() + src_name_incomplete,
dst_folder + os_file_sep() + src_name, ".mbr"))
{
return false;
}
if (FileExists(dst_folder + os_file_sep() + src_name_incomplete+".hash")
&& !rename_with_ext(dst_folder + os_file_sep() + src_name_incomplete,
dst_folder + os_file_sep() + src_name, ".hash"))
{
return false;
}
if (FileExists(dst_folder + os_file_sep() + src_name_incomplete + ".bitmap")
&& !rename_with_ext(dst_folder + os_file_sep() + src_name_incomplete,
dst_folder + os_file_sep() + src_name, ".bitmap"))
{
return false;
}
if (FileExists(dst_folder + os_file_sep() + src_name_incomplete + ".cbitmap")
&& !rename_with_ext(dst_folder + os_file_sep() + src_name_incomplete,
dst_folder + os_file_sep() + src_name, ".cbitmap"))
{
return false;
}
if (FileExists(dst_folder + os_file_sep() + src_name_incomplete + ".sync")
&& !rename_with_ext(dst_folder + os_file_sep() + src_name_incomplete,
dst_folder + os_file_sep() + src_name, ".sync"))
{
return false;
}
if (!rename_with_ext(dst_folder + os_file_sep() + src_name_incomplete,
dst_folder + os_file_sep() + src_name, ""))
{
return false;
}
return true;
}
void update_process_pcdone(size_t n_backups, size_t processed_backups,
ScopedProcess& storage_migration)
{
if (n_backups > 0)
{
int pcdone = (int)((processed_backups*100.f) / n_backups + 0.5f);
ServerStatus::setProcessPcDone(std::string(), storage_migration.getStatusId(), pcdone);
}
}
}
int copy_storage(const std::string& dest_folder, bool ignore_copy_errors)
{
logid_t logid = ServerLogger::getLogId(LOG_CATEGORY_CLEANUP);
ScopedProcess storage_migration(std::string(), sa_storage_migration, std::string(), logid, false, LOG_CATEGORY_CLEANUP);
IDatabase *db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER);
if (db == NULL)
{
ServerLogger::Log(logid, "Could not open files database", LL_ERROR);
return 1;
}
std::string backupfolder = getBackupfolder(db);
if (backupfolder.empty())
{
ServerLogger::Log(logid, "Error getting backupfolder", LL_ERROR);
return 1;
}
ServerLogger::Log(logid, "Deleting incomplete transfers...");
deleteIncomplete(dest_folder);
MDB_env* env = NULL;
if(!openMdb(dest_folder, env))
{
ServerLogger::Log(logid, "Error opening inode db", LL_ERROR);
return 1;
}
ScopedCloseLmdbEnv close_env(env);
ServerBackupDao backup_dao(db);
ServerCleanupDao cleanup_dao(db);
std::vector<int> clientids = backup_dao.getClientIds();
size_t n_backups = 0;
for (size_t i = 0; i < clientids.size(); ++i)
{
ServerCleanupDao::CondString clientname = cleanup_dao.getClientName(clientids[i]);
if (!clientname.exists)
{
continue;
}
std::vector<ServerCleanupDao::SFileBackupInfo> file_backups = cleanup_dao.getFileBackupsOfClient(clientids[i]);
for (size_t j = 0; j < file_backups.size(); ++j)
{
if (file_backups[j].done == 0)
{
continue;
}
if (os_directory_exists(os_file_prefix(dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path)))
{
continue;
}
if (!os_directory_exists(os_file_prefix(backupfolder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path)))
{
continue;
}
++n_backups;
}
std::vector<ServerCleanupDao::SImageBackupInfo> image_backups = cleanup_dao.getImageBackupsOfClient(clientids[i]);
for (size_t j = 0; j < image_backups.size(); ++j)
{
ServerCleanupDao::SImageBackupInfo& ibackup = image_backups[j];
if (ibackup.complete == 0)
{
continue;
}
if (!FileExists(ibackup.path))
{
continue;
}
std::string ibackup_name = ExtractFileName(ExtractFilePath(ibackup.path));
if (ibackup_name.find("Image_") != std::string::npos)
{
std::string ibackup_dest = dest_folder + os_file_sep() + clientname.value + os_file_sep() + ibackup_name;
if (!os_directory_exists(ibackup_dest))
{
++n_backups;
}
}
else
{
std::string dst_name = dest_folder + os_file_sep() + clientname.value + os_file_sep() + ExtractFileName(ibackup.path);
if (!FileExists(dst_name))
{
++n_backups;
}
}
}
}
ServerStatus::setProcessPcDone(std::string(), storage_migration.getStatusId(),
0);
size_t processed_backups = 0;
for (size_t i = 0; i < clientids.size(); ++i)
{
ServerCleanupDao::CondString clientname = cleanup_dao.getClientName(clientids[i]);
if (!clientname.exists)
{
continue;
}
if (!os_directory_exists(dest_folder + os_file_sep() + clientname.value)
&& !os_create_dir(dest_folder + os_file_sep() + clientname.value))
{
ServerLogger::Log(logid, "Error creating folder \"" + dest_folder + os_file_sep() + clientname.value + "\". "+os_last_error_str(), LL_ERROR);
return 1;
}
ServerLogger::Log(logid, "Copying backups of client \"" + clientname.value + "\"...", LL_INFO);
std::vector<ServerCleanupDao::SFileBackupInfo> file_backups = cleanup_dao.getFileBackupsOfClient(clientids[i]);
for (size_t j = 0; j < file_backups.size(); ++j)
{
if (file_backups[j].done == 0)
{
continue;
}
if (os_directory_exists(os_file_prefix(dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path)))
{
continue;
}
if (!os_directory_exists(os_file_prefix(backupfolder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path)))
{
ServerLogger::Log(logid, "Backup id " + convert(file_backups[j].id) + " path " + file_backups[j].path + " of client \"" + clientname.value + "\" does not exist on current storage. Skipping.", LL_WARNING);
continue;
}
IScopedLock client_lock(NULL);
dir_link_lock_client_mutex(clientids[i], client_lock);
ServerLogger::Log(logid, "Copying backup id " + convert(file_backups[j].id) + " path " + file_backups[j].path + " of client \"" + clientname.value + "\"...", LL_INFO);
MDB_txn* txn = NULL;
int rc = mdb_txn_begin(env, NULL, 0, &txn);
if (rc)
{
ServerLogger::Log(logid, "LMDB: Failed to open transaction handle (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
return 1;
}
MDB_dbi dbi;
rc = mdb_dbi_open(txn, NULL, 0, &dbi);
if (rc)
{
ServerLogger::Log(logid, "LMDB: Failed to open database (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
mdb_txn_abort(txn);
return 1;
}
std::string pool_dest = dest_folder + os_file_sep() + clientname.value + os_file_sep() + ".directory_pool";
if (!os_create_dir(dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path + "_incomplete"))
{
ServerLogger::Log(logid, "Error creating folder \""+ dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path + "_incomplete\". "+os_last_error_str(), LL_ERROR);
mdb_txn_abort(txn);
return 1;
}
if (!copy_filebackup(backupfolder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path,
dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path+"_incomplete", pool_dest, txn, dbi, ignore_copy_errors))
{
ServerLogger::Log(logid, "Copying backup id " + convert(file_backups[j].id) + " path " + file_backups[j].path + " of client \"" + clientname.value + "\" failed.", LL_ERROR);
mdb_txn_abort(txn);
continue;
}
//os_sync(dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path + "_incomplete");
if (!os_rename_file(os_file_prefix(dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path + "_incomplete"),
os_file_prefix(dest_folder + os_file_sep() + clientname.value + os_file_sep() + file_backups[j].path)))
{
ServerLogger::Log(logid, "Error renaming folder after copying. " + os_last_error_str(), LL_ERROR);
mdb_txn_abort(txn);
return 1;
}
rc = mdb_txn_commit(txn);
if (rc)
{
ServerLogger::Log(logid, "LMDB: mdb_txn_commit failed (" + (std::string)mdb_strerror(rc) + ")", LL_ERROR);
return 1;
}
++processed_backups;
update_process_pcdone(n_backups, processed_backups, storage_migration);
}
std::vector<ServerCleanupDao::SImageBackupInfo> image_backups = cleanup_dao.getImageBackupsOfClient(clientids[i]);
for (size_t j = 0; j < image_backups.size(); ++j)
{
ServerCleanupDao::SImageBackupInfo& ibackup = image_backups[j];
if (ibackup.complete == 0)
{
continue;
}
if (!FileExists(ibackup.path))
{
ServerLogger::Log(logid, "Image backup id " + convert(image_backups[j].id) + " path " + image_backups[j].path + " of client \"" + clientname.value + "\" does not exist on current storage. Skipping.", LL_WARNING);
continue;
}
std::string ibackup_name = ExtractFileName(ExtractFilePath(ibackup.path));
if (ibackup_name.find("Image_") != std::string::npos)
{
std::string ibackup_dest = dest_folder + os_file_sep() + clientname.value + os_file_sep() + ibackup_name;
if (os_directory_exists(ibackup_dest))
{
continue;
}
ServerLogger::Log(logid, "Copying image backup id " + convert(image_backups[j].id) + " path " + image_backups[j].path + " (whole parent folder) of client \"" + clientname.value + "\"...", LL_INFO);
if (!os_create_dir(ibackup_dest + "_incomplete"))
{
ServerLogger::Log(logid, "Error creating folder \"" + ibackup_dest+"_incomplete" + "\". "+os_last_error_str(), LL_ERROR);
return 1;
}
if (!copy_folder_contents(ExtractFilePath(ibackup.path), ibackup_dest + "_incomplete"))
{
ServerLogger::Log(logid, "Copying image backup id " + convert(image_backups[j].id) + " path " + image_backups[j].path + " of client \"" + clientname.value + "\" failed.", LL_ERROR);
continue;
}
if (!os_rename_file(os_file_prefix(ibackup_dest + "_incomplete"),
os_file_prefix(ibackup_dest)))
{
ServerLogger::Log(logid, "Error renaming image folder after copying. " + os_last_error_str(), LL_ERROR);
return 1;
}
++processed_backups;
update_process_pcdone(n_backups, processed_backups, storage_migration);
}
else
{
std::string dst_name = dest_folder + os_file_sep() + clientname.value + os_file_sep() + ExtractFileName(ibackup.path);
if (FileExists(dst_name))
{
continue;
}
ServerLogger::Log(logid, "Copying image backup id " + convert(image_backups[j].id) + " path " + image_backups[j].path + " of client \"" + clientname.value + "\"...", LL_INFO);
if (!copy_image_backup(ibackup.path, dest_folder + os_file_sep() + clientname.value))
{
ServerLogger::Log(logid, "Copying image backup id " + convert(image_backups[j].id) + " path " + image_backups[j].path + " of client \"" + clientname.value + "\" failed.", LL_ERROR);
continue;
}
}
}
}
ServerLogger::Log(logid, "Storage migration successfully finished.", LL_INFO);
return 0;
}