mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
459 lines
12 KiB
C++
459 lines
12 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2015 Martin Raiber
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************************/
|
|
|
|
#include "FileMetadataPipe.h"
|
|
#include "../common/data.h"
|
|
#include <assert.h>
|
|
#include "../Interface/Server.h"
|
|
#include "../stringtools.h"
|
|
#include "../urbackupcommon/os_functions.h"
|
|
#include "IFileServ.h"
|
|
#include <cstring>
|
|
#include "FileServ.h"
|
|
|
|
const size_t metadata_id_size = 4+4+8+4;
|
|
|
|
|
|
FileMetadataPipe::FileMetadataPipe( IPipe* pipe, const std::wstring& cmd )
|
|
: PipeFileBase(cmd), pipe(pipe),
|
|
#ifdef _WIN32
|
|
hFile(INVALID_HANDLE_VALUE),
|
|
#endif
|
|
metadata_state(MetadataState_Wait),
|
|
errpipe(Server->createMemoryPipe())
|
|
{
|
|
metadata_buffer.resize(4096);
|
|
init();
|
|
}
|
|
|
|
|
|
bool FileMetadataPipe::getExitCode( int& exit_code )
|
|
{
|
|
exit_code = 0;
|
|
return true;
|
|
}
|
|
|
|
bool FileMetadataPipe::readStdoutIntoBuffer( char* buf, size_t buf_avail, size_t& read_bytes )
|
|
{
|
|
if(token_callback.get()==NULL)
|
|
{
|
|
token_callback.reset(FileServ::newTokenCallback());
|
|
}
|
|
|
|
if(buf_avail==0)
|
|
{
|
|
read_bytes = 0;
|
|
return true;
|
|
}
|
|
|
|
if(metadata_state==MetadataState_FnSize)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, sizeof(unsigned int) - fn_off);
|
|
unsigned int fn_size = little_endian(static_cast<unsigned int>(public_fn.size()));
|
|
memcpy(buf, &fn_size + fn_off, read_bytes);
|
|
fn_off+=read_bytes;
|
|
|
|
if(fn_off==sizeof(unsigned int))
|
|
{
|
|
fn_off=0;
|
|
metadata_state = MetadataState_Fn;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if(metadata_state==MetadataState_Fn)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, public_fn.size()- fn_off);
|
|
memcpy(buf, public_fn.data(), read_bytes);
|
|
fn_off+=read_bytes;
|
|
|
|
if(fn_off==public_fn.size())
|
|
{
|
|
metadata_buffer_size = 0;
|
|
metadata_buffer_off = 0;
|
|
if(callback==NULL)
|
|
{
|
|
metadata_state = MetadataState_Common;
|
|
}
|
|
else
|
|
{
|
|
if(!metadata_file->Seek(metadata_file_off))
|
|
{
|
|
errpipe->Write(Server->ConvertToUTF8(L"Error seeking to metadata in \"" + metadata_file->getFilenameW()+L"\""));
|
|
|
|
read_bytes=0;
|
|
metadata_state = MetadataState_Wait;
|
|
return false;
|
|
}
|
|
|
|
metadata_state = MetadataState_File;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if(metadata_state == MetadataState_Common)
|
|
{
|
|
if(metadata_buffer_size==0)
|
|
{
|
|
SFile file_meta = getFileMetadata(os_file_prefix(Server->ConvertToUnicode(local_fn)));
|
|
if(file_meta.name.empty())
|
|
{
|
|
Server->Log("Error getting metadata (created and last modified time) of "+local_fn, LL_ERROR);
|
|
}
|
|
|
|
int64 created = little_endian(file_meta.created);
|
|
int64 modified = little_endian(file_meta.last_modified);
|
|
|
|
CWData meta_data;
|
|
meta_data.addChar(1);
|
|
meta_data.addInt64(created);
|
|
meta_data.addInt64(modified);
|
|
meta_data.addString(token_callback->getFileTokens(Server->ConvertToUnicode(local_fn)));
|
|
|
|
if(meta_data.getDataSize()+sizeof(unsigned int)<metadata_buffer.size())
|
|
{
|
|
unsigned int data_size = little_endian(static_cast<unsigned int>(meta_data.getDataSize()));
|
|
memcpy(metadata_buffer.data(), &data_size, sizeof(data_size));
|
|
memcpy(metadata_buffer.data()+sizeof(unsigned int), meta_data.getDataPtr(), meta_data.getDataSize());
|
|
metadata_buffer_size = meta_data.getDataSize();
|
|
}
|
|
else
|
|
{
|
|
Server->Log("File metadata of "+local_fn+" too large ("+nconvert((size_t)meta_data.getDataSize())+")", LL_ERROR);
|
|
}
|
|
|
|
}
|
|
|
|
if(metadata_buffer_size-metadata_buffer_off>0)
|
|
{
|
|
read_bytes = (std::min)(metadata_buffer_size-metadata_buffer_off, buf_avail);
|
|
memcpy(buf, metadata_buffer.data()+metadata_buffer_off, read_bytes);
|
|
metadata_buffer_off+=read_bytes;
|
|
|
|
if(metadata_buffer_size-metadata_buffer_off == 0)
|
|
{
|
|
metadata_buffer_size = 0;
|
|
metadata_buffer_off = 0;
|
|
metadata_state=MetadataState_Os;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else if(metadata_state == MetadataState_Os)
|
|
{
|
|
if(!transmitCurrMetadata(buf, buf_avail, read_bytes))
|
|
{
|
|
metadata_state = MetadataState_Wait;
|
|
}
|
|
return true;
|
|
}
|
|
else if(metadata_state == MetadataState_File)
|
|
{
|
|
read_bytes = static_cast<size_t>((std::min)(static_cast<int64>(buf_avail), metadata_file_size));
|
|
|
|
if(read_bytes==0)
|
|
{
|
|
metadata_state = MetadataState_Wait;
|
|
return true;
|
|
}
|
|
|
|
_u32 read = metadata_file->Read(buf, static_cast<_u32>(read_bytes));
|
|
if(read!=read_bytes)
|
|
{
|
|
errpipe->Write(Server->ConvertToUTF8(L"Error reading metadata stream from file \""+metadata_file->getFilenameW()+L"\"\n"));
|
|
memset(buf + read, 0, read_bytes - read);
|
|
}
|
|
|
|
metadata_file_size-=read_bytes;
|
|
|
|
if(read_bytes<buf_avail)
|
|
{
|
|
metadata_state = MetadataState_Wait;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
while(true)
|
|
{
|
|
std::string msg;
|
|
size_t r = pipe->Read(&msg, 100000);
|
|
|
|
if(r==0)
|
|
{
|
|
if(pipe->hasError())
|
|
{
|
|
read_bytes = 0;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*buf = ID_METADATA_NOP;
|
|
read_bytes = 1;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CRData msg_data(&msg);
|
|
|
|
if(msg_data.getStr(&public_fn) &&
|
|
msg_data.getStr(&local_fn))
|
|
{
|
|
if(public_fn.empty() &&
|
|
local_fn.empty())
|
|
{
|
|
read_bytes = 0;
|
|
return false;
|
|
}
|
|
|
|
bool isdir=false;
|
|
if(isDirectory(os_file_prefix(Server->ConvertToUnicode(local_fn))))
|
|
{
|
|
isdir=true;
|
|
public_fn = "d" + public_fn;
|
|
}
|
|
else
|
|
{
|
|
public_fn = "f" + public_fn;
|
|
}
|
|
|
|
metadata_state = MetadataState_FnSize;
|
|
*buf = ID_METADATA_V1;
|
|
read_bytes = 1;
|
|
fn_off = 0;
|
|
|
|
if(!msg_data.getVoidPtr(reinterpret_cast<void**>(&callback)))
|
|
{
|
|
callback=NULL;
|
|
}
|
|
else
|
|
{
|
|
std::string orig_path;
|
|
metadata_file = callback->getMetadata(public_fn, orig_path, metadata_file_off, metadata_file_size);
|
|
|
|
if(metadata_file==NULL)
|
|
{
|
|
errpipe->Write("Error opening metadata file for \""+public_fn+"\"");
|
|
|
|
read_bytes=0;
|
|
metadata_state = MetadataState_Wait;
|
|
return false;
|
|
}
|
|
|
|
public_fn = (isdir?"d":"f") + orig_path;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FileMetadataPipe::readStderrIntoBuffer( char* buf, size_t buf_avail, size_t& read_bytes )
|
|
{
|
|
while(true)
|
|
{
|
|
if(stderr_buf.size()>0)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, stderr_buf.size());
|
|
memcpy(buf, stderr_buf.data(), read_bytes);
|
|
stderr_buf.erase(0, read_bytes);
|
|
return true;
|
|
}
|
|
|
|
if(errpipe->Read(&stderr_buf)==0)
|
|
{
|
|
if(pipe->hasError())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
bool FileMetadataPipe::transmitCurrMetadata( char* buf, size_t buf_avail, size_t& read_bytes )
|
|
{
|
|
if(metadata_buffer_size-metadata_buffer_off>0)
|
|
{
|
|
read_bytes = (std::min)(metadata_buffer_size-metadata_buffer_off, buf_avail);
|
|
memcpy(buf, metadata_buffer.data()+metadata_buffer_off, read_bytes);
|
|
metadata_buffer_off+=read_bytes;
|
|
return true;
|
|
}
|
|
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
hFile = CreateFileW(os_file_prefix(Server->ConvertToUnicode(local_fn)).c_str(), GENERIC_READ|ACCESS_SYSTEM_SECURITY|READ_CONTROL, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
if(hFile==INVALID_HANDLE_VALUE)
|
|
{
|
|
errpipe->Write("Error opening file \""+local_fn+"\" to read metadata. Last error: "+nconvert((int)GetLastError())+"\n");
|
|
return false;
|
|
}
|
|
|
|
backup_read_state = 0;
|
|
backup_read_context=NULL;
|
|
}
|
|
|
|
if(backup_read_state==0)
|
|
{
|
|
DWORD total_read = 0;
|
|
DWORD read;
|
|
BOOL b = BackupRead(hFile, reinterpret_cast<LPBYTE>(metadata_buffer.data()+1), metadata_id_size, &read, FALSE, TRUE, &backup_read_context);
|
|
|
|
if(b==FALSE)
|
|
{
|
|
errpipe->Write("Error getting metadata of file \""+local_fn+"\". Last error: "+nconvert((int)GetLastError())+"\n");
|
|
*buf = 0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
if(read==0)
|
|
{
|
|
BackupRead(hFile, NULL, 0, NULL, TRUE, TRUE, &backup_read_context);
|
|
backup_read_context=NULL;
|
|
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
*buf=0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
if(read<metadata_id_size)
|
|
{
|
|
errpipe->Write("Error getting metadata stream structure.\n");
|
|
*buf=0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
total_read += read;
|
|
|
|
metadata_buffer[0]=1;
|
|
|
|
WIN32_STREAM_ID* curr_stream = reinterpret_cast<WIN32_STREAM_ID*>(metadata_buffer.data()+1);
|
|
|
|
if(curr_stream->dwStreamNameSize>0)
|
|
{
|
|
b = BackupRead(hFile, reinterpret_cast<LPBYTE>(metadata_buffer.data()+1) + read, curr_stream->dwStreamNameSize, &read, FALSE, TRUE, &backup_read_context);
|
|
|
|
if(b==FALSE)
|
|
{
|
|
errpipe->Write("Error getting metadata of file \""+local_fn+"\" (2). Last error: "+nconvert((int)GetLastError())+"\n");
|
|
*buf=0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
if(read<curr_stream->dwStreamNameSize)
|
|
{
|
|
errpipe->Write("Error getting metadata stream structure (name).\n");
|
|
*buf=0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
total_read += read;
|
|
}
|
|
|
|
if(curr_stream->dwStreamId==BACKUP_DATA)
|
|
{
|
|
//skip
|
|
LARGE_INTEGER seeked;
|
|
DWORD high_seeked;
|
|
b = BackupSeek(hFile, curr_stream->Size.LowPart, curr_stream->Size.HighPart, &seeked.LowPart, &high_seeked, &backup_read_context);
|
|
seeked.HighPart = high_seeked;
|
|
|
|
if(b==FALSE)
|
|
{
|
|
errpipe->Write("Error skipping data stream of file \""+local_fn+"\" (1). Last error: "+nconvert((int)GetLastError())+"\n");
|
|
*buf=0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
if(seeked.QuadPart!=curr_stream->Size.QuadPart)
|
|
{
|
|
errpipe->Write("Error skipping data stream of file \""+local_fn+"\" (2). Last error: "+nconvert((int)GetLastError())+"\n");
|
|
*buf=0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
return transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
}
|
|
|
|
backup_read_state = 1;
|
|
curr_stream_size = curr_stream->Size;
|
|
metadata_buffer_size = total_read + 1;
|
|
metadata_buffer_off = 0;
|
|
curr_pos = 0;
|
|
|
|
return transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
}
|
|
else if(backup_read_state==1)
|
|
{
|
|
DWORD toread = static_cast<DWORD>((std::min)(curr_stream_size.QuadPart-curr_pos, static_cast<int64>(buf_avail)));
|
|
|
|
DWORD read;
|
|
BOOL b = BackupRead(hFile, reinterpret_cast<LPBYTE>(buf), toread, &read, FALSE, TRUE, &backup_read_context);
|
|
|
|
if(b==FALSE)
|
|
{
|
|
errpipe->Write("Error reading metadata stream of file \""+local_fn+"\". Last error: "+nconvert((int)GetLastError())+"\n");
|
|
memset(buf, 0, toread);
|
|
}
|
|
|
|
read_bytes = read;
|
|
curr_pos+=read;
|
|
|
|
if(curr_pos==curr_stream_size.QuadPart)
|
|
{
|
|
backup_read_state = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#else //_WIN32
|
|
|
|
bool FileMetadataPipe::transmitCurrMetadata(char* buf, size_t buf_avail, size_t& read_bytes)
|
|
{
|
|
assert(false);
|
|
//TODO: Implement
|
|
return false;
|
|
}
|
|
|
|
#endif //_WIN32
|
|
|