/************************************************************************* * 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 "../urbackupcommon/fileclient/FileClient.h" #include "../urbackupcommon/fileclient/tcpstack.h" #include "server_prepare_hash.h" #include "../Interface/Pipe.h" #include "../Interface/File.h" #include "../Interface/Server.h" #include "filedownload.h" #include "../urbackupcommon/os_functions.h" #include "../stringtools.h" #include #include "../urbackupcommon/chunk_hasher.h" extern std::string server_identity; bool FileDownload::copy_file_fd(IFile *fsrc, IFile *fdst) { fsrc->Seek(0); fdst->Seek(0); char buf[4096]; size_t rc; while( (rc=(_u32)fsrc->Read(buf, 4096))>0) { fdst->Write(buf, (_u32)rc); } return true; } void FileDownload::filedownload(std::string remotefn, std::string dest, int method, int predicted_filesize, SQueueStatus queueStatus) { IFsFile *dstfile; if(queueStatus==SQueueStatus_Queue || queueStatus==SQueueStatus_NoQueue) { Server->Log("Opening destination file..."); if(method==0) { dstfile=Server->openFile(dest, MODE_WRITE); } else { dstfile=Server->openFile(dest, MODE_RW_CREATE); } if(dstfile==NULL) { Server->Log("Cannot open file"); return; } } _u32 rc=ERR_SUCCESS; if(method==0) { if(queueStatus==SQueueStatus_Queue) { FileDownloadQueueItemFull qi = { remotefn, predicted_filesize, false }; dlqueueFull.push_back(qi); } else { Server->Log("Downloading file..."); for(size_t i=0;iGetFile(remotefn, dstfile, true, false, 0, false, 0); Server->destroy(dstfile); } } else if(method==1) { IFile *hashfile; IFsFile *hashfile_output; if(queueStatus==SQueueStatus_Queue || queueStatus==SQueueStatus_NoQueue) { hashfile=Server->openTemporaryFile(); hashfile_output=Server->openTemporaryFile(); Server->Log("Building hashes..."); build_chunk_hashs(dstfile, hashfile, NULL, NULL, false, NULL, NULL); Server->Log("Downloading file..."); if(queueStatus==SQueueStatus_Queue) { FileDownloadQueueItemChunked qi = { remotefn, dstfile, NULL, hashfile, hashfile_output, predicted_filesize, false }; dlqueueChunked.push_back(qi); } } else { for(size_t i=0;i=0) { remote_filesize = predicted_filesize; } if(queueStatus==SQueueStatus_NoQueue || queueStatus==SQueueStatus_IsQueued) { rc=fc_chunked->GetFileChunked(remotefn, dstfile, hashfile, hashfile_output, remote_filesize, 0, false, NULL); cleanup_tmpfile(hashfile); cleanup_tmpfile(hashfile_output); _i64 fsize=dstfile->Size(); Server->destroy(dstfile); if(rc==ERR_SUCCESS && fsize>remote_filesize) { os_file_truncate(dest, remote_filesize); } } } else if(method==2) { IFile *hashfile; IFsFile *hashfile_output; IFile *patchfile; if(queueStatus==SQueueStatus_Queue || queueStatus==SQueueStatus_NoQueue) { hashfile=Server->openTemporaryFile(); hashfile_output=Server->openTemporaryFile(); patchfile=Server->openTemporaryFile(); Server->Log("Building hashes..."); build_chunk_hashs(dstfile, hashfile, NULL, NULL, false); if(queueStatus==SQueueStatus_Queue) { FileDownloadQueueItemChunked qi = { remotefn, dstfile, patchfile, hashfile, hashfile_output, predicted_filesize, false }; dlqueueChunked.push_back(qi); } /*int64 dstf_size = dstfile->Size(); if(dstf_size>0) { dstfile->Remove(); os_file_truncate(widen(dest), (dstf_size/(512*1024)) * 512*1024 + 1); dstfile=Server->openFile(dest, MODE_RW); }*/ } else { for(size_t i=0;i0) { remote_filesize = predicted_filesize; } if(queueStatus==SQueueStatus_IsQueued || queueStatus==SQueueStatus_NoQueue) { Server->Log("Downloading file..."); rc=fc_chunked->GetFilePatch(remotefn, dstfile, patchfile, hashfile, hashfile_output, remote_filesize, 0, false, NULL); IFile *tmpfile=Server->openTemporaryFile(); Server->Log("Copying to temporary..."); copy_file_fd(dstfile, tmpfile); tmpfile->Seek(0); m_chunkpatchfile=tmpfile; ChunkPatcher patcher; patcher.setCallback(this); curr_chunk_patch_pos =0; Server->Log("Patching temporary..."); patcher.ApplyPatch(dstfile, patchfile, NULL); Server->Log("Copying back..."); copy_file_fd(tmpfile, dstfile); cleanup_tmpfile(hashfile); cleanup_tmpfile(hashfile_output); cleanup_tmpfile(tmpfile); _i64 fsize=dstfile->Size(); Server->destroy(dstfile); if(remote_filesize==patcher.getFilesize() && fsize>=patcher.getFilesize()) { if(rc==ERR_SUCCESS && fsize>patcher.getFilesize()) { os_file_truncate(dest, patcher.getFilesize()); } cleanup_tmpfile(patchfile); } else { Server->Log("Filesize is wrong...", LL_ERROR); Server->Log("Patchfile: "+patchfile->getFilename(), LL_ERROR); } } } else { Server->Log("File download method unknown", LL_ERROR); return; } if(queueStatus==SQueueStatus_NoQueue || queueStatus==SQueueStatus_IsQueued) { if(rc!=ERR_SUCCESS) { Server->Log("File download failed ec="+FileClient::getErrorString(rc)); } else { Server->Log("File download succeeded"); } } } void FileDownload::filedownload( std::string csvfile ) { std::string data = getFile(csvfile); int lines = linecount(data); for(int line=0;line cols; Tokenize(ldata, cols, ","); if(cols.size()!=4) continue; filedownload(cols[0], cols[1], atoi(cols[2].c_str()), atoi(cols[3].c_str()), SQueueStatus_Queue); } for(int line=0;line cols; Tokenize(ldata, cols, ","); if(cols.size()!=4) continue; filedownload(cols[0], cols[1], atoi(cols[2].c_str()), atoi(cols[3].c_str()), SQueueStatus_IsQueued); } } IPipe * FileDownload::new_fileclient_connection(void) { for(size_t i=0;i<30;++i) { IPipe *cp=Server->ConnectStream(m_servername, m_tcpport, 10000); if(cp==NULL) { Server->Log("Cannot connect to server"); Server->wait(2000); } else { Server->Log("Reconnected"); return cp; } } return NULL; } void FileDownload::next_chunk_patcher_bytes(const char *buf, size_t bsize, bool changed, bool* is_sparse) { if(changed) { if(!m_chunkpatchfile->Seek(curr_chunk_patch_pos) || m_chunkpatchfile->Write(buf, (_u32)bsize)!=bsize) { Server->Log("Writing to file failed", LL_ERROR); exit(3); } } curr_chunk_patch_pos +=bsize; } void FileDownload::next_sparse_extent_bytes(const char * buf, size_t bsize) { } void FileDownload::cleanup_tmpfile(IFile *tmpfile) { std::string fn=tmpfile->getFilename(); Server->destroy(tmpfile); Server->deleteFile(fn); } int64 FileDownload::chunk_patcher_pos() { return curr_chunk_patch_pos; } bool FileDownload::getQueuedFileChunked( std::string& remotefn, IFile*& orig_file, IFile*& patchfile, IFile*& chunkhashes, IFsFile*& hashoutput, _i64& predicted_filesize, int64& file_id, bool& is_script) { for(size_t i=0;iLog("Connecting to server..."); cp=Server->ConnectStream(servername, tcpport, 10000); if(cp==NULL) { Server->Log("Cannot connect to server"); Server->wait(2000); } } if(cp==NULL) return; fc_chunked.reset(new FileClientChunked(cp, true, &tcpstack, this, NULL, server_identity, NULL)); fc_chunked->setQueueCallback(this); fc_chunked->setDestroyPipe(true); fc.reset(new FileClient(false, server_identity, 2)); cp=NULL; for(size_t i=0;i<30 && cp==NULL;++i) { Server->Log("Connecting to server..."); cp=Server->ConnectStream(servername, tcpport, 10000); if(cp==NULL) { Server->Log("Cannot connect to server"); Server->wait(2000); } } if(cp==NULL) return; fc->Connect(cp); fc->setQueueCallback(this); } std::string FileDownload::getQueuedFileFull( FileClient::MetadataQueue& metadata, size_t& folder_items, bool& finish_script, int64& file_id) { for(size_t i=0;i