mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
1283 lines
35 KiB
C++
1283 lines
35 KiB
C++
/*************************************************************************
|
|
* 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 <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"
|
|
#include "PipeSessions.h"
|
|
#include "../common/adler32.h"
|
|
#include <limits.h>
|
|
|
|
#ifndef _WIN32
|
|
#include <sys/types.h>
|
|
|
|
#ifndef __FreeBSD__
|
|
#include <sys/xattr.h>
|
|
#else
|
|
#include <sys/extattr.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#if defined(__APPLE__)
|
|
#define llistxattr(path, list, size) listxattr(path, list, size, XATTR_NOFOLLOW)
|
|
#define lgetxattr(path, name, value, size) getxattr(path, name, value, size, 0, XATTR_NOFOLLOW)
|
|
#define stat64 stat
|
|
#define lstat64 lstat
|
|
#endif
|
|
|
|
#if defined(__FreeBSD__)
|
|
#define llistxattr(path, list, size) extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size)
|
|
#define lgetxattr(path, name, value, size) extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size)
|
|
#define stat64 stat
|
|
#define lstat64 lstat
|
|
#endif
|
|
|
|
const size_t metadata_id_size = 4+4+8+4;
|
|
|
|
|
|
FileMetadataPipe::FileMetadataPipe( IPipe* pipe, const std::string& cmd )
|
|
: PipeFileBase(cmd), pipe(pipe),
|
|
#ifdef _WIN32
|
|
hFile(INVALID_HANDLE_VALUE),
|
|
backup_read_state(-1),
|
|
#else
|
|
backup_state(BackupState_StatInit),
|
|
#endif
|
|
metadata_state(MetadataState_Wait),
|
|
errpipe(Server->createMemoryPipe())
|
|
{
|
|
metadata_buffer.resize(4096);
|
|
init();
|
|
}
|
|
|
|
FileMetadataPipe::FileMetadataPipe()
|
|
: PipeFileBase(std::string()), pipe(nullptr),
|
|
#ifdef _WIN32
|
|
hFile(INVALID_HANDLE_VALUE),
|
|
backup_read_state(-1),
|
|
#else
|
|
backup_state(BackupState_StatInit),
|
|
#endif
|
|
metadata_state(MetadataState_Wait),
|
|
errpipe(Server->createMemoryPipe())
|
|
{
|
|
metadata_buffer.resize(4096);
|
|
}
|
|
|
|
FileMetadataPipe::~FileMetadataPipe()
|
|
{
|
|
assert(token_callback.get() == nullptr);
|
|
}
|
|
|
|
|
|
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() == nullptr)
|
|
{
|
|
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()));
|
|
fn_size = little_endian(fn_size);
|
|
memcpy(buf, reinterpret_cast<char*>(&fn_size) + fn_off, read_bytes);
|
|
curr_checksum = urb_adler32(curr_checksum, reinterpret_cast<char*>(&fn_size) + fn_off, static_cast<_u32>(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()+fn_off, read_bytes);
|
|
curr_checksum = urb_adler32(curr_checksum, public_fn.data()+fn_off, static_cast<_u32>(read_bytes));
|
|
fn_off+=read_bytes;
|
|
|
|
if(fn_off==public_fn.size())
|
|
{
|
|
fn_off=0;
|
|
metadata_state = MetadataState_FnChecksum;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if(metadata_state==MetadataState_FnChecksum)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, sizeof(unsigned int) - fn_off);
|
|
unsigned int endian_curr_checksum = little_endian(curr_checksum);
|
|
memcpy(buf, reinterpret_cast<char*>(&endian_curr_checksum) + fn_off, read_bytes);
|
|
fn_off+=read_bytes;
|
|
|
|
if(fn_off==sizeof(unsigned int))
|
|
{
|
|
metadata_buffer_size = 0;
|
|
metadata_buffer_off = 0;
|
|
curr_checksum = urb_adler32(0, nullptr, 0);
|
|
if(callback==nullptr)
|
|
{
|
|
metadata_state = MetadataState_Common;
|
|
}
|
|
else
|
|
{
|
|
if(!metadata_file->Seek(metadata_file_off))
|
|
{
|
|
std::string msg = "Error seeking to metadata in \"" + metadata_file->getFilename() + "\"";
|
|
Server->Log(msg, LL_ERROR);
|
|
errpipe->Write(msg);
|
|
|
|
read_bytes=0;
|
|
metadata_file.reset();
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
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(local_fn));
|
|
if(file_meta.name.empty())
|
|
{
|
|
Server->Log("Error getting metadata (created and last modified time) of "+local_fn, LL_ERROR);
|
|
}
|
|
|
|
CWData meta_data;
|
|
meta_data.addChar(1);
|
|
#ifdef _WIN32
|
|
meta_data.addVarInt(file_meta.created);
|
|
#else
|
|
meta_data.addVarInt(0);
|
|
#endif
|
|
meta_data.addVarInt(file_meta.last_modified);
|
|
meta_data.addVarInt(file_meta.accessed);
|
|
meta_data.addVarInt(folder_items);
|
|
meta_data.addVarInt(metadata_id);
|
|
if(token_callback.get()!=nullptr)
|
|
{
|
|
meta_data.addString(token_callback->getFileTokens(local_fn));
|
|
}
|
|
else
|
|
{
|
|
meta_data.addString("");
|
|
}
|
|
|
|
|
|
size_t meta_size = meta_data.getDataSize() + 2 * sizeof(unsigned int);
|
|
|
|
if(meta_size>20*1024*1024)
|
|
{
|
|
Server->Log("File metadata of " + local_fn + " too large (" + convert((size_t)meta_data.getDataSize()) + ")", LL_ERROR);
|
|
return false;
|
|
}
|
|
else if (meta_size > metadata_buffer.size())
|
|
{
|
|
metadata_buffer.resize(meta_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()+sizeof(unsigned int);
|
|
|
|
curr_checksum = urb_adler32(curr_checksum, metadata_buffer.data(), static_cast<_u32>(metadata_buffer_size));
|
|
unsigned int endian_curr_checksum = little_endian(curr_checksum);
|
|
memcpy(metadata_buffer.data()+metadata_buffer_size, &endian_curr_checksum, sizeof(endian_curr_checksum));
|
|
metadata_buffer_size+=sizeof(endian_curr_checksum);
|
|
}
|
|
|
|
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;
|
|
curr_checksum = urb_adler32(0, nullptr, 0);
|
|
metadata_state = MetadataState_Os;
|
|
}
|
|
return true;
|
|
}
|
|
else if(metadata_state == MetadataState_Os)
|
|
{
|
|
bool b = transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
|
|
if(read_bytes>0)
|
|
{
|
|
curr_checksum = urb_adler32(curr_checksum, buf, static_cast<_u32>(read_bytes));
|
|
}
|
|
|
|
if(!b)
|
|
{
|
|
fn_off=0;
|
|
metadata_state = MetadataState_OsChecksum;
|
|
|
|
#ifndef _WIN32
|
|
backup_state=BackupState_StatInit;
|
|
#else
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
backup_read_state = -1;
|
|
#endif
|
|
|
|
}
|
|
return true;
|
|
}
|
|
else if(metadata_state == MetadataState_OsChecksum)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, sizeof(unsigned int) - fn_off);
|
|
unsigned int endian_curr_checksum = little_endian(curr_checksum);
|
|
memcpy(buf, reinterpret_cast<char*>(&endian_curr_checksum) + fn_off, read_bytes);
|
|
fn_off+=read_bytes;
|
|
|
|
if(fn_off==sizeof(unsigned int))
|
|
{
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
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_FileChecksum;
|
|
return true;
|
|
}
|
|
|
|
_u32 read = metadata_file->Read(buf, static_cast<_u32>(read_bytes));
|
|
if(read!=read_bytes)
|
|
{
|
|
std::string msg;
|
|
Server->Log(msg, LL_ERROR);
|
|
errpipe->Write(msg);
|
|
memset(buf + read, 0, read_bytes - read);
|
|
}
|
|
|
|
curr_checksum = urb_adler32(curr_checksum, buf, static_cast<_u32>(read_bytes));
|
|
|
|
metadata_file_size-=read_bytes;
|
|
|
|
if(read_bytes<buf_avail)
|
|
{
|
|
fn_off=0;
|
|
metadata_state = MetadataState_File;
|
|
}
|
|
return true;
|
|
}
|
|
else if(metadata_state == MetadataState_FileChecksum)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, sizeof(unsigned int) - fn_off);
|
|
unsigned int endian_curr_checksum = little_endian(curr_checksum);
|
|
memcpy(buf, reinterpret_cast<char*>(&endian_curr_checksum) + fn_off, read_bytes);
|
|
fn_off+=read_bytes;
|
|
|
|
if(fn_off==sizeof(unsigned int))
|
|
{
|
|
metadata_file.reset();
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
metadata_state = MetadataState_Wait;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (metadata_state == MetadataState_Raw)
|
|
{
|
|
if (raw_metadata.size() - metadata_buffer_off > 0)
|
|
{
|
|
read_bytes = (std::min)(raw_metadata.size() - metadata_buffer_off, buf_avail);
|
|
memcpy(buf, raw_metadata.data() + metadata_buffer_off, read_bytes);
|
|
metadata_buffer_off += read_bytes;
|
|
}
|
|
if (raw_metadata.size() - metadata_buffer_off == 0)
|
|
{
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
metadata_state = MetadataState_Wait;
|
|
}
|
|
return true;
|
|
}
|
|
else if (metadata_state == MetadataState_RawFileFnSize)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, sizeof(unsigned int) - fn_off);
|
|
unsigned int fn_size = little_endian(static_cast<unsigned int>(public_fn.size()));
|
|
fn_size = little_endian(fn_size);
|
|
memcpy(buf, reinterpret_cast<char*>(&fn_size) + fn_off, read_bytes);
|
|
curr_checksum = urb_adler32(curr_checksum, reinterpret_cast<char*>(&fn_size) + fn_off, static_cast<_u32>(read_bytes));
|
|
fn_off += read_bytes;
|
|
|
|
if (fn_off == sizeof(unsigned int))
|
|
{
|
|
fn_off = 0;
|
|
metadata_state = MetadataState_RawFileFn;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (metadata_state == MetadataState_RawFileFn)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, public_fn.size() - fn_off);
|
|
memcpy(buf, public_fn.data() + fn_off, read_bytes);
|
|
curr_checksum = urb_adler32(curr_checksum, public_fn.data() + fn_off, static_cast<_u32>(read_bytes));
|
|
fn_off += read_bytes;
|
|
|
|
if (fn_off == public_fn.size())
|
|
{
|
|
fn_off = 0;
|
|
metadata_state = MetadataState_RawFileFnChecksum;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (metadata_state == MetadataState_RawFileFnChecksum)
|
|
{
|
|
read_bytes = (std::min)(buf_avail, sizeof(unsigned int) - fn_off);
|
|
unsigned int endian_curr_checksum = little_endian(curr_checksum);
|
|
memcpy(buf, reinterpret_cast<char*>(&endian_curr_checksum) + fn_off, read_bytes);
|
|
fn_off += read_bytes;
|
|
|
|
if (fn_off == sizeof(unsigned int))
|
|
{
|
|
int64* datasize = reinterpret_cast<int64*>(metadata_buffer.data());
|
|
metadata_file_size = transmit_file->Size();
|
|
metadata_file_off = 0;
|
|
*datasize = little_endian(metadata_file_size);
|
|
metadata_buffer_size = sizeof(int64);
|
|
metadata_buffer_off = 0;
|
|
metadata_state = MetadataState_RawFileDataSize;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (metadata_state == MetadataState_RawFileDataSize)
|
|
{
|
|
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;
|
|
curr_checksum = urb_adler32(0, nullptr, 0);
|
|
metadata_state = MetadataState_RawFileData;
|
|
sha512_init(&transmit_file_ctx);
|
|
}
|
|
return true;
|
|
}
|
|
else if (metadata_state == MetadataState_RawFileData)
|
|
{
|
|
_u32 max_read = static_cast<_u32>((std::min)(metadata_file_size-metadata_file_off, (std::min)((int64)32768, static_cast<int64>(buf_avail))));
|
|
|
|
bool has_read_error = false;
|
|
read_bytes = transmit_file->Read(metadata_file_off, buf, max_read, &has_read_error);
|
|
|
|
sha512_update(&transmit_file_ctx, reinterpret_cast<unsigned char*>(buf), static_cast<_u32>(read_bytes));
|
|
|
|
if (has_read_error)
|
|
{
|
|
Server->Log("Error reading from file " + transmit_file->getFilename() + ". " + os_last_error_str(), LL_WARNING);
|
|
return false;
|
|
}
|
|
|
|
metadata_file_off += read_bytes;
|
|
|
|
if (metadata_file_off == metadata_file_size)
|
|
{
|
|
metadata_state = MetadataState_RawFileDataChecksum;
|
|
sha512_final(&transmit_file_ctx, reinterpret_cast<unsigned char*>(metadata_buffer.data()));
|
|
metadata_buffer_size = SHA512_DIGEST_SIZE;
|
|
metadata_buffer_off = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (metadata_state == MetadataState_RawFileDataChecksum)
|
|
{
|
|
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)
|
|
{
|
|
transmit_wait_pipe->Write(std::string());
|
|
transmit_wait_pipe = nullptr;
|
|
transmit_file = nullptr;
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
metadata_state = MetadataState_Wait;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
while(true)
|
|
{
|
|
std::string msg;
|
|
size_t r = pipe->Read(&msg, 60000);
|
|
|
|
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);
|
|
|
|
char id;
|
|
if (msg_data.getChar(&id))
|
|
{
|
|
if (id == METADATA_PIPE_SEND_FILE &&
|
|
msg_data.getStr(&public_fn) &&
|
|
msg_data.getStr(&local_fn) &&
|
|
msg_data.getInt64(&folder_items) &&
|
|
msg_data.getInt64(&metadata_id) &&
|
|
msg_data.getStr(&server_token) &&
|
|
msg_data.getInt64(&active_gen))
|
|
{
|
|
assert(!public_fn.empty() || !local_fn.empty());
|
|
|
|
if (std::find(last_public_fns.begin(), last_public_fns.end(), public_fn) != last_public_fns.end())
|
|
{
|
|
PipeSessions::fileMetadataDone(public_fn, server_token, active_gen);
|
|
*buf = ID_METADATA_NOP;
|
|
read_bytes = 1;
|
|
return true;
|
|
}
|
|
|
|
last_public_fns.push_back(public_fn);
|
|
|
|
if (last_public_fns.size() > 10)
|
|
{
|
|
last_public_fns.pop_front();
|
|
}
|
|
|
|
int file_type_flags = os_get_file_type(os_file_prefix(local_fn));
|
|
|
|
if (file_type_flags == 0)
|
|
{
|
|
Server->Log("Error getting file type of " + local_fn+". "+os_last_error_str(), LL_ERROR);
|
|
*buf = ID_METADATA_NOP;
|
|
read_bytes = 1;
|
|
PipeSessions::fileMetadataDone(public_fn, server_token, active_gen);
|
|
return true;
|
|
}
|
|
|
|
if (!msg_data.getVoidPtr(reinterpret_cast<void**>(&callback)))
|
|
{
|
|
callback = nullptr;
|
|
}
|
|
|
|
if (callback==nullptr && !openFileHandle() )
|
|
{
|
|
Server->Log("Error opening file handle to " + local_fn+". "+os_last_error_str(), LL_ERROR);
|
|
*buf = ID_METADATA_NOP;
|
|
read_bytes = 1;
|
|
PipeSessions::fileMetadataDone(public_fn, server_token, active_gen);
|
|
return true;
|
|
}
|
|
|
|
std::string file_type;
|
|
if ((file_type_flags & EFileType_Directory)
|
|
&& (file_type_flags & EFileType_Symlink))
|
|
{
|
|
file_type = "l";
|
|
}
|
|
else if (file_type_flags & EFileType_Directory)
|
|
{
|
|
file_type = "d";
|
|
}
|
|
else
|
|
{
|
|
file_type = "f";
|
|
}
|
|
|
|
public_fn = file_type + public_fn;
|
|
|
|
metadata_state = MetadataState_FnSize;
|
|
*buf = ID_METADATA_V1;
|
|
read_bytes = 1;
|
|
fn_off = 0;
|
|
curr_checksum = urb_adler32(0, nullptr, 0);
|
|
|
|
if(callback!=nullptr)
|
|
{
|
|
std::string orig_path;
|
|
_u32 version = 0;
|
|
metadata_file.reset(callback->getMetadata(public_fn, &orig_path, &metadata_file_off, &metadata_file_size, &version, false));
|
|
|
|
if (metadata_file.get() == nullptr)
|
|
{
|
|
Server->Log("Error opening metadata file for \"" + public_fn + "\"", LL_ERROR);
|
|
errpipe->Write("Error opening metadata file for \"" + public_fn + "\"");
|
|
|
|
*buf = ID_METADATA_NOP;
|
|
read_bytes = 1;
|
|
metadata_state = MetadataState_Wait;
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
return true;
|
|
}
|
|
|
|
if (version != 0)
|
|
{
|
|
*buf = version;
|
|
}
|
|
public_fn = file_type + orig_path;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else if (id == METADATA_PIPE_SEND_RAW
|
|
&& msg_data.getStr(&public_fn)
|
|
&& msg_data.getStr(&raw_metadata)
|
|
&& msg_data.getStr(&server_token)
|
|
&& msg_data.getInt64(&active_gen))
|
|
{
|
|
metadata_state = MetadataState_Raw;
|
|
metadata_buffer_off = 0;
|
|
return readStdoutIntoBuffer(buf, buf_avail, read_bytes);
|
|
}
|
|
else if (id == METADATA_PIPE_SEND_RAW_FILEDATA
|
|
&& msg_data.getStr(&public_fn)
|
|
&& msg_data.getVoidPtr(reinterpret_cast<void**>(&transmit_file))
|
|
&& msg_data.getVoidPtr(reinterpret_cast<void**>(&transmit_wait_pipe))
|
|
&& msg_data.getStr(&server_token)
|
|
&& msg_data.getInt64(&active_gen) )
|
|
{
|
|
metadata_state = MetadataState_RawFileFnSize;
|
|
*buf = ID_RAW_FILE;
|
|
read_bytes = 1;
|
|
fn_off = 0;
|
|
curr_checksum = urb_adler32(0, nullptr, 0);
|
|
|
|
return true;
|
|
}
|
|
else if (id == METADATA_PIPE_EXIT)
|
|
{
|
|
errpipe->shutdown();
|
|
read_bytes = 0;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Unknown metadata pipe id: " + convert((int)id), LL_ERROR);
|
|
assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Cannot get metadata pipe id", LL_ERROR);
|
|
assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileMetadataPipe::finishStdout()
|
|
{
|
|
token_callback.reset();
|
|
}
|
|
|
|
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(errpipe->hasError())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileMetadataPipe::cleanupOnForceShutdown()
|
|
{
|
|
if (metadata_state != MetadataState_Wait)
|
|
{
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
}
|
|
|
|
metadata_file.reset();
|
|
|
|
#ifdef _WIN32
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (backup_read_context != NULL)
|
|
{
|
|
BackupRead(hFile, NULL, 0, NULL, TRUE, TRUE, &backup_read_context);
|
|
backup_read_context = NULL;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
#endif
|
|
|
|
while (true)
|
|
{
|
|
std::string msg;
|
|
size_t r = pipe->Read(&msg, 0);
|
|
|
|
if (r == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
CRData msg_data(&msg);
|
|
|
|
char id;
|
|
if (msg_data.getChar(&id))
|
|
{
|
|
if (id == METADATA_PIPE_SEND_FILE &&
|
|
msg_data.getStr(&public_fn) &&
|
|
msg_data.getStr(&local_fn) &&
|
|
msg_data.getInt64(&folder_items) &&
|
|
msg_data.getInt64(&metadata_id) &&
|
|
msg_data.getStr(&server_token) &&
|
|
msg_data.getInt64(&active_gen) )
|
|
{
|
|
PipeSessions::fileMetadataDone(public_fn, server_token, active_gen);
|
|
}
|
|
else if (id == METADATA_PIPE_SEND_RAW
|
|
&& msg_data.getStr(&public_fn)
|
|
&& msg_data.getStr(&raw_metadata)
|
|
&& msg_data.getStr(&server_token)
|
|
&& msg_data.getInt64(&active_gen))
|
|
{
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
}
|
|
else if (id == METADATA_PIPE_SEND_RAW_FILEDATA
|
|
&& msg_data.getStr(&public_fn)
|
|
&& msg_data.getVoidPtr(reinterpret_cast<void**>(&transmit_file))
|
|
&& msg_data.getVoidPtr(reinterpret_cast<void**>(&transmit_wait_pipe))
|
|
&& msg_data.getStr(&server_token)
|
|
&& msg_data.getInt64(&active_gen))
|
|
{
|
|
PipeSessions::fileMetadataDone(public_fn.substr(1), server_token, active_gen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FileMetadataPipe::forceExitWait()
|
|
{
|
|
CWData data;
|
|
data.addChar(METADATA_PIPE_EXIT);
|
|
pipe->Write(data.getDataPtr(), data.getDataSize());
|
|
|
|
errpipe->shutdown();
|
|
|
|
waitForExit();
|
|
}
|
|
|
|
IPipe* FileMetadataPipe::getErrPipe()
|
|
{
|
|
return errpipe.get();
|
|
}
|
|
|
|
bool FileMetadataPipe::openOsMetadataFile(const std::string& fn)
|
|
{
|
|
local_fn = fn;
|
|
return openFileHandle();
|
|
}
|
|
|
|
bool FileMetadataPipe::readCurrOsMetadata(char* buf, size_t buf_avail, size_t& read_bytes)
|
|
{
|
|
return transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
}
|
|
|
|
#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(backup_read_state==-1)
|
|
{
|
|
if(hFile==INVALID_HANDLE_VALUE)
|
|
{
|
|
errpipe->Write("Error opening file \""+local_fn+"\" to read metadata. Last error: "+convert((int)GetLastError())+"\n");
|
|
return false;
|
|
}
|
|
|
|
backup_read_state = 0;
|
|
backup_read_context=NULL;
|
|
|
|
FILE_BASIC_INFO file_information;
|
|
if(GetFileInformationByHandleEx(hFile, FileBasicInfo, &file_information, sizeof(file_information))==FALSE)
|
|
{
|
|
errpipe->Write("Error getting file attributes of \""+local_fn+"\". Last error: "+convert((int)GetLastError())+"\n");
|
|
return false;
|
|
}
|
|
|
|
CWData data;
|
|
data.addChar(1);
|
|
data.addUInt(file_information.FileAttributes);
|
|
data.addVarInt(file_information.CreationTime.QuadPart);
|
|
data.addVarInt(file_information.LastAccessTime.QuadPart);
|
|
data.addVarInt(file_information.LastWriteTime.QuadPart);
|
|
data.addVarInt(file_information.ChangeTime.QuadPart);
|
|
|
|
_u32 data_size = little_endian(static_cast<_u32>(data.getDataSize()));
|
|
memcpy(metadata_buffer.data(), &data_size, sizeof(data_size));
|
|
memcpy(metadata_buffer.data()+sizeof(data_size), data.getDataPtr(), data.getDataSize());
|
|
metadata_buffer_size = data.getDataSize()+sizeof(data_size);
|
|
metadata_buffer_off=0;
|
|
return transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
}
|
|
|
|
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: "+convert((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;
|
|
backup_read_state = -1;
|
|
|
|
*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)
|
|
{
|
|
if (3 + read + curr_stream->dwStreamNameSize > metadata_buffer.size())
|
|
{
|
|
metadata_buffer.resize(3 + read + curr_stream->dwStreamNameSize);
|
|
}
|
|
|
|
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: "+convert((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;
|
|
}
|
|
|
|
curr_stream->cStreamName[curr_stream->dwStreamNameSize/sizeof(WCHAR)] = 0;
|
|
|
|
total_read += read;
|
|
|
|
stream_name = Server->ConvertFromWchar(curr_stream->cStreamName);
|
|
}
|
|
else
|
|
{
|
|
stream_name.clear();
|
|
}
|
|
|
|
if(curr_stream->dwStreamId==BACKUP_DATA
|
|
|| curr_stream->dwStreamId==BACKUP_SPARSE_BLOCK)
|
|
{
|
|
//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: "+convert((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: "+convert((int)GetLastError())+"\n");
|
|
*buf=0;
|
|
read_bytes = 1;
|
|
return false;
|
|
}
|
|
|
|
return transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
}
|
|
|
|
if (curr_stream->Size.QuadPart > 0)
|
|
{
|
|
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 \""+stream_name+"\" of file \""+local_fn+"\". Last error: "+convert((int)GetLastError())+"\n");
|
|
memset(buf, 0, toread);
|
|
}
|
|
|
|
read_bytes = read;
|
|
curr_pos+=read;
|
|
|
|
assert(curr_pos <= curr_stream_size.QuadPart);
|
|
|
|
if(curr_pos==curr_stream_size.QuadPart)
|
|
{
|
|
backup_read_state = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FileMetadataPipe::openFileHandle()
|
|
{
|
|
backup_read_context = NULL;
|
|
hFile = CreateFileW(Server->ConvertToWchar(os_file_prefix(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 | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
|
|
|
backup_read_state = -1;
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE
|
|
&& next(local_fn, 0, "\\\\?\\UNC\\"))
|
|
{
|
|
hFile = CreateFileW(Server->ConvertToWchar(os_file_prefix(local_fn)).c_str(), GENERIC_READ| READ_CONTROL, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
|
}
|
|
|
|
return hFile != INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
#else //_WIN32
|
|
|
|
void serialize_stat_buf(const struct stat64& buf, const std::string& symlink_target, CWData& data)
|
|
{
|
|
data.addChar(1);
|
|
data.addVarInt(buf.st_dev);
|
|
data.addVarInt(buf.st_mode);
|
|
data.addVarInt(buf.st_uid);
|
|
data.addVarInt(buf.st_gid);
|
|
data.addVarInt(buf.st_rdev);
|
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
|
data.addVarInt(buf.st_atimespec.tv_sec);
|
|
data.addUInt(buf.st_atimespec.tv_nsec);
|
|
data.addVarInt(buf.st_mtimespec.tv_sec);
|
|
data.addUInt(buf.st_mtimespec.tv_nsec);
|
|
data.addVarInt(buf.st_ctimespec.tv_sec);
|
|
data.addUInt(buf.st_ctimespec.tv_nsec);
|
|
data.addVarInt(buf.st_flags);
|
|
#else
|
|
data.addVarInt(buf.st_atime);
|
|
data.addUInt(buf.st_atim.tv_nsec);
|
|
data.addVarInt(buf.st_mtime);
|
|
data.addUInt(buf.st_mtim.tv_nsec);
|
|
data.addVarInt(buf.st_ctime);
|
|
data.addUInt(buf.st_ctim.tv_nsec);
|
|
data.addVarInt(0);
|
|
#endif
|
|
data.addString(symlink_target);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool get_xattr_keys(const std::string& fn, std::vector<std::string>& keys)
|
|
{
|
|
ssize_t mult = 1;
|
|
while(true)
|
|
{
|
|
ssize_t bufsize;
|
|
bufsize = llistxattr(fn.c_str(), nullptr, 0);
|
|
|
|
if(bufsize==-1)
|
|
{
|
|
if (errno == EOPNOTSUPP)
|
|
{
|
|
return true;
|
|
}
|
|
#ifdef __APPLE__
|
|
if (errno == EPERM)
|
|
{
|
|
//fs object does not support extended attributes
|
|
return true;
|
|
}
|
|
#endif
|
|
Server->Log("Error getting extended attribute list of file "+fn+" errno: "+convert(errno), LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
std::string buf;
|
|
buf.resize(bufsize*mult);
|
|
|
|
bufsize = llistxattr(fn.c_str(), &buf[0], buf.size());
|
|
|
|
if(bufsize==-1 && errno==ERANGE)
|
|
{
|
|
++mult;
|
|
if (mult >= 10)
|
|
{
|
|
Server->Log("Error getting extended attribute list of file " + fn + " errno: " + convert(errno) + " (3)", LL_ERROR);
|
|
return false;
|
|
}
|
|
Server->Log("Extended attribute list size increased. Retrying...", LL_DEBUG);
|
|
continue;
|
|
}
|
|
|
|
if(bufsize==-1)
|
|
{
|
|
Server->Log("Error getting extended attribute list of file "+fn+" errno: "+convert(errno)+" (2)", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
Tokenize(buf, keys, std::string(1, '\0'));
|
|
|
|
for(size_t i=0;i<keys.size();)
|
|
{
|
|
if (keys[i].empty()
|
|
|| keys[i].size()>=UINT_MAX)
|
|
{
|
|
keys.erase(keys.begin() + i);
|
|
continue;
|
|
}
|
|
|
|
unsigned int ksize = static_cast<unsigned int>(keys[i].size());
|
|
ksize = little_endian(ksize);
|
|
|
|
keys[i].insert(0, reinterpret_cast<char*>(&ksize), sizeof(ksize));
|
|
|
|
++i;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool get_xattr(const std::string& fn, const std::string& key, std::string& value)
|
|
{
|
|
while(true)
|
|
{
|
|
ssize_t bufsize;
|
|
bufsize = lgetxattr(fn.c_str(), key.c_str()+sizeof(unsigned int), nullptr, 0);
|
|
|
|
if(bufsize==-1)
|
|
{
|
|
Server->Log("Error getting extended attribute \""+key+"\" of file \""+fn+"\" errno: "+convert(errno), LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
value.resize(bufsize+sizeof(_u32));
|
|
|
|
bufsize = lgetxattr(fn.c_str(), key.c_str()+sizeof(unsigned int), &value[sizeof(_u32)], value.size()-sizeof(_u32));
|
|
|
|
if(bufsize==-1 && errno==ERANGE)
|
|
{
|
|
Server->Log("Extended attribute size increased. Retrying...", LL_DEBUG);
|
|
continue;
|
|
}
|
|
|
|
if(bufsize==-1)
|
|
{
|
|
Server->Log("Error getting extended attribute \""+key+"\" of file \""+fn+"\" errno: "+convert(errno)+" (2)", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(bufsize<value.size()-sizeof(_u32))
|
|
{
|
|
value.resize(bufsize+sizeof(_u32));
|
|
}
|
|
|
|
_u32 vsize=static_cast<_u32>(bufsize);
|
|
vsize=little_endian(vsize);
|
|
|
|
memcpy(&value[0], &vsize, sizeof(vsize));
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FileMetadataPipe::transmitCurrMetadata(char* buf, size_t buf_avail, size_t& read_bytes)
|
|
{
|
|
if(backup_state==BackupState_StatInit)
|
|
{
|
|
CWData data;
|
|
struct stat64 statbuf;
|
|
int rc = lstat64(local_fn.c_str(), &statbuf);
|
|
|
|
if(rc!=0)
|
|
{
|
|
Server->Log("Error with lstat of "+local_fn+" errorcode: "+convert(errno), LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
std::string symlink_target;
|
|
if (S_ISLNK(statbuf.st_mode))
|
|
{
|
|
if (!os_get_symlink_target(local_fn, symlink_target))
|
|
{
|
|
Server->Log("Error getting symlink target of " + local_fn + " errorcode: " + convert(errno), LL_ERROR);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
serialize_stat_buf(statbuf, symlink_target, data);
|
|
|
|
if(data.getDataSize()+sizeof(_u32)>metadata_buffer.size())
|
|
{
|
|
Server->Log("File metadata of "+local_fn+" too large ("+convert((size_t)data.getDataSize()+sizeof(_u32))+")", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
|
|
_u32 metadata_size = little_endian(static_cast<_u32>(data.getDataSize()));
|
|
memcpy(metadata_buffer.data(), &metadata_size, sizeof(_u32));
|
|
memcpy(metadata_buffer.data()+sizeof(_u32), data.getDataPtr(), data.getDataSize());
|
|
metadata_buffer_size=data.getDataSize()+sizeof(_u32);
|
|
metadata_buffer_off=0;
|
|
backup_state=BackupState_Stat;
|
|
|
|
return transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
}
|
|
else if(backup_state==BackupState_Stat)
|
|
{
|
|
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)
|
|
{
|
|
backup_state=BackupState_EAttrInit;
|
|
}
|
|
return true;
|
|
}
|
|
else if(backup_state==BackupState_EAttrInit)
|
|
{
|
|
eattr_keys.clear();
|
|
if(!get_xattr_keys(local_fn, eattr_keys))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CWData data;
|
|
data.addInt64(eattr_keys.size());
|
|
|
|
memcpy(metadata_buffer.data(), data.getDataPtr(), data.getDataSize());
|
|
metadata_buffer_size=data.getDataSize();
|
|
metadata_buffer_off=0;
|
|
backup_state=BackupState_EAttr;
|
|
|
|
return transmitCurrMetadata(buf, buf_avail, read_bytes);
|
|
}
|
|
else if(backup_state==BackupState_EAttr)
|
|
{
|
|
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)
|
|
{
|
|
if(!eattr_keys.empty())
|
|
{
|
|
eattr_idx=0;
|
|
backup_state=BackupState_EAttr_Vals_Key;
|
|
eattr_key_off=0;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else if(backup_state==BackupState_EAttr_Vals_Key)
|
|
{
|
|
if(eattr_keys[eattr_idx].size()-eattr_key_off>0)
|
|
{
|
|
read_bytes = (std::min)(eattr_keys[eattr_idx].size()-eattr_key_off, buf_avail);
|
|
memcpy(buf, eattr_keys[eattr_idx].c_str()+eattr_key_off, read_bytes);
|
|
eattr_key_off+=read_bytes;
|
|
}
|
|
|
|
if(eattr_keys[eattr_idx].size()-eattr_key_off==0)
|
|
{
|
|
if(!get_xattr(local_fn, eattr_keys[eattr_idx], eattr_val))
|
|
{
|
|
eattr_val.resize(sizeof(_u32));
|
|
unsigned int umax = UINT_MAX;
|
|
memcpy(&eattr_val[0], &umax, sizeof(umax));
|
|
}
|
|
backup_state = BackupState_EAttr_Vals_Val;
|
|
eattr_val_off=0;
|
|
}
|
|
return true;
|
|
}
|
|
else if (backup_state == BackupState_EAttr_Vals_Val)
|
|
{
|
|
if (eattr_val.size() - eattr_val_off > 0)
|
|
{
|
|
read_bytes = (std::min)(eattr_val.size() - eattr_val_off, buf_avail);
|
|
memcpy(buf, eattr_val.data() + eattr_val_off, read_bytes);
|
|
eattr_val_off += read_bytes;
|
|
}
|
|
|
|
if (eattr_val.size() - eattr_val_off == 0)
|
|
{
|
|
if (eattr_idx + 1 < eattr_keys.size())
|
|
{
|
|
++eattr_idx;
|
|
backup_state = BackupState_EAttr_Vals_Key;
|
|
eattr_key_off = 0;
|
|
}
|
|
else
|
|
{
|
|
//finished
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FileMetadataPipe::openFileHandle()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#endif //_WIN32
|
|
|