/************************************************************************* * 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 . **************************************************************************/ #include "../vld.h" #ifdef _WIN32 #define DLLEXPORT extern "C" __declspec (dllexport) #else #define DLLEXPORT extern "C" #endif #ifdef __linux__ #include #include #endif #include #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 #include "file_permissions.h" #include "FilesystemManager.h" #include "../urbackupcommon/chunk_hasher.h" #include "../urbackupcommon/WalCheckpointThread.h" #include "client_restore_http.h" #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES #include "../common/miniz.h" #include #include 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 == nullptr) { return std::string(); } std::string ret(reinterpret_cast(cdata), reinterpret_cast(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(); #define ADD_ACTION(x) Server->AddAction( new Actions::x ); 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 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 f(Server->openFile(fn, MODE_READ)); if (f.get() == nullptr) { 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 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 == nullptr) { Server->Log("Error loading cryptoplugin", LL_ERROR); } btrfs_fak = reinterpret_cast(Server->getPlugin(Server->getThreadID(), Server->StartPlugin("btrfsplugin", params))); if (btrfs_fak == nullptr) { Server->Log("Error loading btrfsplugin", LL_ERROR); } clouddrive_fak = reinterpret_cast(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 == nullptr) { 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("restore_http") == "true") { ADD_ACTION(status); ADD_ACTION(login); ADD_ACTION(get_clientnames); ADD_ACTION(get_backupimages); ADD_ACTION(start_download); ADD_ACTION(download_progress); ADD_ACTION(has_network_device); ADD_ACTION(ping_server); ADD_ACTION(has_internet_connection); ADD_ACTION(configure_server); ADD_ACTION(get_disks); ADD_ACTION(get_is_disk_mbr); ADD_ACTION(write_mbr); ADD_ACTION(get_partition); ADD_ACTION(restart); ADD_ACTION(get_keyboard_layouts); ADD_ACTION(set_keyboard_layout); ADD_ACTION(get_connection_settings); ADD_ACTION(get_spill_disks); ADD_ACTION(test_spill_disks); ADD_ACTION(setup_spill_disks); ADD_ACTION(cleanup_spill_disks); ADD_ACTION(resize_disk); ADD_ACTION(restore_finished); ADD_ACTION(resize_part); ADD_ACTION(capabilities); ADD_ACTION(get_tmpfn); Server->Log("Started UrBackup Restore HTTP backend...", LL_INFO); 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 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 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 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(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 std::thread sm([]() { FilesystemManager::startupMountFileSystems(); }); sm.detach(); 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;iBind(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(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==nullptr) 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_vEndTransaction(); db->destroyAllQueries(); return true; }