urbackup_backend/urbackupclient/dllmain.cpp

985 lines
25 KiB
C++

/*************************************************************************
* UrBackup - Client/Server backup system
* Copyright (C) 2011-2016 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 "../vld.h"
#ifdef _WIN32
#define DLLEXPORT extern "C" __declspec (dllexport)
#else
#define DLLEXPORT extern "C"
#endif
#ifdef __linux__
#include <linux/fs.h>
#include <sys/ioctl.h>
#endif
#include <vector>
#ifndef STATIC_PLUGIN
#define DEF_SERVER
#endif
#include "../Interface/Server.h"
#ifndef STATIC_PLUGIN
IServer *Server;
#else
#include "../StaticPluginRegistration.h"
extern IServer* Server;
#define LoadActions LoadActions_urbackupclient
#define UnloadActions UnloadActions_urbackupclient
#endif
#include "../Interface/Action.h"
#include "../Interface/Database.h"
#include "../Interface/SessionMgr.h"
#include "../Interface/Pipe.h"
#include "../Interface/Query.h"
#include "../Interface/Thread.h"
#include "../Interface/File.h"
#include "../Interface/SettingsReader.h"
#include "../fsimageplugin/IFSImageFactory.h"
#include "../cryptoplugin/ICryptoFactory.h"
#include "../btrfs/btrfsplugin/IBtrfsFactory.h"
#include "../clouddrive/IClouddriveFactory.h"
#include "database.h"
#include "tokens.h"
#include "ClientService.h"
#include "client.h"
#include "../stringtools.h"
#include "ServerIdentityMgr.h"
#include "../urbackupcommon/os_functions.h"
#ifdef _WIN32
#include "DirectoryWatcherThread.h"
#include "win_sysvol.h"
#endif
#include "InternetClient.h"
#include <stdlib.h>
#include "file_permissions.h"
#include "FilesystemManager.h"
#include "../urbackupcommon/chunk_hasher.h"
#include "../urbackupcommon/WalCheckpointThread.h"
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#include "../common/miniz.h"
#include <vector>
namespace
{
#include "backup_client_db.h"
std::string get_backup_client_db_data()
{
size_t out_len;
void* cdata = tinfl_decompress_mem_to_heap(backup_client_db_z, backup_client_db_z_len, &out_len, TINFL_FLAG_PARSE_ZLIB_HEADER|TINFL_FLAG_COMPUTE_ADLER32);
if (cdata == NULL)
{
return std::string();
}
std::string ret(reinterpret_cast<char*>(cdata), reinterpret_cast<char*>(cdata) + out_len);
mz_free(cdata);
return ret;
}
}
PLUGIN_ID filesrv_pluginid;
IFSImageFactory *image_fak;
ICryptoFactory *crypto_fak;
IBtrfsFactory* btrfs_fak;
IClouddriveFactory* clouddrive_fak;
std::string server_identity;
std::string server_token;
const unsigned short default_urbackup_serviceport=35623;
void init_mutex1(void);
bool testEscape(void);
void do_restore(void);
void restore_wizard(void);
void upgrade(void);
bool upgrade_client(void);
void parse_devnum_test();
std::string lang="en";
std::string time_format_str_de="%d.%m.%Y %H:%M";
std::string time_format_str="%d.%m.%Y %H:%M";
#ifdef _WIN32
const std::string pw_file="pw.txt";
const std::string pw_change_file="pw_change.txt";
const std::string new_file="new.txt";
#else
const std::string pw_file="urbackup/pw.txt";
const std::string pw_change_file="urbackup/pw_change.txt";
const std::string new_file="urbackup/new.txt";
#endif
namespace
{
THREADPOOL_TICKET indexthread_ticket;
std::vector<THREADPOOL_TICKET> internetclient_tickets;
int64 roundUp(int64 numToRound, int64 multiple)
{
return ((numToRound + multiple - 1) / multiple) * multiple;
}
int64 roundDown(int64 numToRound, int64 multiple)
{
return ((numToRound / multiple) * multiple);
}
const int64 dm_block_size = 1 * 1024 * 1024;
void print_ext(const IFsFile::SFileExtent& ext, const std::string& file_dm_block_dev, int64& lin_off)
{
int64 start = roundUp(ext.volume_offset, dm_block_size);
int64 end = roundDown(ext.volume_offset + ext.size, dm_block_size);
if (end - start >= dm_block_size)
{
int64 bcount = (end - start) / 512;
std::cout << lin_off << " " << bcount << " linear " << file_dm_block_dev << " " << (start / 512) << std::endl;
lin_off += bcount;
}
}
void do_print_dm_file_extents(const std::string& fn)
{
std::unique_ptr<IFsFile> f(Server->openFile(fn, MODE_READ));
if (f.get() == NULL)
{
std::cerr << "Error opening file " << fn << " " << os_last_error_str() << std::endl;
exit(1);
}
#ifdef FS_IOC_FSSETXATTR
fsxattr attr = {};
#ifdef FS_XFLAG_IMMUTABLE
attr.fsx_xflags |= FS_XFLAG_IMMUTABLE;
#endif
ioctl(f->getOsHandle(), FS_IOC_FSSETXATTR, &attr);
#ifdef FS_XFLAG_NODUMP
attr.fsx_xflags |= FS_XFLAG_NODUMP;
#endif
ioctl(f->getOsHandle(), FS_IOC_FSSETXATTR, &attr);
#ifdef FS_XFLAG_NODEFRAG
attr.fsx_xflags |= FS_XFLAG_NODEFRAG;
#endif
ioctl(f->getOsHandle(), FS_IOC_FSSETXATTR, &attr);
#endif
std::string file_dm_block_dev = Server->getServerParameter("file-dm-block-dev");
int64 lin_off = 0;
int64 pos = 0;
bool more_data = true;
IFsFile::SFileExtent last_ext;
while (more_data)
{
std::vector<IFsFile::SFileExtent> exts = f->getFileExtents(pos, 0, more_data);
for (size_t i = 0; i < exts.size(); ++i)
{
if (last_ext.offset == -1)
{
last_ext = exts[i];
}
else if (last_ext.offset + last_ext.size != exts[i].offset
|| last_ext.volume_offset + last_ext.size != exts[i].volume_offset)
{
print_ext(last_ext, file_dm_block_dev, lin_off);
last_ext = exts[i];
}
else
{
last_ext.size += exts[i].size;
}
pos = (std::max)(exts[i].offset + exts[i].size, pos);
}
}
if (last_ext.offset != -1)
{
print_ext(last_ext, file_dm_block_dev, lin_off);
}
if ((lin_off * 512) < (f->Size() * 3) / 4)
{
std::cerr << "ERROR: Only " << PrettyPrintBytes(lin_off * 512) << " of " << PrettyPrintBytes(f->Size()) << " was usable" << std::endl;
exit(2);
}
exit(0);
}
}
DLLEXPORT void LoadActions(IServer* pServer)
{
Server=pServer;
#ifdef _DEBUG
parse_devnum_test();
#endif
std::string rmtest=Server->getServerParameter("rmtest");
if(!rmtest.empty())
{
os_remove_nonempty_dir(rmtest);
return;
}
std::string ssltest = Server->getServerParameter("ssltest");
if (!ssltest.empty())
{
IPipe* p = Server->ConnectSslStream("google.de", 443, 10000);
p->Write("GET / HTTP/1.0\r\n\r\n");
std::string ret;
while (p->Read(&ret) > 0)
{
Server->Log("SSL_OUT: " + ret);
}
return;
}
std::string print_dm_file_extents = Server->getServerParameter("print-dm-file-extents");
if (!print_dm_file_extents.empty())
{
do_print_dm_file_extents(print_dm_file_extents);
return;
}
#ifdef _WIN32
char t_lang[20];
GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT,LOCALE_SISO639LANGNAME ,t_lang,sizeof(t_lang));
lang=t_lang;
#endif
if(lang=="de")
{
time_format_str=time_format_str_de;
}
//writeZeroblockdata();
#ifdef _WIN32
wchar_t tmpp[MAX_PATH];
DWORD l;
if((l=GetTempPathW(MAX_PATH, tmpp))==0 || l>MAX_PATH )
{
wcscpy_s(tmpp,L"C:\\");
}
std::string w_tmp=Server->ConvertFromWchar(tmpp);
if(!w_tmp.empty() && w_tmp[w_tmp.size()-1]=='\\')
{
w_tmp.erase(w_tmp.size()-1, 1);
}
os_remove_nonempty_dir(w_tmp+os_file_sep()+"urbackup_client_tmp");
if(!os_create_dir(w_tmp+os_file_sep()+"urbackup_client_tmp"))
{
Server->wait(5000);
os_create_dir(w_tmp+os_file_sep()+"urbackup_client_tmp");
}
Server->setTemporaryDirectory(w_tmp+os_file_sep()+"urbackup_client_tmp");
#endif
str_map params;
crypto_fak = (ICryptoFactory *)Server->getPlugin(Server->getThreadID(), Server->StartPlugin("cryptoplugin", params));
if (crypto_fak == NULL)
{
Server->Log("Error loading cryptoplugin", LL_ERROR);
}
btrfs_fak = reinterpret_cast<IBtrfsFactory*>(Server->getPlugin(Server->getThreadID(), Server->StartPlugin("btrfsplugin", params)));
if (btrfs_fak == nullptr)
{
Server->Log("Error loading btrfsplugin", LL_ERROR);
}
clouddrive_fak = reinterpret_cast<IClouddriveFactory*>(Server->getPlugin(Server->getThreadID(), Server->StartPlugin("clouddriveplugin", params)));
if (clouddrive_fak == nullptr)
{
Server->Log("Error loading clouddriveplugin", LL_ERROR);
}
{
str_map params;
image_fak = (IFSImageFactory *)Server->getPlugin(Server->getThreadID(), Server->StartPlugin("fsimageplugin", params));
if (image_fak == NULL)
{
Server->Log("Error loading fsimageplugin", LL_ERROR);
}
}
if(Server->getServerParameter("restore_mode")=="true")
{
Server->setServerParameter("max_worker_clients", "1");
}
if(Server->getServerParameter("restore")=="true")
{
do_restore();
exit(10);
return;
}
if(Server->getServerParameter("restore_wizard")=="true")
{
restore_wizard();
exit(10);
return;
}
if (!Server->getServerParameter("mount").empty())
{
str_map secret_params;
ParseParamStrHttp(Server->getServerParameter("mount_secret_params"), &secret_params);
bool ret = FilesystemManager::mountFileSystem(Server->getServerParameter("mount"),
Server->getServerParameter("mount_params"), secret_params,
Server->getServerParameter("mount_path"));
exit(ret ? 1 : 0);
return;
}
#ifdef _WIN32
std::string restore=Server->getServerParameter("allow_restore");
if(restore=="default" && !Server->fileExists(Server->getServerWorkingDir()+"\\UrBackupClient.exe"))
{
//Client without tray icon
Server->setServerParameter("allow_restore", "server-confirms");
}
else if(restore.empty())
{
Server->setServerParameter("allow_restore", "default");
}
Server->setSocketWindowSizes(3 * 1024 * 1024, 128 * 1024);
#endif
init_chunk_hasher();
ServerIdentityMgr::init_mutex();
#ifdef _WIN32
DirectoryWatcherThread::init_mutex();
#endif
if(getFile(pw_file).size()<5)
{
writestring(Server->getSessionMgr()->GenerateSessionIDWithUser("",""), pw_file);
}
if(getFile(pw_change_file).size()<5)
{
write_file_only_admin(Server->getSessionMgr()->GenerateSessionIDWithUser("",""), pw_change_file);
}
if( !FileExists("urbackup/backup_client.db") )
{
writestring(get_backup_client_db_data(), "urbackup/backup_client.db");
}
#ifndef _DEBUG
change_file_permissions_admin_only("urbackup/backup_client.db");
#endif
str_map db_params;
db_params["wal_autocheckpoint"] = "0";
if(! Server->openDatabase("urbackup/backup_client.db", URBACKUPDB_CLIENT, db_params) )
{
Server->Log("Couldn't open Database backup_client.db", LL_ERROR);
exit(1);
}
WalCheckpointThread::init_mutex();
WalCheckpointThread* wal_checkpoint_thread = new WalCheckpointThread(10 * 1024 * 1024, 200 * 1024 * 1024,
"urbackup" + os_file_sep() + "backup_client.db", URBACKUPDB_CLIENT);
Server->createThread(wal_checkpoint_thread, "db checkpoint");
if (!upgrade_client())
{
Server->Log("Upgrading client database failed. Startup failed.", LL_ERROR);
exit(1);
}
#ifdef _WIN32
if( !FileExists("prefilebackup.bat") && FileExists("prefilebackup_new.bat") )
{
copy_file("prefilebackup_new.bat", "prefilebackup.bat");
Server->deleteFile("prefilebackup_new.bat");
}
#endif
#ifdef _WIN32
#define INITIAL_SETTINGS_PREFIX ""
#else
#define INITIAL_SETTINGS_PREFIX "urbackup/"
#endif
if( !FileExists("urbackup/data/settings.cfg") && FileExists(INITIAL_SETTINGS_PREFIX "initial_settings.cfg") )
{
std::unique_ptr<ISettingsReader> settings_reader(Server->createFileSettingsReader(INITIAL_SETTINGS_PREFIX "initial_settings.cfg"));
std::string access_keys;
std::string client_access_keys;
if (settings_reader->getValue("access_keys", &access_keys) && !access_keys.empty())
{
ClientDAO cd(Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT));
std::vector<std::string> toks;
Tokenize(access_keys, toks, ";");
for (size_t i = 0; i < toks.size(); ++i)
{
std::string username = getuntil(":", toks[i]);
std::string access_key = getafter(":", toks[i]);
if (username.size() > 1
&& !access_key.empty())
{
bool is_token = username[0] == 't';
username = username.substr(1);
if (!is_token)
{
bool is_user = username[0] == 'u';
os_create_dir(tokens::tokens_path);
std::string token_path;
if (is_user)
{
token_path = std::string(tokens::tokens_path) + os_file_sep() + "user_" + bytesToHex(username);
}
else
{
token_path = std::string(tokens::tokens_path) + os_file_sep() + "group_" + bytesToHex(username);
}
tokens::write_token(tokens::get_hostname(), is_user, username, token_path, cd, access_key);
}
else
{
client_access_keys += "key." + username + "=" +
access_key + "\n";
client_access_keys += "key_age." + username + "=" +
convert(Server->getTimeSeconds()) + "\n";
}
}
}
if (!client_access_keys.empty()
&& !FileExists("urbackup/access_keys.properties"))
{
write_file_only_admin(client_access_keys, "urbackup/access_keys.properties");
}
}
copy_file(INITIAL_SETTINGS_PREFIX "initial_settings.cfg", "urbackup/data/settings.cfg");
Server->deleteFile(INITIAL_SETTINGS_PREFIX "initial_settings.cfg");
}
std::vector<SFile> data_dirs = getFiles("urbackup");
#ifndef _DEBUG
for (SFile dir : data_dirs)
{
if (dir.isdir
&& next(dir.name, 0, "data"))
{
if (FileExists("urbackup/"+dir.name+"/settings.cfg"))
{
change_file_permissions_admin_only("urbackup/data/settings.cfg");
}
if (FileExists("urbackup/"+dir.name+"/filelist.ub"))
{
change_file_permissions_admin_only("urbackup/data/filelist.ub");
}
change_file_permissions_admin_only("urbackup/"+dir.name);
}
}
#endif
#ifndef _DEBUG
#ifdef _WIN32
change_file_permissions_admin_only("urbackup");
#endif
if(FileExists("debug.log"))
{
change_file_permissions_admin_only("debug.log");
}
#endif
bool do_leak_check=(Server->getServerParameter("leak_check")=="true");
ClientConnector::init_mutex();
filesrv_pluginid = Server->StartPlugin("fileserv", params);
IndexThread* it = new IndexThread();
if (!do_leak_check)
{
Server->createThread(it, "file indexing");
}
else
{
indexthread_ticket = Server->getThreadPool()->execute(it, "file indexing");
}
unsigned short urbackup_serviceport = default_urbackup_serviceport;
if(!Server->getServerParameter("urbackup_serviceport").empty())
{
urbackup_serviceport = static_cast<unsigned short>(atoi(Server->getServerParameter("urbackup_serviceport").c_str()));
}
IServer::BindTarget serviceport_bind_target = IServer::BindTarget_All;
if (Server->getServerParameter("internet_only_mode") == "true")
{
serviceport_bind_target = IServer::BindTarget_Localhost;
}
Server->StartCustomStreamService(new ClientService(), "urbackupserver", urbackup_serviceport, -1, serviceport_bind_target);
internetclient_tickets=InternetClient::start(do_leak_check);
#ifdef _WIN32
cacheVolumes();
#endif
Server->Log("Started UrBackupClient Backend...", LL_INFO);
Server->wait(1000);
}
DLLEXPORT void UnloadActions(void)
{
if(Server->getServerParameter("leak_check")=="true")
{
IndexThread::doStop();
Server->getThreadPool()->waitFor(indexthread_ticket);
ServerIdentityMgr::destroy_mutex();
InternetClient::stop(internetclient_tickets);
ClientConnector::destroy_mutex();
Server->destroyAllDatabases();
}
}
#ifdef STATIC_PLUGIN
namespace
{
static RegisterPluginHelper register_plugin(LoadActions, UnloadActions, 10);
}
#endif
void upgrade_client1_2(IDatabase *db)
{
db->Write("ALTER TABLE shadowcopies ADD vol TEXT");
}
void upgrade_client2_3(IDatabase *db)
{
db->Write("ALTER TABLE shadowcopies ADD refs INTEGER");
db->Write("ALTER TABLE shadowcopies ADD starttime DATE");
}
void upgrade_client3_4(IDatabase *db)
{
db->Write("ALTER TABLE shadowcopies ADD starttoken TEXT");
}
void upgrade_client4_5(IDatabase *db)
{
db->Write("DROP TABLE mdirs");
db->Write("CREATE TABLE mdirs ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT )");
db->Write("DROP TABLE mdirs_backup");
db->Write("CREATE TABLE mdirs_backup ( id INTEGER, name TEXT )");
db->Write("CREATE TABLE mfiles ( dir_id INTEGER, name TEXT );");
db->Write("CREATE TABLE mfiles_backup ( dir_id INTEGER, name TEXT );");
db->Write("CREATE INDEX IF NOT EXISTS mfiles_backup_idx ON mfiles_backup( dir_id ASC )");
db->Write("DELETE FROM files");
}
void upgrade_client5_6(IDatabase *db)
{
db->Write("DELETE FROM files");
}
void upgrade_client6_7(IDatabase *db)
{
db->Write("DELETE FROM files");
}
void upgrade_client7_8(IDatabase *db)
{
db->Write("DELETE FROM files");
}
void upgrade_client8_9(IDatabase *db)
{
db->Write("DELETE FROM files");
}
void upgrade_client9_10(IDatabase *db)
{
db->Write("CREATE TABLE filehashes (name TEXT, filesize INTEGER, modifytime INTEGER, hashdata BLOB)");
db->Write("CREATE UNIQUE INDEX filehashes_idx ON filehashes (name ASC)");
}
void update_client10_11(IDatabase *db)
{
db->Write("DROP TABLE filehashes");
db->Write("DROP INDEX IF EXISTS filehashes_idx");
db->Write("DELETE FROM files");
}
void update_client11_12(IDatabase *db)
{
db_results res = db->Read("SELECT MAX(tvalue) AS c FROM misc WHERE tkey='last_backup_filetime'");
db->Write("DELETE FROM misc WHERE tkey='last_backup_filetime'");
if(!res.empty())
{
db->Write("INSERT INTO misc (tkey, tvalue) VALUES ('last_backup_filetime', '"+res[0]["c"]+"')");
}
db_results misc = db->Read("SELECT tkey, tvalue FROM misc");
db->Write("DROP TABLE misc");
db->Write("CREATE TABLE misc (tkey TEXT UNIQUE, tvalue TEXT)");
IQuery* q_insert = db->Prepare("INSERT INTO misc (tkey, tvalue) VALUES (?, ?)");
for(size_t i=0;i<misc.size();++i)
{
q_insert->Bind(misc[i]["tkey"]);
q_insert->Bind(misc[i]["tvalue"]);
q_insert->Write();
q_insert->Reset();
}
}
void update_client12_13(IDatabase *db)
{
db->Write("ALTER TABLE backupdirs ADD optional INTEGER");
db->Write("UPDATE backupdirs SET optional=0 WHERE optional IS NULL");
}
void update_client13_14(IDatabase *db)
{
db->Write("ALTER TABLE journal_data ADD frn_high INTEGER");
db->Write("ALTER TABLE journal_data ADD parent_frn_high INTEGER");
db->Write("ALTER TABLE map_frn ADD frn_high INTEGER");
db->Write("ALTER TABLE map_frn ADD pid_high INTEGER");
db->Write("DELETE FROM journal_data");
db->Write("DELETE FROM map_frn");
db->Write("DELETE FROM journal_ids");
db->Write("DROP INDEX IF EXISTS frn_index");
db->Write("DROP INDEX IF EXISTS frn_pid_index");
db->Write("CREATE INDEX IF NOT EXISTS frn_index ON map_frn( frn ASC, frn_high ASC )");
db->Write("CREATE INDEX IF NOT EXISTS frn_pid_index ON map_frn( pid ASC, pid_high ASC )");
}
void update_client14_15(IDatabase *db)
{
db->Write("DELETE FROM journal_data");
db->Write("DELETE FROM map_frn");
db->Write("DELETE FROM journal_ids");
}
void update_client15_16(IDatabase *db)
{
db->Write("DELETE FROM files");
}
void update_client16_17(IDatabase *db)
{
db->Write("CREATE TABLE continuous_watch_queue (id INTEGER PRIMARY KEY, data_size INTEGER, data BLOB)");
db->Write("ALTER TABLE backupdirs ADD tgroup INTEGER");
db->Write("UPDATE backupdirs SET tgroup=0 WHERE tgroup IS NULL");
db->Write("CREATE TABLE fileaccess_tokens (id INTEGER PRIMARY KEY, accountname TEXT, token TEXT, is_user INTEGER)");
db->Write("CREATE UNIQUE INDEX fileaccess_tokens_unique ON fileaccess_tokens(accountname, is_user)");
}
void update_client17_18(IDatabase* db)
{
db->Write("DROP TABLE mfiles");
db->Write("DROP TABLE mfiles_backup");
}
void update_client18_19(IDatabase* db)
{
db->Write("CREATE TABLE token_group_memberships (uid INTEGER REFERENCES fileaccess_tokens(id) ON DELETE CASCADE, gid INTEGER REFERENCES fileaccess_tokens(id) ON DELETE CASCADE)");
db->Write("CREATE UNIQUE INDEX token_group_memberships_unique ON token_group_memberships(uid, gid)");
}
void update_client19_20(IDatabase* db)
{
db->Write("ALTER TABLE backupdirs ADD symlinked INTEGER DEFAULT 0");
db->Write("UPDATE backupdirs SET symlinked=0 WHERE symlinked IS NULL");
}
void update_client20_21(IDatabase* db)
{
db->Write("ALTER TABLE shadowcopies ADD clientsubname TEXT");
db->Write("UPDATE shadowcopies SET clientsubname='' WHERE clientsubname IS NULL");
}
void update_client21_22(IDatabase* db)
{
db->Write("ALTER TABLE files ADD tgroup INTEGER");
db->Write("UPDATE files SET tgroup=0 WHERE tgroup IS NULL");
}
void update_client22_23(IDatabase* db)
{
db->Write("DROP INDEX files_idx");
db->Write("DELETE FROM files");
db->Write("CREATE UNIQUE INDEX files_idx ON files (name ASC, tgroup)");
db->Write("UPDATE backupdirs SET optional=38 WHERE optional=0");
}
void update_client23_24(IDatabase* db)
{
db->Write("ALTER TABLE backupdirs ADD reset_keep INTEGER DEFAULT 0");
db->Write("UPDATE backupdirs SET reset_keep=0 WHERE reset_keep IS NULL");
db->Write("CREATE TABLE virtual_client_group_offsets (id INTEGER PRIMARY KEY, virtual_client TEXT UNIQUE, group_offset INTEGER)");
}
void update_client24_25(IDatabase* db)
{
db->Write("CREATE TABLE hardlinks (vol TEXT, frn_high INTEGER, frn_low INTEGER, parent_frn_high INTEGER, parent_frn_low INTEGER,"
"PRIMARY KEY(vol, frn_high, frn_low, parent_frn_high, parent_frn_low) ) WITHOUT ROWID");
}
void update_client25_26(IDatabase* db)
{
#ifdef _WIN32
if (os_get_file_type("urbctctl.exe")!=0)
{
system("urbctctl.exe reset all");
}
#endif
}
void update_client26_27(IDatabase* db)
{
db->Write("ALTER TABLE files ADD generation INTEGER DEFAULT 0");
}
void update_client27_28(IDatabase* db)
{
}
void update_client28_29(IDatabase* db)
{
db->Write("CREATE TABLE client_facets (id INTEGER PRIMARY KEY, name TEXT, server_identity TEXT)");
db->Write("ALTER TABLE backupdirs ADD facet INTEGER REFERENCES client_facets(id)");
db->Write("ALTER TABLE virtual_client_group_offsets ADD facet INTEGER REFERENCES client_facets(id)");
db->Write("ALTER TABLE fileaccess_tokens ADD facet INTEGER REFERENCES client_facets(id)");
db->Write("INSERT INTO client_facets (name, server_identity) VALUES ('default', '')");
int64 fid = db->getLastInsertID();
db->Write("UPDATE backupdirs SET facet=" + convert(fid));
db->Write("UPDATE virtual_client_group_offsets SET facet=" + convert(fid));
db->Write("UPDATE fileaccess_tokens SET facet=" + convert(fid));
os_rename_file("data", "data_" + convert(fid));
Server->deleteFile("urbackup/session_idents.txt");
ClientConnector::updateDefaultDirsSetting(db, true, 0, false, static_cast<int>(fid));
}
bool upgrade_client(void)
{
IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
IQuery *q=db->Prepare("SELECT tvalue FROM misc WHERE tkey='db_version'");
if(q==NULL)
return false;
db_results res_v=q->Read();
if(res_v.empty())
return false;
int ver=watoi(res_v[0]["tvalue"]);
int old_v;
int max_v = 29;
if (ver > max_v)
{
Server->Log("Client database version (" + convert(ver) + ") bigger than latest known version (" + convert(max_v) + ")."
" This client version cannot run with this database", LL_ERROR);
return false;
}
db->Write("PRAGMA journal_mode=WAL");
if (ver == max_v)
{
return true;
}
db->BeginWriteTransaction();
IQuery *q_update=db->Prepare("UPDATE misc SET tvalue=? WHERE tkey='db_version'");
do
{
old_v=ver;
switch(ver)
{
case 1:
upgrade_client1_2(db);
++ver;
break;
case 2:
upgrade_client2_3(db);
++ver;
break;
case 3:
upgrade_client3_4(db);
++ver;
break;
case 4:
upgrade_client4_5(db);
++ver;
break;
case 5:
upgrade_client5_6(db);
++ver;
break;
case 6:
upgrade_client6_7(db);
++ver;
break;
case 7:
upgrade_client7_8(db);
++ver;
break;
case 8:
upgrade_client8_9(db);
++ver;
break;
case 9:
upgrade_client9_10(db);
++ver;
break;
case 10:
update_client10_11(db);
++ver;
break;
case 11:
update_client11_12(db);
++ver;
break;
case 12:
update_client12_13(db);
++ver;
break;
case 13:
update_client13_14(db);
++ver;
break;
case 14:
update_client14_15(db);
++ver;
break;
case 15:
update_client15_16(db);
++ver;
break;
case 16:
update_client16_17(db);
++ver;
break;
case 17:
update_client17_18(db);
++ver;
break;
case 18:
update_client18_19(db);
++ver;
break;
case 19:
update_client19_20(db);
++ver;
break;
case 20:
update_client20_21(db);
++ver;
break;
case 21:
update_client21_22(db);
++ver;
break;
case 22:
update_client22_23(db);
++ver;
break;
case 23:
update_client23_24(db);
++ver;
break;
case 24:
update_client24_25(db);
++ver;
break;
case 25:
update_client25_26(db);
++ver;
break;
case 26:
update_client26_27(db);
++ver;
break;
case 27:
update_client27_28(db);
++ver;
break;
case 28:
update_client28_29(db);
++ver;
break;
default:
break;
}
if(ver!=old_v)
{
q_update->Bind(ver);
q_update->Write();
q_update->Reset();
}
}
while(old_v<ver);
db->EndTransaction();
db->destroyAllQueries();
return true;
}