/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2021 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 "LocalFullFileBackup.h" #include "../urbackupcommon/filelist_utils.h" #include "../fileservplugin/IFileServ.h" #include "../urbackupcommon/chunk_hasher.h" #include "../urbackupcommon/file_metadata.h" #include "../common/data.h" #include "../fileservplugin/IFileMetadataPipe.h" #include "client.h" #include "../Interface/BackupFileSystem.h" #include bool LocalFullFileBackup::prepareBackuppath() { const std::time_t t = std::time(nullptr); char mbstr[100]; if (!std::strftime(mbstr, sizeof(mbstr), "%Y-%m-%d %H-%M.new", std::localtime(&t))) return false; std::string prefix = std::string(mbstr); log("Creating new subvolume for backup...", LL_INFO); if (!orig_backup_files->createSubvol(prefix)) { log("Error creating new subvolume for backup. " + orig_backup_files->lastError(), LL_ERROR); return false; } prepareBackupFiles(prefix); backup_files->createDir(".hashes"); backup_files->createDir(getBackupInternalDataDir()); return (backup_files->getFileType(getBackupInternalDataDir()) & EFileType_Directory)>0; } bool LocalFullFileBackup::run() { std::string server_token; server_token.resize(16); Server->secureRandomFill(&server_token[0], server_token.size()); server_token = bytesToHex(server_token); unsigned int flags = flag_with_orig_path | flag_with_proper_symlinks; int sha_version = 528; int running_jobs = 2; CWData data; data.addChar(IndexThread::IndexThreadAction_StartFullFileBackup); unsigned int curr_result_id = IndexThread::getResultId(); data.addUInt(curr_result_id); data.addString(server_token); data.addInt(backupgroup); data.addInt(flags); data.addString(clientsubname); data.addInt(sha_version); data.addInt(running_jobs); data.addInt(facet_id); data.addChar(1); /*std::string async_id; async_id.resize(16); Server->randomFill(&async_id[0], async_id.size()); //for phash data.addString2(async_id);*/ log("Indexing files to backup", LL_INFO); IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize()); IndexThread::refResult(curr_result_id); bool with_phash = false; while (true) { std::string msg; bool has_result = IndexThread::getResult(curr_result_id, 0, msg); if (has_result && !msg.empty()) { if (msg == "phash") { with_phash = true; } else { IndexThread::removeResult(curr_result_id); if (msg != "done") { Server->Log("Indexing failed with \"" + msg + "\"", LL_ERROR); return false; } break; } } else { Server->wait(1000); } } log("Indexing finished", LL_INFO); logIndexResult(); if (!prepareBackuppath()) return false; std::string filelistpath = IndexThread::getFileSrv()->getShareDir(backupgroup > 0 ? ("urbackup/filelist_" + convert(backupgroup) + ".ub") : "urbackup/filelist.ub", "#I"+server_identity+"#"); if (filelistpath.empty()) return false; std::unique_ptr curr_file_list(Server->openFile(filelistpath, MODE_READ)); if (curr_file_list.get() == nullptr) { log("Error opening current file list. " + os_last_error_str(), LL_ERROR); return false; } std::vector diffs; bool backup_with_components; total_bytes = getIncrementalSize(curr_file_list.get(), diffs, backup_with_components, true); std::string curr_orig_path; std::string orig_sep; std::string curr_path; std::string curr_os_path; log("Copying files...", LL_INFO); updateProgressPc(0, total_bytes, 0); std::unique_ptr filelist_out(backup_files->openFile(getBackupInternalDataDir() + "\\filelist.ub", MODE_WRITE)); int depth = 0; FileListParser list_parser; char buffer[4096]; bool has_read_error = false; _u32 read; SFile cf; curr_file_list->Seek(0); while ((read = curr_file_list->Read(buffer, 4096, &has_read_error)) > 0) { if (has_read_error) { return false; } for (size_t i = 0; i < read; ++i) { std::map extra_params; bool b = list_parser.nextEntry(buffer[i], cf, &extra_params); if (b) { FileMetadata metadata; metadata.read(extra_params); bool has_orig_path = metadata.has_orig_path; if (has_orig_path) { curr_orig_path = metadata.orig_path; str_map::iterator it_orig_sep = extra_params.find("orig_sep"); if (it_orig_sep != extra_params.end()) { orig_sep = it_orig_sep->second; } if (orig_sep.empty()) orig_sep = "\\"; } int64 ctime = Server->getTimeMS(); if (ctime - laststatsupdate > status_update_intervall) { updateProgress(ctime); } if (cf.isdir) { if (cf.name != "..") { if (!curr_path.empty()) curr_path += "/"; curr_path += cf.name; if (!curr_os_path.empty()) curr_os_path += "\\"; curr_os_path += cf.name; if (!backup_files->createDir(curr_os_path)) { log("Error creating directory \"" + curr_os_path + "\"", LL_ERROR); return false; } if (!backup_files->createDir(".hashes\\" + curr_os_path)) { log("Error creating hash directory \".hashes\\" + curr_os_path + "\"", LL_ERROR); return false; } std::string sourcepath = IndexThread::getFileSrv()->getShareDir(curr_path, std::string()); std::string metadatapath = ".hashes\\" + curr_os_path + "\\"+ metadata_dir_fn; std::unique_ptr metadataf(backup_files->openFile(metadatapath, MODE_WRITE)); if (metadataf.get() == nullptr) { log("Error opening metadata file \"" + metadatapath + "\"", LL_ERROR); return false; } if (!backupMetadata(metadataf.get(), sourcepath, &metadata)) { log("Error backing up metadata of \"" + sourcepath + "\"", LL_ERROR); return false; } if (depth == 0) { referenceShadowcopy(cf.name, server_token, clientsubname); } ++depth; } else { curr_path = ExtractFilePath(curr_path, "/"); curr_os_path = ExtractFilePath(curr_os_path, "\\"); if (!has_orig_path) { curr_orig_path = ExtractFilePath(curr_orig_path, orig_sep); } --depth; if (depth == 0) { unreferenceShadowcopy(curr_path, server_token, clientsubname, 0); } } } else { if (depth == 0) { referenceShadowcopy(cf.name, server_token, clientsubname); } if (!has_orig_path) { if (curr_orig_path != orig_sep) { metadata.orig_path = curr_orig_path + orig_sep + cf.name; } else { metadata.orig_path = orig_sep + cf.name; } } std::string sourcepath = IndexThread::getFileSrv()->getShareDir(curr_path+"/"+cf.name, std::string()); std::string metadatapath = ".hashes\\" + curr_os_path + "\\" + cf.name; std::string targetpath = curr_os_path + "\\" + cf.name; if (metadatapath.find("btrfs.h") != std::string::npos) int abc = 5; std::unique_ptr metadataf(backup_files->openFile(metadatapath, MODE_WRITE)); if (metadataf.get() == nullptr) { log("Error opening metadata file at \"" + metadatapath + "\"", LL_ERROR); return false; } if (!backupMetadata(metadataf.get(), sourcepath, &metadata)) { log("Error backing up file metadata of \"" + sourcepath + "\"", LL_ERROR); return false; } std::unique_ptr sourcef(Server->openFile(sourcepath, MODE_READ_SEQUENTIAL_BACKUP)); if (sourcef.get() == nullptr) { log("Error opening \"" + sourcepath + "\" for backup. " + os_last_error_str(), LL_ERROR); return false; } if (sourcepath.find("btrfs.c") != std::string::npos) int abct = 5; std::unique_ptr destf(backup_files->openFile(targetpath, MODE_WRITE)); FsExtentIterator extent_iterator(sourcef.get()); bool b = build_chunk_hashs(sourcef.get(), metadataf.get(), nullptr, destf.get(), false, nullptr, nullptr, false, nullptr, &extent_iterator, std::pair(), this); done_bytes += cf.size; if (!b) { log("Error backing up \"" + sourcepath + "\"", LL_ERROR); return false; } if (depth == 0) { unreferenceShadowcopy(cf.name, server_token, clientsubname, 0); } } writeFileItem(filelist_out.get(), cf); } } } filelist_out.reset(); if (!backup_files->sync(std::string())) { log("Error syncing backup file system", LL_ERROR); return false; } if (!backup_files->renameToFinal()) { log("Error renaming backup subvol to final location", LL_ERROR); return false; } return sync(); } void LocalFullFileBackup::updateBchPc(int64 done, int64 total) { file_done_bytes = done; int64 ctime = Server->getTimeMS(); if (ctime - laststatsupdate > status_update_intervall) { updateProgress(ctime); } }