/************************************************************************* * 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 #include "../Interface/Server.h" #include "../Interface/ThreadPool.h" #include "../Interface/Thread.h" #include "../Interface/File.h" #include "../urbackupcommon/fileclient/tcpstack.h" #include "../common/data.h" #include "../stringtools.h" #include "../urbackupcommon/os_functions.h" #include "client_restore.h" #include "client_restore_http.h" #include #include #include #include #include #include "../urbackupcommon/fileclient/socket_header.h" #ifndef _WIN32 #include #include #else #include #endif #include "../urbackupcommon/mbrdata.h" #include "../fileservplugin/settings.h" #include "../fileservplugin/packet_ids.h" #include #include "../cryptoplugin/ICryptoFactory.h" #include "../fsimageplugin/IFSImageFactory.h" #ifndef _WIN32 #include "../config.h" #include #include #include #include #ifdef HAVE_LINUX_FS_H #include #endif #ifndef BLKRRPART #define BLKRRPART _IO(0x12,95) #endif #endif using namespace restore; #ifdef _WIN32 const std::string pw_file="pw.txt"; #else const std::string pw_file="urbackup/pw.txt"; #endif const std::string configure_wlan="wicd-curses"; const std::string configure_networkcard=configure_wlan; #define UDP_SOURCE_PORT 35623 extern ICryptoFactory *crypto_fak; extern IFSImageFactory* image_fak; namespace { bool rereadPartitionLayout(const std::string& devfn) { bool ret = true; #ifndef _WIN32 int fd = open(devfn.c_str(), O_NONBLOCK | O_RDWR); if (fd == -1) return false; int rc = ioctl(fd, BLKRRPART, NULL); if (rc == -1) { ret = false; Server->Log("Error re-reading partition layout. " + os_last_error_str(), LL_WARNING); } close(fd); #endif return ret; } } namespace restore { std::string trim2(const std::string& str) { size_t startpos = str.find_first_not_of(" \t\n"); size_t endpos = str.find_last_not_of(" \t\n"); if (std::string::npos == startpos || std::string::npos == endpos) { return ""; } else { return str.substr(startpos, endpos - startpos + 1); } } std::string getResponse(IPipe* c) { CTCPStack tcpstack; char* resp = nullptr; char buffer[1024]; size_t packetsize; while (resp == nullptr) { size_t rc = c->Read(buffer, 1024, 60000); if (rc == 0) { return ""; } tcpstack.AddData(buffer, rc); resp = tcpstack.getPacket(&packetsize); if (resp != nullptr && packetsize == 0) { delete[]resp; return ""; } } std::string ret; ret.resize(packetsize); memcpy(&ret[0], resp, packetsize); delete[]resp; return ret; } std::string restorePw() { return getFile(pw_file); } std::vector getBackupclients(int& ec) { std::string pw = getFile(pw_file); CTCPStack tcpstack; std::vector ret; ec = 0; IPipe* c = Server->ConnectStream("localhost", 35623, 60000); if (c == nullptr) { Server->Log("Error connecting to client service -1", LL_ERROR); ec = 10; return ret; } tcpstack.Send(c, "GET BACKUPCLIENTS#pw=" + pw); std::string r = getResponse(c); if (r.empty()) { Server->Log("No response from ClientConnector", LL_ERROR); ec = 1; } else { if (r[0] == '0') { Server->Log("No backupserver found", LL_ERROR); ec = 2; } else { std::vector toks; std::string t = r.substr(1); Tokenize(t, toks, "\n"); for (size_t i = 0; i < toks.size(); ++i) { std::string nam = trim2(getafter("|", toks[i])); if (!nam.empty()) { if (std::find(ret.begin(), ret.end(), nam) == ret.end()) { ret.push_back(nam); } } } } } Server->destroy(c); return ret; } IPipe* connectToService(int& ec) { IPipe* c = Server->ConnectStream("localhost", 35623, 60000); if (c == nullptr) { Server->Log("Error connecting to client service -1", LL_ERROR); ec = 10; return nullptr; } return c; } IPipe* connectToService() { int ec = 0; return connectToService(ec); } } struct SPasswordSalt { std::string salt; std::string rnd; int pbkdf2_rounds; }; std::vector getSalt(const std::string& username, int tries, int *ec) { std::string pw=getFile(pw_file); CTCPStack tcpstack; *ec=0; std::vector ret; IPipe *c=connectToService(*ec); if(c==nullptr) { return ret; } bool has_ok = false; tcpstack.Send(c, "GET SALT#pw="+pw+"&username="+username); std::string r=getResponse(c); if(r.empty() ) { Server->Log("No response from ClientConnector", LL_ERROR); *ec=1; } else { Server->Log("Salt Response: "+r, LL_INFO); std::vector toks; Tokenize(r, toks, "/"); for(size_t i=0;i salt_toks; Tokenize(toks[i], salt_toks, ";"); SPasswordSalt new_salts; new_salts.pbkdf2_rounds=0; if(salt_toks.size()==3) { new_salts.salt = salt_toks[1]; new_salts.rnd = salt_toks[2]; } else if(salt_toks.size()==4) { new_salts.salt = salt_toks[1]; new_salts.rnd = salt_toks[2]; new_salts.pbkdf2_rounds = atoi(salt_toks[3].c_str()); } ret.push_back(new_salts); } else { Server->Log("SALT error: "+toks[i], LL_ERROR); Server->Log("Username not found on server", LL_ERROR); SPasswordSalt new_salts = {}; ret.push_back(new_salts); tries = 0; } } } Server->destroy(c); if (!has_ok && tries>0) { Server->wait(10000); return getSalt(username, tries - 1, ec); } return ret; } bool tryLogin(const std::string& username, const std::string& password, std::vector salts, int tries, int *ec) { std::string pw=getFile(pw_file); CTCPStack tcpstack; *ec=0; IPipe *c=connectToService(*ec); if(c==nullptr) { return false; } std::string auth_str; if(!username.empty()) { auth_str="&username="+username; for(size_t i=0;iGenerateHexMD5(salts[i].salt+password); if(salts[i].pbkdf2_rounds>0) { pw_md5 = strlower(crypto_fak->generatePasswordHash(hexToBytes(pw_md5), salts[i].salt, salts[i].pbkdf2_rounds)); } auth_str+="&password"+convert(i)+"="+ Server->GenerateHexMD5(salts[i].rnd+pw_md5); } } } bool do_retry = false; bool ret=false; tcpstack.Send(c, "LOGIN FOR DOWNLOAD#pw="+pw+auth_str); std::string r=getResponse(c); if(r.empty() ) { Server->Log("No response from ClientConnector", LL_ERROR); *ec=1; } else { Server->Log("Login Response: "+r, LL_INFO); if(r=="ok") { ret=true; } else if (r == "no channels available") { ret = true; do_retry = true; } else { if (r == "err") { tries = 0; } Server->Log("Error during login: "+r, LL_ERROR); } } Server->destroy(c); if ((!ret || do_retry) && tries>0) { Server->wait(10000); return tryLogin(username, password, salts, tries - 1, ec); } return ret; } std::vector getBackupimages(std::string clientname, int *ec) { std::string pw=getFile(pw_file); CTCPStack tcpstack; std::vector ret; *ec=0; IPipe *c=Server->ConnectStream("localhost", 35623, 60000); if(c==nullptr) { Server->Log("Error connecting to client service -1", LL_ERROR); *ec=10; return ret; } tcpstack.Send(c, "GET BACKUPIMAGES "+clientname+"#pw="+pw); std::string r=getResponse(c); if(r.empty() ) { Server->Log("No response from ClientConnector", LL_ERROR); *ec=1; } else { if(r[0]=='0') { Server->Log("No backupserver found", LL_ERROR); *ec=1; } else { ret = parse_backup_images_output(r.substr(1)); } } Server->destroy(c); return ret; } struct SFileBackup { bool operator<(const SFileBackup &other) const { return other.time_s getFileBackups(std::string clientname, int *ec) { std::string pw=getFile(pw_file); CTCPStack tcpstack; std::vector ret; *ec=0; IPipe *c=Server->ConnectStream("localhost", 35623, 60000); if(c==nullptr) { Server->Log("Error connecting to client service -1", LL_ERROR); *ec=10; return ret; } tcpstack.Send(c, "GET FILE BACKUPS "+clientname+"#pw="+pw); std::string r=getResponse(c); if(r.empty() ) { Server->Log("No response from ClientConnector", LL_ERROR); *ec=1; } else { if(r[0]=='0') { Server->Log("No backupserver found", LL_ERROR); *ec=1; } else { std::vector toks; std::string t=r.substr(1); Tokenize(t, toks, "\n"); for(size_t i=0;i t2; Tokenize(toks[i], t2, "|"); if(t2.size()==4 ) { SFileBackup si; si.id=atoi(t2[0].c_str()); si.time_s=os_atoi64(t2[1]); si.time_str=t2[2]; si.tgroup=atoi(t2[3].c_str()); ret.push_back(si); } } } } Server->destroy(c); return ret; } volatile bool restore_retry_ok=false; namespace restore { EDownloadResult retryDownload(EDownloadResult errrc, int img_id, std::string img_time, std::string outfile, bool mbr, LoginData login_data, DownloadStatus& dl_status, int recur_depth, int64* o_imgsize, int64* o_output_file_size) { if (recur_depth == 0) { Server->Log("Read Timeout: Retrying -1", LL_WARNING); int total_tries = 20; int tries = total_tries; EDownloadResult rc; do { Server->wait(30000); int64 starttime = Server->getTimeMS(); rc = downloadImage(img_id, img_time, outfile, mbr, login_data, dl_status, recur_depth + 1, o_imgsize, o_output_file_size); if (rc == EDownloadResult_Ok) { return rc; } --tries; if (Server->getTimeMS() - starttime > 180000) { tries = total_tries; } if (rc == EDownloadResult_SizeReadError) { do_login(login_data); Server->wait(10000); } } while (tries > 0); } Server->Log("Read Timeout.", LL_ERROR); return errrc; } EDownloadResult downloadImage(int img_id, std::string img_time, std::string outfile, bool mbr, LoginData login_data, DownloadStatus& dl_status, int recur_depth, int64* o_imgsize, int64* o_output_file_size) { std::string pw = getFile(pw_file); CTCPStack tcpstack; std::vector ret; std::unique_ptr client_pipe(Server->ConnectStream("localhost", 35623, 60000)); if (client_pipe.get() == nullptr) { Server->Log("Error connecting to client service -1", LL_ERROR); return EDownloadResult_ConnectError; } std::string s_offset; if (dl_status.offset != -1) { s_offset = "&offset=" + convert(dl_status.offset) + "&received_bytes=" + convert(dl_status.received); } std::string dl_args; if (img_id != 0 || !img_time.empty()) { dl_args = "&img_id=" + convert(img_id) + "&time=" + img_time; } else if (login_data.has_login_data && !login_data.token.empty()) { dl_args = "&token=" + EscapeParamString(login_data.token); } tcpstack.Send(client_pipe.get(), "DOWNLOAD IMAGE#pw=" + pw + dl_args + "&mbr=" + convert(mbr) + s_offset); std::string restore_out = outfile; Server->Log("Restoring to " + restore_out); std::unique_ptr out_file(Server->openFile(restore_out, MODE_RW_READNONE)); if (out_file.get() == nullptr) { Server->Log("Could not open \"" + restore_out + "\" for writing. "+os_last_error_str(), LL_ERROR); return EDownloadResult_OpenError; } else if (o_output_file_size != nullptr) { *o_output_file_size = out_file->Size(); } _i64 imgsize = -1; client_pipe->Read((char*)&imgsize, sizeof(_i64), 60000); if (o_imgsize != nullptr) { *o_imgsize = imgsize; } if (imgsize == -1) { Server->Log("Error reading size", LL_ERROR); return EDownloadResult_SizeReadError; } if (imgsize == -2) { Server->Log("Connection timeout", LL_ERROR); EDownloadResult rc = retryDownload(EDownloadResult_TimeoutError1, img_id, img_time, outfile, mbr, login_data, dl_status, recur_depth, o_imgsize, o_output_file_size); return rc; } if (!mbr && imgsize > out_file->Size()) { Server->Log("Output device too small. File size = " + convert(out_file->Size()) + " needed = " + convert(imgsize), LL_ERROR); return EDownloadResult_DeviceTooSmall; } const size_t c_buffer_size = 32768; const unsigned int c_block_size = 4096; char buf[c_buffer_size]; if (mbr == true) { _i64 read = 0; while (read < imgsize) { size_t c_read = client_pipe->Read(buf, c_buffer_size, 180000); if (c_read == 0) { EDownloadResult rc = retryDownload(EDownloadResult_TimeoutError2, img_id, img_time, outfile, mbr, login_data, dl_status, recur_depth, o_imgsize, o_output_file_size); return rc; } out_file->Write(buf, (_u32)c_read); read += c_read; } return EDownloadResult_Ok; } else { _i64 read = 0; unsigned int blockleft = 0; unsigned int off = 0; char blockdata[c_block_size]; bool first = true; bool has_data = false; _i64 pos = 0; while (pos < imgsize) { size_t r = client_pipe->Read(&buf[off], c_buffer_size - off, 180000); if (r != 0) r += off; off = 0; if (r == 0) { Server->Log("Read Timeout: Retrying", LL_WARNING); client_pipe.reset(nullptr); out_file.reset(nullptr); if (has_data) { return retryDownload(EDownloadResult_TimeoutError2, img_id, img_time, outfile, mbr, login_data, dl_status, recur_depth, o_imgsize, o_output_file_size); } else { Server->Log("Read Timeout: No data", LL_ERROR); return EDownloadResult_TimeoutError2; } } while (true) { if (blockleft == 0) { if (!first) { //Only write one block _u32 tw = c_block_size; //Don't write over blockdev boundary if (imgsize >= pos && imgsize - pos < c_block_size) tw = (_u32)(imgsize - pos); //Write to blockdev _u32 woff = 0; do { bool has_write_error = false; _u32 w = out_file->Write(&blockdata[woff], tw - woff, &has_write_error); if (w == 0) { Server->Log("Writing to output file failed", LL_ERROR); return EDownloadResult_WriteFailed; } if (has_write_error) { Server->Log("Writing to output file failed -2", LL_ERROR); return EDownloadResult_WriteFailed; } woff += w; } while (tw - woff > 0); if (pos > dl_status.offset) { dl_status.offset = pos; } has_data = true; } else { first = false; if (pos != -1 && !restore_retry_ok) { restore_retry_ok = true; } } if (r - off >= sizeof(_i64)) { blockleft = c_block_size; _i64* s = reinterpret_cast<_i64*>(&buf[off]); if (*s == 0x7fffffffffffffffLL) { Server->Log("Restore finished", LL_INFO); pos = *s; break; } if (*s > imgsize) { Server->Log("Invalid seek value: " + convert(*s), LL_ERROR); //TODO return error code here after deprecation period pos = *s; break; } else if (*s < pos) { Server->Log("Position out of order!", LL_ERROR); } else { if (!out_file->Seek(*s)) { Server->Log("Seeking in output file failed (to position " + convert(*s) + ")", LL_ERROR); return EDownloadResult_WriteFailed; } pos = *s; } off += sizeof(_i64); } else if (r - off > 0) { memmove(buf, &buf[off], r - off); off = (_u32)r - off; break; } else { off = 0; break; } } else { unsigned int available = (std::min)((unsigned int)r - off, blockleft); if (available > 0) { memcpy(&blockdata[c_block_size - blockleft], &buf[off], (_u32)available); } read += available; blockleft -= available; off += available; dl_status.received += available; if (off >= r) { off = 0; break; } } } } return EDownloadResult_Ok; } return EDownloadResult_Ok; } int downloadFiles(int backupid, std::string backup_time) { std::string pw=getFile(pw_file); CTCPStack tcpstack; std::vector ret; std::unique_ptr client_pipe(Server->ConnectStream("localhost", 35623, 60000)); if(client_pipe.get()==nullptr) { Server->Log("Error connecting to client service -1", LL_ERROR); return 10; } tcpstack.Send(client_pipe.get(), "DOWNLOAD FILES#pw="+pw+"&backupid="+convert(backupid)+"&time="+backup_time); int64 starttime = Server->getTimeMS(); while(Server->getTimeMS()-starttime<60000) { std::string msg; if(client_pipe->Read(&msg, 60000)>0) { tcpstack.AddData(&msg[0], msg.size()); if(tcpstack.getPacket(msg) ) { if(msg=="ok") { return 0; } else { Server->Log("Error: "+msg, LL_ERROR); return 1; } break; } } else { break; } } Server->Log("Timeout", LL_ERROR); return 2; } } //namespace restore namespace restore { bool LookupBlocking(std::string pServer, in_addr *dest) { const char* host=pServer.c_str(); unsigned int addr = inet_addr(host); if (addr != INADDR_NONE) { dest->s_addr = addr; } else { addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_protocol = IPPROTO_TCP; hints.ai_socktype = SOCK_STREAM; addrinfo* hp; if(getaddrinfo(host, nullptr, &hints, &hp)==0 && hp!=nullptr) { if(hp->ai_addrlen<=sizeof(sockaddr_in)) { memcpy(dest, &reinterpret_cast(hp->ai_addr)->sin_addr, sizeof(in_addr)); freeaddrinfo(hp); return true; } else { freeaddrinfo(hp); return false; } } else { return false; } } return true; } bool ping_server(void) { int type = SOCK_DGRAM; #if !defined(_WIN32) && defined(SOCK_CLOEXEC) type |= SOCK_CLOEXEC; #endif SOCKET udpsock=socket(AF_INET, type,0); #if !defined(_WIN32) && !defined(SOCK_CLOEXEC) fcntl(udpsock, F_SETFD, fcntl(udpsock, F_GETFD, 0) | FD_CLOEXEC); #endif std::string server=Server->getServerParameter("ping_server"); if(server.empty()) return false; sockaddr_in server_addr; if(!LookupBlocking(server, &server_addr.sin_addr)) return false; server_addr.sin_family=AF_INET; server_addr.sin_port=htons(UDP_SOURCE_PORT); std::string ping_clientname=getFile("clientname.txt"); std::vector buffer; buffer.resize(ping_clientname.size()+2); buffer[0]=ID_PONG; buffer[1]=FILESERV_VERSION; memcpy(&buffer[2], ping_clientname.c_str(), ping_clientname.size()); while(true) { int rc = sendto(udpsock, &buffer[0], static_cast(buffer.size()), 0, (sockaddr*)&server_addr, sizeof(server_addr)); if(rc == -1) { Server->Log("Error sending to UrBackup server on UDP socket", LL_ERROR); return false; } Server->wait(10000); } return true; } std::vector lsblk(const std::string& dev) { int rc = system(("lsblk -x MAJ:MIN -o MAJ:MIN,MODEL,SIZE,TYPE,PATH -P "+dev+" 1> out").c_str()); std::vector ret; if(rc!=0) { Server->Log("Error while running 'lsblk'", LL_ERROR); return ret; } std::vector lines; Tokenize(getFile("out"), lines, "\n"); for(size_t i=0;i parts = lsblk(dev); std::map parts_maj_min; for(auto& it: parts) { if(it.type=="part") { int maj = watoi(getuntil(":", it.maj_min)); int min = watoi(getafter(":", it.maj_min)); parts_maj_min[maj*100000ULL + min] = it; } } int idx=1; for(auto it: parts_maj_min) { if(idx==partnum) { return it.second.path; } ++idx; } return std::string(); } } namespace restore { std::vector parse_backup_images_output(const std::string& data) { std::vector ret; std::vector toks; Tokenize(data, toks, "\n"); for (size_t i = 0; i < toks.size(); ++i) { std::vector t2; Tokenize(toks[i], t2, "|"); if (t2.size() == 3 || (t2.size() == 4 && t2[0] != "#")) { SImage si; si.id = atoi(t2[0].c_str()); si.time_s = os_atoi64(t2[1]); si.time_str = t2[2]; if (t2.size() == 4) { si.letter = t2[3]; } ret.push_back(si); } else if ( (t2.size() == 4 || t2.size() == 5) && t2[0] == "#" && !ret.empty()) { SImage si; si.id = atoi(t2[1].c_str()); si.time_s = os_atoi64(t2[2]); si.time_str = t2[3]; if (t2.size() == 5) { si.letter = t2[4]; } ret[ret.size() - 1].assoc.push_back(si); } } return ret; } JSON::Array backup_images_output_to_json(const std::string& data) { std::vector images = parse_backup_images_output(data); JSON::Array ret; for (size_t i = 0; i < images.size(); ++i) { JSON::Object image; image.set("id", images[i].id); image.set("time_s", images[i].time_s); image.set("time_str", images[i].time_str); image.set("letter", images[i].letter); if (!images[i].assoc.empty()) { JSON::Array assoc; for (size_t j = 0; j < images[i].assoc.size(); ++j) { JSON::Object image_assoc; image_assoc.set("id", images[i].assoc[j].id); image_assoc.set("time_s", images[i].assoc[j].time_s); image_assoc.set("time_str", images[i].assoc[j].time_str); image_assoc.set("letter", images[i].assoc[j].letter); assoc.add(image_assoc); } image.set("assoc", assoc); } ret.add(image); } return ret; } std::string backup_images_output_to_json_str(const std::string& data) { return backup_images_output_to_json(data).stringify(false); } struct SgdiskPart { size_t number; int64 start; int64 end; std::string size; std::string code; std::string type_name; std::string part_guid; std::string unique_guid; std::string name; std::string flags; }; std::vector getSgdiskParts(const std::string& dev) { std::vector ret; std::string out; int rc = os_popen("sgdisk -p \""+dev+"\" 2>&1", out); if(rc!=0) return ret; if(out.find("Number")==std::string::npos) return ret; std::string table = getafter("\n", getafter("Number", out)); std::vector lines; Tokenize(table, lines, "\n"); for(auto& line: lines) { std::vector cols; Tokenize(line, cols, " "); SgdiskPart new_part; new_part.number = 0; new_part.start = -1; new_part.end = -1; size_t idx=0; for(auto& col: cols) { std::string cd = trim(col); if(!cd.empty()) { if(idx==0) new_part.number = watoi(cd); else if(idx==1) new_part.start = watoi64(cd); else if(idx==2) new_part.end = watoi64(cd); else if(idx==3) new_part.size = cd; else if(idx==4) new_part.size += " "+ cd; else if(idx==5) new_part.code = cd; else new_part.type_name += cd; ++idx; } } if(new_part.start!=-1 && new_part.number!=0 && new_part.end!=-1) { std::string iout; rc = os_popen("sgdisk -i "+std::to_string(new_part.number)+" \""+dev+"\" 2>&1", iout); if(rc==0) { new_part.part_guid=trim(getbetween("Partition GUID code: ", " ", iout)); if(new_part.part_guid.empty()) new_part.part_guid=trim(getbetween("Partition GUID code: ", "\n", iout)); new_part.unique_guid = trim(getbetween("Partition unique GUID: ", "\n", iout)); new_part.flags = trim(getbetween("Attribute flags: ", "\n", iout)); new_part.name = trim(getbetween("Partition name: '", "\n", iout)); if(!new_part.name.empty() && new_part.name[new_part.name.size()-1]=='\'') new_part.name = new_part.name.substr(0, new_part.name.size()-1); } else { Server->Log("Getting detailed info for partition "+std::to_string(new_part.number)+" failed: "+iout, LL_ERROR); } ret.push_back(new_part); } } return ret; } std::string createSgdiskPart(const SgdiskPart& lp, const std::string& out_device) { std::string out; int rc = os_popen("sgdisk -n "+std::to_string(lp.number)+":"+std::to_string(lp.start)+":0 " "-A "+std::to_string(lp.number)+":=:"+lp.flags+" " "-c "+std::to_string(lp.number)+":\""+lp.name+"\" " "-t "+std::to_string(lp.number)+":"+lp.part_guid+" " "-u "+std::to_string(lp.number)+":"+lp.unique_guid+" " +out_device, out); if(rc!=0) { return "Error creating partition "+std::to_string(lp.number)+": "+trim(out); } return std::string(); } int64 getSgdiskDiskSize(const std::string& dev) { std::string out; int rc = os_popen("sgdisk -p \""+dev+"\" 2>&1", out); if(rc!=0) return -1; std::string s = trim(getbetween("Disk "+dev+": ", " sectors", out)); if(s.empty()) return -1; return watoi64(s); } bool do_restore_write_mbr(const std::string& mbr_filename, const std::string& out_device, bool fix_gpt, std::string& errmsg) { IFile *f=Server->openFile(mbr_filename, MODE_READ); if(f==nullptr) { errmsg = "Could not open MBR file"; return false; } size_t fsize=(size_t)f->Size(); char *buf=new char[fsize]; f->Read(buf, (_u32)fsize); Server->destroy(f); CRData mbr(buf, fsize); SMBRData mbrdata(mbr); if(mbrdata.hasError()) { errmsg = "Error while parsing MBR data"; delete []buf; return false; } IFile *dev=Server->openFile(out_device, MODE_RW); if(dev==nullptr) { errmsg = "Could not open device file for writing"; delete []buf; return false; } dev->Seek(0); Server->Log("Writing MBR data...", LL_INFO); if (dev->Write(mbrdata.mbr_data) != mbrdata.mbr_data.size()) { errmsg = "Writing MBR data failed. " + os_last_error_str(); Server->Log(errmsg, LL_ERROR); } else { Server->Log("done.", LL_INFO); } bool curr_fix_gpt=false; if (mbrdata.gpt_style) { Server->Log("Writing GPT header..."); if (dev->Write(mbrdata.gpt_header_pos, mbrdata.gpt_header) != mbrdata.gpt_header.size() ) { errmsg = "Writing GPT header failed. " + os_last_error_str(); Server->Log(errmsg, LL_ERROR); } Server->Log("Writing GPT table..."); if (dev->Write(mbrdata.gpt_table_pos, mbrdata.gpt_table) != mbrdata.gpt_table.size()) { errmsg = "Writing GPT table failed. " + os_last_error_str(); Server->Log(errmsg, LL_ERROR); } if (mbrdata.backup_gpt_header_pos != -1) { Server->Log("Writing GPT backup header..."); if (dev->Write(mbrdata.backup_gpt_header_pos, mbrdata.backup_gpt_header) != mbrdata.backup_gpt_header.size()) { errmsg = "Writing GPT backup header failed. " + os_last_error_str(); Server->Log(errmsg, LL_ERROR); curr_fix_gpt = true; } } if (mbrdata.backup_gpt_table_pos != -1) { Server->Log("Writing GPT backup table..."); if (dev->Write(mbrdata.backup_gpt_table_pos, mbrdata.backup_gpt_table) != mbrdata.backup_gpt_table.size()) { errmsg = "Writing backup GPT table failed. " + os_last_error_str(); Server->Log(errmsg, LL_ERROR); curr_fix_gpt = true; } } } if (mbrdata.extra_data_pos != -1) { Server->Log("Writing extra data at position "+convert(mbrdata.extra_data_pos)+" size "+convert(mbrdata.extra_data.size())+" ..."); if (dev->Write(mbrdata.extra_data_pos, mbrdata.extra_data) != mbrdata.extra_data.size()) { errmsg = "Writing extra data failed. " + os_last_error_str(); Server->Log(errmsg, LL_ERROR); } } if(curr_fix_gpt && fix_gpt) { std::string fix_output; int rc = os_popen("sgdisk -e " + out_device + " 2>&1", fix_output); errmsg="Fixed GPT backup table: "+trim(fix_output); if(fix_output.find("too big for the disk")!=std::string::npos) { std::vector partitions = getSgdiskParts(out_device); int64 sgdisk_size = getSgdiskDiskSize(out_device); while(!partitions.empty()) { if(sgdisk_size<0) break; SgdiskPart lp = partitions[partitions.size()-1]; if(lp.start>=sgdisk_size) { Server->Log("Partition "+std::to_string(lp.number)+" not on disk. Removing..."); std::string out; rc = os_popen("sgdisk -d "+std::to_string(lp.number)+" "+out_device+" 2>&1", out); if(rc!=0) { errmsg="Error deleting last partition (1): "+trim(out); Server->Log(errmsg, LL_ERROR); } } else { break; } } if(!partitions.empty()) { Server->Log("Partition too big. Resizing last partition", LL_WARNING); SgdiskPart lp = partitions[partitions.size()-1]; Server->Log("Last partition num="+std::to_string(lp.number)+" start="+std::to_string(lp.start)+" end="+std::to_string(lp.end) +" size="+lp.size, LL_INFO); std::string out; rc = os_popen("sgdisk -d "+std::to_string(lp.number)+" "+out_device+" 2>&1", out); if(rc!=0) { errmsg="Error deleting last partition: "+trim(out); Server->Log(errmsg, LL_ERROR); } std::string err = createSgdiskPart(lp, out_device); if(!err.empty()) { errmsg="Error re-creating last partition: "+trim(err); Server->Log(errmsg, LL_ERROR); } } else { Server->Log("No partitions found", LL_ERROR); } } } Server->destroy(dev); if (out_device.find("/dev/") == 0) { rereadPartitionLayout(out_device); } delete []buf; return true; } } void do_restore(void) { std::string cmd=Server->getServerParameter("restore_cmd"); if(cmd=="write_mbr") { std::string mbr_filename=Server->getServerParameter("mbr_filename"); std::string out_device=Server->getServerParameter("out_device"); if(mbr_filename.empty()) { Server->Log("MBR filename not specified (mbr_filename parameter)", LL_ERROR); exit(1); } if(out_device.empty()) { Server->Log("Output device not specified (out_device paramter)", LL_ERROR); exit(1); } std::string errmsg; if(!do_restore_write_mbr(mbr_filename, out_device, false, errmsg)) { Server->Log(errmsg, LL_ERROR); exit(2); } exit(0); } else if(cmd=="mbrinfo") { std::string mbr_filename=Server->getServerParameter("mbr_filename"); if(mbr_filename.empty()) { Server->Log("MBR filename not specified (mbr_filename parameter)", LL_ERROR); exit(1); } IFile *f=Server->openFile(mbr_filename, MODE_READ); if(f==nullptr) { Server->Log("Could not open MBR file", LL_ERROR); exit(1); } size_t fsize=(size_t)f->Size(); char *buf=new char[fsize]; f->Read(buf, (_u32)fsize); Server->destroy(f); CRData mbr(buf, fsize); SMBRData mbrdata(mbr); if(mbrdata.hasError()) { Server->Log("Error while parsing MBR data", LL_ERROR); delete []buf; exit(1); } std::cout << mbrdata.infoString(); delete []buf; exit(0); } else if (cmd == "login") { LoginData login_data; login_data.has_login_data = true; login_data.username = Server->getServerParameter("username"); login_data.password = Server->getServerParameter("password"); char* pw = getenv("LOGIN_PASSWORD"); if (pw != nullptr) { login_data.password = pw; } if (do_login(login_data)) { exit(0); } else { exit(1); } } else if (cmd == "read_mbr") { std::string dev_fn = Server->getServerParameter("device_fn"); std::unique_ptr dev(Server->openFile(dev_fn, MODE_READ_DEVICE)); if (dev.get() == nullptr) { Server->Log("Error opening device at " + dev_fn+". "+os_last_error_str(), LL_ERROR); exit(5); } bool gpt_style; std::vector partitions = image_fak->readPartitions(dev.get(), gpt_style); if (partitions.empty()) { Server->Log("No partitions found", LL_ERROR); exit(4); } for (size_t i = 0; i < partitions.size(); ++i) { std::cout << "partition " << (i+1) << " off " << partitions[i].offset << " size " << partitions[i].length << std::endl; } exit(0); } else if(cmd=="resize_ntfs") { std::string dev_fn = Server->getServerParameter("device_fn"); std::unique_ptr dev(Server->openFile(dev_fn, MODE_READ_DEVICE)); if (dev.get() == nullptr) { Server->Log("Error opening device at " + dev_fn+". "+os_last_error_str(), LL_ERROR); exit(5); } dev.reset(); std::string s_new_size = Server->getServerParameter("new_size"); if(s_new_size.empty()) { Server->Log("new_size paramater missing", LL_ERROR); exit(6); } int64 new_size = watoi64(s_new_size); std::string err; std::atomic complete_pc; std::atomic done(false); std::thread t( [&err, dev_fn, new_size, &complete_pc, &done]() { err = restore::resize_ntfs(new_size, dev_fn, complete_pc); done=true; }); t.detach(); int last_pc = complete_pc; while(!done) { Server->wait(1000); if(complete_pc!=last_pc) { last_pc = complete_pc; Server->Log("Resize "+std::to_string(last_pc)+"% complete", LL_INFO); } } if(!err.empty()) { Server->Log("Resize error: "+err, LL_ERROR); exit(1); } exit(0); } else if(cmd=="get_sgparts") { std::string dev_fn = Server->getServerParameter("device_fn"); Server->Log("Device size: "+PrettyPrintBytes(getSgdiskDiskSize(dev_fn)*512), LL_INFO); std::vector parts = getSgdiskParts(dev_fn); for(const SgdiskPart& part: parts) { Server->Log("part number="+std::to_string(part.number) +" start="+std::to_string(part.start)+" end="+std::to_string(part.end) +" size="+part.size+" part_uuid="+part.part_guid+" unique_uuid="+part.unique_guid +" name=\""+part.name+"\" flags="+part.flags, LL_INFO); } exit(0); } else if(cmd=="help") { Server->Log("restore_cmd commands are...", LL_INFO); Server->Log("write_mbr(mbr_filename,out_device)", LL_INFO); Server->Log("get_clientnames", LL_INFO); Server->Log("get_backupimages(restore_name)", LL_INFO); Server->Log("get_backupimages_json(restore_name)", LL_INFO); Server->Log("get_file_backups(restore_name)", LL_INFO); Server->Log("download_mbr(restore_img_id,restore_time,restore_out)", LL_INFO); Server->Log("download_image(restore_img_id,restore_time,restore_out)", LL_INFO); Server->Log("download_files(restore_backupid,restore_time,restore_out)", LL_INFO); Server->Log("download_progress(mbr_filename,out_device)", LL_INFO); Server->Log("login(username,password)", LL_INFO); Server->Log("read_mbr(device_fn)", LL_INFO); Server->Log("resize_ntfs(device_fn,new_size)", LL_INFO); exit(0); } else if(cmd=="ping_server") { bool b=ping_server(); exit(b?0:1); } IPipe *c=Server->ConnectStream("localhost", 35623, 60000); if(c==nullptr) { Server->Log("Error connecting to client service -1", LL_ERROR); exit(1);return; } std::string pw=getFile(pw_file); CTCPStack tcpstack; if(cmd=="get_clientnames") { tcpstack.Send(c, "GET BACKUPCLIENTS#pw="+pw); std::string r=getResponse(c); if(r.empty() ) { Server->Log("No response from ClientConnector", LL_ERROR); Server->destroy(c);exit(2);return; } else { if(r[0]=='0') { Server->Log("No backupserver found", LL_ERROR); Server->destroy(c);exit(3);return; } else { std::cout << r.substr(1) ; Server->destroy(c);exit(0);return; } } } else if(cmd=="get_backupimages" || cmd == "get_backupimages_json") { tcpstack.Send(c, "GET BACKUPIMAGES "+Server->getServerParameter("restore_name")+"#pw="+pw); std::string r=getResponse(c); if(r.empty() ) { Server->Log("No response from ClientConnector", LL_ERROR); Server->destroy(c);exit(2);return; } else { if(r[0]=='0') { Server->Log("No backupserver found", LL_ERROR); Server->destroy(c);exit(3);return; } else { if (cmd == "get_backupimages_json") { std::cout << backup_images_output_to_json_str(r.substr(1)); } else { std::cout << r.substr(1); } Server->destroy(c);exit(0);return; } } } else if(cmd=="get_file_backups" ) { tcpstack.Send(c, "GET FILE BACKUPS "+Server->getServerParameter("restore_name")+"#pw="+pw); std::string r=getResponse(c); if(r.empty() ) { Server->Log("No response from ClientConnector", LL_ERROR); Server->destroy(c);exit(2);return; } else { if(r[0]=='0') { Server->Log("No backupserver found", LL_ERROR); Server->destroy(c);exit(3);return; } else { std::cout << r.substr(1) ; Server->destroy(c);exit(0);return; } } } else if(cmd=="download_mbr" || cmd=="download_image" ) { bool mbr=false; if(cmd=="download_mbr") mbr=true; LoginData login_data; login_data.has_login_data = false; std::string restore_token = Server->getServerParameter("restore_token"); if (!restore_token.empty()) { login_data.has_login_data = true; login_data.token = restore_token; } DownloadStatus dl_status; int ec=downloadImage(atoi(Server->getServerParameter("restore_img_id").c_str()), Server->getServerParameter("restore_time"), Server->getServerParameter("restore_out"), mbr, login_data, dl_status); exit(ec); } else if(cmd=="download_files") { int ec=downloadFiles(atoi(Server->getServerParameter("restore_backupid").c_str()), Server->getServerParameter("restore_time")); exit(ec); } else if(cmd=="download_progress") { std::string decorate = Server->getServerParameter("decorate"); tcpstack.Send(c, "GET DOWNLOADPROGRESS#pw="+pw); int lpc=0; while(true) { std::string curr; size_t r=c->Read(&curr, 10000); for(int i=0;iifr_addr)->sin_addr.s_addr) { close(sck); return true; } } close(sck); return false; #endif //sun #endif //_WIN32 } } void ping_named_server(void) { std::string out; while(out.empty()) { int rc=system("dialog --inputbox \"`cat urbackup/restore/enter_server_ip_input`\" 8 30 2> out"); out=getFile("out"); if(rc!=0) { return; } } if(!out.empty()) { system(("./urbackuprestoreclient --ping-server \""+out+"\" &").c_str()); } } namespace restore { bool has_internet_connection(int& ec, std::string& errstatus) { ec = 0; IPipe* c = connectToService(ec); if (c == nullptr) { return false; } std::string pw = getFile(pw_file); CTCPStack tcpstack; tcpstack.Send(c, "STATUS DETAIL#pw=" + pw); std::string r = getResponse(c); if (r.empty()) { Server->Log("No response from ClientConnector", LL_ERROR); ec = 1; return false; } std::string internet_connected = getbetween("\"internet_connected\": ", ",", r); std::string internet_status = getbetween("\"internet_status\": \"", "\",", r); if (internet_connected == "true" && r.find("\"servers\": []") == std::string::npos) { return true; } errstatus = internet_status; ec = 2; return false; } } namespace { bool internet_server_configured = false; } namespace restore { void configure_internet_server() { std::string internet_server; while (internet_server.empty()) { int rc = system("dialog --inputbox \"`cat urbackup/restore/enter_internet_server_name`\" 8 30 2> out"); internet_server = trim(getFile("out")); if (rc != 0) { system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70"); return; } } if (internet_server.empty()) { system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70"); return; } std::string internet_authkey; while (internet_authkey.empty()) { int rc = system("dialog --inputbox \"`cat urbackup/restore/enter_internet_server_authkey`\" 8 30 2> out"); internet_authkey = getFile("out"); if (rc != 0) { system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70"); return; } } if (internet_authkey.empty()) { system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70"); return; } system("clear"); std::cout << "Configuring UrBackup restore client as Internet client..." << std::endl; configure_internet_server(internet_server, internet_authkey, "", true); } void configure_internet_server(std::string server_url, const std::string& server_authkey, const std::string& server_proxy, bool with_cli) { system("systemctl stop restore-client"); std::string internet_server_port = "55415"; if (server_url.find(":") != std::string::npos) { internet_server_port = getafter(":", server_url); server_url = getuntil(":", server_url); } std::string rnd; rnd.resize(16); Server->secureRandomFill(&rnd[0], 16); std::string clientname = "##restore##" + bytesToHex(rnd); os_create_dir_recursive("urbackup/data_1"); writestring("internet_mode_enabled=true\n" "internet_server=" + server_url + "\n" "internet_server_port=" + internet_server_port + "\n" "internet_authkey=" + server_authkey + "\n" "computername=" + clientname + "\n", "urbackup/data_1/settings.cfg"); if (with_cli) std::cout << "Starting UrBackup Internet restore client..." << std::endl; if (system("systemctl restart restore-client-internet") != 0) { std::cout << "Error starting Internet restore client." << std::endl; char ch; std::cin >> ch; return; } if (!with_cli) return; std::cout << "Waiting for Internet restore client to connect..." << std::endl; Server->wait(1000); int64 starttime = Server->getTimeMS(); int ec; std::string errstatus; while (Server->getTimeMS() - starttime < 60000) { if (has_internet_connection(ec, errstatus)) { internet_server_configured = true; return; } if (ec == 2 && next(errstatus, 0, "error:")) break; Server->wait(1000); } std::cout << "Connecting to Internet server failed: " << errstatus << std::endl; std::cout << "Restarting local UrBackup client... " << errstatus << std::endl; system("systemctl stop restore-client-internet"); Server->deleteFile("urbackup/data_1/settings.cfg"); system("systemctl start restore-client"); std::string errmsg; switch (ec) { case 10: case 1: errmsg = "`cat urbackup/restore/internal_error`"; break; case 2: errmsg = getFile("urbackup/restore/internet_authentification_failed"); errmsg = greplace("_STATUS_", errstatus, errmsg); break; } int r = system(("dialog --menu \"`cat urbackup/restore/error_happend` " + errmsg + ". `cat urbackup/restore/how_to_continue`\" 15 50 10 " "\"r\" \"`cat urbackup/restore/search_again`\" " "\"i\" \"`cat urbackup/restore/connect_to_internet_server`\" " "\"s\" \"`cat urbackup/restore/start_shell`\" \"s\" " "\"`cat urbackup/restore/stop_restore`\" 2> out").c_str()); if (r != 0) { return; } std::string out = getFile("out"); if (out == "s") { system("bash"); } else if (out == "i") { configure_internet_server(); } } void configure_local_server() { system("systemctl stop restore-client-internet"); Server->deleteFile("urbackup/data_1/settings.cfg"); system("systemctl restart restore-client"); } } namespace restore { bool do_login(LoginData& login_data, bool with_cli_output) { if (login_data.has_login_data) { if (login_data.username.empty()) { int ec; return tryLogin("", "", std::vector(), 10, &ec); } int ec; std::vector salts = getSalt(login_data.username, 10, &ec); bool found_salt = false; for (size_t i = 0; i < salts.size(); ++i) { if (!salts[i].salt.empty() && !salts[i].rnd.empty()) { found_salt = true; break; } } if (!found_salt) { return false; } return tryLogin(login_data.username, login_data.password, salts, 10, &ec); } if (with_cli_output) { system("clear"); system("cat urbackup/restore/trying_to_login"); } int ec; if (!tryLogin("", "", std::vector(), 0, &ec)) { if (ec == 1) { //Server probably does not support logging in std::vector clients = getBackupclients(ec); if (ec == 0 && !clients.empty()) { return true; } } if (!with_cli_output) { return false; } while (true) { int rc = system("dialog --inputbox \"`cat urbackup/restore/enter_username`\" 8 30 2> out"); std::string username = getFile("out"); if (rc != 0) { return false; } std::vector salts = getSalt(username, 5, &ec); bool found_salt = false; for (size_t i = 0; i < salts.size(); ++i) { if (!salts[i].salt.empty() && !salts[i].rnd.empty()) { found_salt = true; break; } } if (!found_salt) { rc = system("dialog --yesno \"`cat urbackup/restore/user_not_found`\" 7 50"); if (rc != 0) { return false; } } else { rc = system("dialog --insecure --passwordbox \"`cat urbackup/restore/enter_password`\" 8 30 2> out"); std::string password = getFile("out"); if (rc != 0) { return false; } if (!tryLogin(username, password, salts, 5, &ec)) { rc = system("dialog --yesno \"`cat urbackup/restore/login_failed`\" 7 50"); if (rc != 0) { return false; } } else { login_data.has_login_data = true; login_data.username = username; login_data.password = password; return true; } } } } else { login_data.has_login_data = true; return true; } } } const int start_state=-2; void restore_wizard(void) { int state=start_state; std::vector clients; std::string clientname; std::vector images; SImage selimage; SImage r_selimage; std::string seldrive; std::string windows_partition; std::string selpart; std::string err; bool res_sysvol=false; LoginData login_data; while(true) { switch(state) { case -2: { system("dialog --msgbox \"`cat urbackup/restore/welcome`\" 10 70"); ++state; }break; case -1: { if(!has_network_device()) { system("clear"); system("cat urbackup/restore/search_network"); int tries=20; bool has_interface=false; do { bool b=has_network_device(); if(b) { has_interface=true; break; } --tries; Server->wait(1000); } while(tries>=0); if(has_interface==false) { int r=system("dialog --menu \"`cat urbackup/restore/no_network_device`\" 15 50 10 \"n\" \"`cat urbackup/restore/configure_networkcard`\" \"w\" \"`cat urbackup/restore/configure_wlan`\" \"e\" \"`cat urbackup/restore/start_shell`\" \"c\" \"`cat urbackup/restore/continue_restore`\" 2> out"); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); if(out=="n") { system(configure_networkcard.c_str()); state=0; } else if(out=="w") { system(configure_wlan.c_str()); state=0; } else if(out=="e") { system("bash"); state=0; } else if(out=="c") state=0; else state=99; } else { state=0; } } else { state=0; } }break; case 0: { if (!internet_server_configured) { system("urbackup/restore/progress-start.sh | dialog --backtitle \"`cat urbackup/restore/search`\" --gauge \"`cat urbackup/restore/t_progress`\" 6 60 0"); } ++state; }break; case 1: { std::string errmsg; int ec; login_data = LoginData(); if(do_login(login_data)) { clients=getBackupclients(ec); switch(ec) { case 10: case 1: errmsg="`cat urbackup/restore/internal_error`"; break; case 2: errmsg="`cat urbackup/restore/no_server_found`"; break; } if(errmsg.empty() && clients.empty()) { ec=3; errmsg="`cat urbackup/restore/no_clients_found`"; } } else { errmsg="login failed"; ec=1; } if(ec!=0) { int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` "+errmsg+". `cat urbackup/restore/how_to_continue`\" 15 50 10 " "\"r\" \"`cat urbackup/restore/search_again`\" " "\"n\" \"`cat urbackup/restore/configure_networkcard`\" " "\"w\" \"`cat urbackup/restore/configure_wlan`\" " "\"e\" \"`cat urbackup/restore/enter_server_ip`\" " "\"i\" \"`cat urbackup/restore/connect_to_internet_server`\" " "\"s\" \"`cat urbackup/restore/start_shell`\" \"s\" " "\"`cat urbackup/restore/stop_restore`\" 2> out").c_str()); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); if(out=="r") state=0; else if(out=="n") { system(configure_networkcard.c_str()); state=0; } else if(out=="w") { system(configure_wlan.c_str()); state=0; } else if(out=="e") { ping_named_server(); state=0; } else if (out == "i") { configure_internet_server(); state = 0; } else if(out=="s") { system("bash"); state=0; } else state=99; } else { std::string mi; for(size_t i=0;i out").c_str()); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); clientname=clients[atoi(out.c_str())-1]; ++state; } }break; case 2: { int ec; images=getBackupimages(clientname, &ec); std::string errmsg; switch(ec) { case 10: case 1: errmsg="`cat urbackup/restore/internal_error`"; break; case 2: errmsg="`cat urbackup/restore/no_server_found`"; break; } if(images.empty()) { ec=3; errmsg="`cat urbackup/restore/no_images_for_client1` '"+clientname+"' `cat urbackup/restore/no_images_for_client2`"; } std::sort(images.begin(), images.end()); if(ec!=0) { int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` "+errmsg+". `cat urbackup/restore/how_to_continue`\" 15 50 10 \"a\" \"`cat urbackup/restore/error_j_select`\" \"r\" \"`cat urbackup/restore/search_again`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out").c_str()); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); if(out=="r") state=0; else if(out=="a") state=1; else state=99; } else { std::string mi; for(size_t i=0;i out").c_str()); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); selimage=images[atoi(out.c_str())-1]; r_selimage=selimage; ++state; } }break; case 3: { std::vector drives = lsblk(""); bool has_disk=false; for(size_t i=0;i out"); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); if(out=="r") state=3; else state=99; break; } std::string mi; for(size_t i=0;i out"; writestring(scmd, "scmd.sh"); int r=system(scmd.c_str()); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); int driveidx=atoi(out.c_str())-1; seldrive=drives[driveidx].path; r=system(("dialog --yesno \"`cat urbackup/restore/select_certain`\\n"+drives[driveidx].model+" `cat urbackup/restore/size`: "+drives[driveidx].size+"\" 10 50").c_str()); if(r!=0) { break; } ++state; }break; case 4: { system("clear"); system("cat urbackup/restore/loading_mbr"); system("echo"); if(FileExists("mbr.dat")) { Server->deleteFile("mbr.dat"); } system("touch mbr.dat"); DownloadStatus dl_status; EDownloadResult rc = downloadImage(selimage.id, convert(selimage.time_s), "mbr.dat", true, login_data, dl_status); if (rc != EDownloadResult_Ok ) { Server->Log("Error downloading MBR", LL_ERROR); err="cannot_read_mbr"; state=101; break; } system("cat urbackup/restore/reading_mbr"); system("echo"); if (is_disk_mbr("mbr.dat")) { std::cout << "Restoring whole disk..." << std::endl; ++state; selpart = seldrive; break; } IFile *f=Server->openFile("mbr.dat", MODE_READ); if(f==nullptr) { err="cannot_read_mbr"; state=101; break; } size_t fsize=(size_t)f->Size(); char *buf=new char[fsize]; f->Read(buf, (_u32)fsize); Server->destroy(f); CRData mbr(buf, fsize); SMBRData mbrdata(mbr); if(mbrdata.hasError()) { err="error_while_reading_mbr"; exit(3); state=101; break; } system("cat urbackup/restore/writing_mbr"); system("echo"); IFile *dev=Server->openFile(seldrive, MODE_RW); if(dev==nullptr) { err="cannot_open_disk"; state=101; break; } dev->Seek(0); if(dev->Write(mbrdata.mbr_data)!=mbrdata.mbr_data.size()) { err="error_writing_mbr"; state=101; break; } bool fix_gpt = false; std::vector partitions; if(mbrdata.gpt_style) { std::string onlypart = ExtractFileName(seldrive); std::string logical_block_size_str = getFile("/sys/block/"+onlypart+"/queue/logical_block_size"); if(!logical_block_size_str.empty()) { unsigned int logical_block_size = atoi(logical_block_size_str.c_str()); if(logical_block_size!=mbrdata.gpt_header_pos) { err="gpt_logical_blocksize_change"; state=101; break; } Server->Log("Logical block size: "+convert(logical_block_size)); } else { Server->Log("Could not get logical block size"); } system("cat urbackup/restore/writing_gpt_header"); system("echo"); if(!dev->Seek(mbrdata.gpt_header_pos) || dev->Write(mbrdata.gpt_header)!=mbrdata.gpt_header.size()) { err="error_writing_gpt"; state=101; break; } system("cat urbackup/restore/writing_gpt_table"); system("echo"); if(!dev->Seek(mbrdata.gpt_table_pos) || dev->Write(mbrdata.gpt_table)!=mbrdata.gpt_table.size()) { err="error_writing_gpt"; state=101; break; } system("cat urbackup/restore/writing_backup_gpt_header"); system("echo"); if(!dev->Seek(mbrdata.backup_gpt_header_pos) || dev->Write(mbrdata.backup_gpt_header)!=mbrdata.backup_gpt_header.size()) { std::cout << "Error writing backup GPT header. " << os_last_error_str() << ". Fixing with GNU parted..." << std::endl; fix_gpt = true; } system("cat urbackup/restore/writing_backup_gpt_table"); system("echo"); if(!dev->Seek(mbrdata.backup_gpt_table_pos) || dev->Write(mbrdata.backup_gpt_table)!=mbrdata.backup_gpt_table.size()) { std::cout << "Error writing backup GPT table. " << os_last_error_str() << ". Fixing with GNU parted..." << std::endl; fix_gpt = true; } if (fix_gpt) { system(("echo -e \"w\\nY\\nY\" | gdisk " + seldrive).c_str()); bool t_gpt_style; partitions = image_fak->readPartitions(mbrdata.mbr_data, mbrdata.gpt_header, mbrdata.gpt_table, t_gpt_style); } else { system(("echo -e \"w\\nY\\nY\" | gdisk " + seldrive + " > /dev/null 2>&1").c_str()); } } if (mbrdata.extra_data_pos != -1) { std::cout << "Writing extra data..." << std::endl; if (dev->Write(mbrdata.extra_data_pos, mbrdata.extra_data) != mbrdata.extra_data.size()) { std::cout << "Error writing extra data. Ignoring..."; } } Server->destroy(dev); system("cat urbackup/restore/reading_partition_table"); system("echo"); Server->Log("Selected device: "+seldrive+" Partition: "+convert(mbrdata.partition_number)); system(("partprobe "+seldrive+" > /dev/null 2>&1").c_str()); Server->wait(10000); system("cat urbackup/restore/testing_partition"); system("echo"); dev=nullptr; std::string partpath = getPartitionPath(seldrive, mbrdata.partition_number); if(!partpath.empty()) { dev=Server->openFile(partpath, MODE_RW); } int try_c=0; int delete_parts = 0; while(dev==nullptr && try_c<10) { system(("partprobe "+seldrive+" > /dev/null 2>&1").c_str()); Server->wait(10000); system("cat urbackup/restore/testing_partition"); system("echo"); partpath = getPartitionPath(seldrive, mbrdata.partition_number); if(!partpath.empty()) { dev=Server->openFile(partpath, MODE_RW); } if(dev==nullptr) { if (!mbrdata.gpt_style) { Server->Log("Trying to fix LBA partitioning scheme via fdisk..."); //Fix LBA partition signature system(("echo w | fdisk " + seldrive + " > /dev/null 2>&1").c_str()); system(("echo -e \"w\\nY\" | fdisk " + seldrive + " > /dev/null 2>&1").c_str()); } else { Server->Log("Trying to fix GPT partitioning scheme via gdisk..."); system(("echo -e \"w\\nY\" | gdisk " + seldrive).c_str()); } if (fix_gpt && try_c>5 && !partitions.empty()) { //TODO: the last partition might not be the one with the highest offset Server->Log("Deleting last GPT partition..."); ++delete_parts; std::string cmd; for (int i = 0; i < delete_parts; ++i) { if (partitions.size() - i == mbrdata.partition_number) { Server->Log("Cannot delete last GPT partition. This is the partition to be restored. DISK IS PROBABLY TOO SMALL TO RESTORE TO.", LL_ERROR); system("read -n1 -r -p \"Press any key to continue...\" key"); fix_gpt = false; try_c = 100; } cmd += "d\\n" + convert(partitions.size() - i); } cmd += "\\nw\\nY"; if(fix_gpt) system(("echo -e \""+cmd+"\" | gdisk " + seldrive).c_str()); } } ++try_c; } if(dev==nullptr) { err="no_restore_partition"; state=101; break; } selpart=partpath; Server->destroy(dev); delete []buf; ++state; }break; case 5: { if(windows_partition.empty()) { windows_partition=selpart; } RestoreThread rt(selimage.id, convert(selimage.time_s), selpart, login_data); THREADPOOL_TICKET rt_ticket=Server->getThreadPool()->execute(&rt, "image restore"); while(true) { system(("./urbackuprestoreclient --image-download-progress | dialog --backtitle \"`cat urbackup/restore/restoration"+(std::string)(res_sysvol?"_sysvol":"")+"`\" --gauge \"`cat urbackup/restore/t_progress`\" 6 60 0").c_str()); while(!rt.isDone() && !restore_retry_ok) { Server->wait(1000); } if(rt.isDone()) break; if(restore_retry_ok) restore_retry_ok=false; } Server->getThreadPool()->waitFor(rt_ticket); EDownloadResult rc=rt.getRC(); std::string errmsg; switch(rc) { case EDownloadResult_ConnectError: errmsg="`cat urbackup/restore/no_connection`"; break; case EDownloadResult_OpenError: errmsg="`cat urbackup/restore/cannot_write_on_partition`"; break; case EDownloadResult_SizeReadError: errmsg="`cat urbackup/restore/wrong_size`"; break; case EDownloadResult_TimeoutError2: errmsg="`cat urbackup/restore/server_doesnot_respond`"; break; case EDownloadResult_TimeoutError1: errmsg="`cat urbackup/restore/server_connection_timeout`"; break; case EDownloadResult_WriteFailed: errmsg="`cat urbackup/restore/writing_failed`"; break; case EDownloadResult_DeviceTooSmall: errmsg = greplace("_1_", PrettyPrintBytes(rt.getImgsize()), greplace("_2_", PrettyPrintBytes(rt.getOutputFileSize()), getFile("urbackup/restore/device_too_small"))); break; }; if(rc!=0) { int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` "+errmsg+". `cat urbackup/restore/how_to_continue`\" 15 50 10 \"r\" \"`cat urbackup/restore/restart_restore`\" \"s\" \"`cat urbackup/restore/start_shell`\" \"o\" \"`cat urbackup/restore/restore_other`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out").c_str()); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); if(out=="r") state=5; else if(out=="o") state=2; else if(out=="s") { system("bash"); state=0; } else state=99; } else { if(r_selimage.assoc.size()>0) { selimage=r_selimage.assoc[0]; r_selimage.assoc.erase(r_selimage.assoc.begin()); res_sysvol=true; state=4; } else { ++state; } } }break; case 6: { int r=system("dialog --menu \"`cat urbackup/restore/restore_success`\" 15 70 10 \"r\" \"`cat urbackup/restore/restart_computer`\" \"b\" \"`cat urbackup/restore/bootable_on_different_hardware`\" \"o\" \"`cat urbackup/restore/restore_other`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out"); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); if(out=="r") system("init 6"); else if(out=="o") state=2; else if(out=="s") system("bash"); else if(out=="b") system(("python3 driver_edit.py "+windows_partition).c_str()); else system("init 6"); }break; case 99: { system("dialog --msgbox \"`cat urbackup/restore/computer_halt`\" 7 50"); system("init 0"); exit(1); }break; case 101: { int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` `cat urbackup/restore/"+err+"`. `cat urbackup/restore/how_to_continue`\" 15 50 10 \"r\" \"`cat urbackup/restore/restart_restore`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out").c_str()); if(r!=0) { state=start_state; break; } std::string out=getFile("out"); if(out=="r") state=0; else state=99; }break; default: { system("dialog --msgbox \"`cat urbackup/restore/internal_error`!!!!\" 7 50"); exit(99); }break; } } }