mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
410 lines
9.1 KiB
C++
410 lines
9.1 KiB
C++
#include "SparseFile.h"
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
|
|
namespace
|
|
{
|
|
int64 roundUp(int64 numToRound, int64 multiple)
|
|
{
|
|
return ((numToRound + multiple - 1) / multiple) * multiple;
|
|
}
|
|
|
|
int64 roundDown(int64 numToRound, int64 multiple)
|
|
{
|
|
return ((numToRound / multiple) * multiple);
|
|
}
|
|
}
|
|
|
|
|
|
SparseFile::SparseFile(IFsFile * backing_file, IFile * sparse_extends_f, bool read_only, int64 blocksize, bool take_file_ownership, int64 max_size)
|
|
: backing_file(backing_file), has_error(false), seek_pos(0), backing_pos(0), take_file_ownership(take_file_ownership), max_size(max_size)
|
|
{
|
|
int64 n_extents;
|
|
sparse_extends_f->Seek(0);
|
|
if (sparse_extends_f->Read(reinterpret_cast<char*>(&n_extents), sizeof(n_extents)) != sizeof(n_extents))
|
|
{
|
|
has_error = true;
|
|
return;
|
|
}
|
|
|
|
std::vector<IFsFile::SSparseExtent> sparse_extents;
|
|
sparse_extents.resize(static_cast<size_t>(n_extents));
|
|
|
|
if (sparse_extends_f->Read(reinterpret_cast<char*>(sparse_extents.data()), static_cast<_u32>(sparse_extents.size()*sizeof(IFsFile::SSparseExtent)))
|
|
!= static_cast<_u32>(sparse_extents.size()*sizeof(IFsFile::SSparseExtent)))
|
|
{
|
|
has_error = true;
|
|
return;
|
|
}
|
|
|
|
initWithSparseExtents(sparse_extents, read_only, blocksize);
|
|
}
|
|
|
|
SparseFile::SparseFile(IFsFile * backing_file, const std::vector<IFsFile::SSparseExtent>& sparse_extents, bool read_only, int64 blocksize, bool take_file_ownership, int64 max_size)
|
|
: backing_file(backing_file), has_error(false), seek_pos(0), backing_pos(0), take_file_ownership(take_file_ownership), max_size(max_size)
|
|
{
|
|
initWithSparseExtents(sparse_extents, read_only, blocksize);
|
|
}
|
|
|
|
SparseFile::~SparseFile()
|
|
{
|
|
if (take_file_ownership)
|
|
{
|
|
Server->destroy(backing_file);
|
|
}
|
|
}
|
|
|
|
bool SparseFile::hasError()
|
|
{
|
|
return has_error;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class BackingReadStrOp : public SparseFile::IOrigOp
|
|
{
|
|
public:
|
|
BackingReadStrOp(IFile* backing_file)
|
|
: backing_file(backing_file)
|
|
{}
|
|
|
|
virtual _u32 origOp(_u32 buffer_offset, _u32 max_size, bool * has_error)
|
|
{
|
|
std::string ret_add = backing_file->Read(max_size, has_error);
|
|
ret += ret_add;
|
|
return static_cast<_u32>(ret_add.size());
|
|
}
|
|
|
|
std::string getRet()
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
private:
|
|
std::string ret;
|
|
IFile* backing_file;
|
|
};
|
|
}
|
|
|
|
|
|
std::string SparseFile::Read(int64 spos, _u32 tr, bool * has_error)
|
|
{
|
|
if (!Seek(spos))
|
|
{
|
|
if (has_error) *has_error = true;
|
|
return std::string();
|
|
}
|
|
|
|
return Read(tr, has_error);
|
|
}
|
|
|
|
std::string SparseFile::Read(_u32 tr, bool * has_error)
|
|
{
|
|
BackingReadStrOp read_str_op(backing_file);
|
|
_u32 read = mappedOrigOp(&read_str_op, tr, has_error);
|
|
return read_str_op.getRet();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class BackingReadBufOp : public SparseFile::IOrigOp
|
|
{
|
|
public:
|
|
BackingReadBufOp(IFile* backing_file, char * buffer)
|
|
: backing_file(backing_file), buffer(buffer)
|
|
{}
|
|
|
|
virtual _u32 origOp(_u32 buffer_offset, _u32 max_size, bool * has_error)
|
|
{
|
|
return backing_file->Read(buffer+buffer_offset, max_size, has_error);
|
|
}
|
|
|
|
private:
|
|
char * buffer;
|
|
IFile* backing_file;
|
|
};
|
|
}
|
|
|
|
|
|
_u32 SparseFile::Read(char * buffer, _u32 bsize, bool * has_error)
|
|
{
|
|
BackingReadBufOp read_buf_op(backing_file, buffer);
|
|
return mappedOrigOp(&read_buf_op, bsize, has_error);
|
|
}
|
|
|
|
_u32 SparseFile::Read(int64 spos, char * buffer, _u32 bsize, bool * has_error)
|
|
{
|
|
if (!Seek(spos))
|
|
{
|
|
if (has_error) *has_error = true;
|
|
return 0;
|
|
}
|
|
|
|
return Read(buffer, bsize, has_error);
|
|
}
|
|
|
|
_u32 SparseFile::Write(const std::string & tw, bool * has_error)
|
|
{
|
|
return Write(tw.data(), static_cast<_u32>(tw.size()), has_error);
|
|
}
|
|
|
|
_u32 SparseFile::Write(int64 spos, const std::string & tw, bool * has_error)
|
|
{
|
|
if (!Seek(spos))
|
|
{
|
|
if (has_error) *has_error = true;
|
|
return 0;
|
|
}
|
|
|
|
return Write(tw, has_error);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class BackingWriteBufOp : public SparseFile::IOrigOp
|
|
{
|
|
public:
|
|
BackingWriteBufOp(IFile* backing_file, const char * buffer)
|
|
: backing_file(backing_file), buffer(buffer)
|
|
{}
|
|
|
|
virtual _u32 origOp(_u32 buffer_offset, _u32 max_size, bool * has_error)
|
|
{
|
|
return backing_file->Write(buffer + buffer_offset, max_size, has_error);
|
|
}
|
|
|
|
private:
|
|
const char * buffer;
|
|
IFile* backing_file;
|
|
};
|
|
}
|
|
|
|
_u32 SparseFile::Write(const char * buffer, _u32 bsize, bool * has_error)
|
|
{
|
|
BackingWriteBufOp write_buf_op(backing_file, buffer);
|
|
return mappedOrigOp(&write_buf_op, bsize, has_error);
|
|
}
|
|
|
|
_u32 SparseFile::Write(int64 spos, const char * buffer, _u32 bsize, bool * has_error)
|
|
{
|
|
if (!Seek(spos))
|
|
{
|
|
if (has_error) *has_error = true;
|
|
return 0;
|
|
}
|
|
|
|
return Write(buffer, bsize, has_error);
|
|
}
|
|
|
|
bool SparseFile::Seek(_i64 spos)
|
|
{
|
|
seek_pos = spos;
|
|
backing_pos = mapToBackingOffset(spos);
|
|
return backing_file->Seek(backing_pos);
|
|
}
|
|
|
|
_i64 SparseFile::Size(void)
|
|
{
|
|
return backing_file->Size()-sparse_extents_size;
|
|
}
|
|
|
|
_i64 SparseFile::RealSize()
|
|
{
|
|
return backing_file->Size();
|
|
}
|
|
|
|
bool SparseFile::PunchHole(_i64 spos, _i64 size)
|
|
{
|
|
int64 punch_start = mapToBackingOffset(spos);
|
|
int64 punch_end = mapToBackingOffset(spos + size);
|
|
return backing_file->PunchHole(punch_start, punch_end - punch_start);
|
|
}
|
|
|
|
bool SparseFile::Sync()
|
|
{
|
|
return backing_file->Sync();
|
|
}
|
|
|
|
std::string SparseFile::getFilename(void)
|
|
{
|
|
return backing_file->getFilename();
|
|
}
|
|
|
|
IFsFile::os_file_handle SparseFile::getOsHandle(bool release_handle)
|
|
{
|
|
return backing_file->getOsHandle(release_handle);
|
|
}
|
|
|
|
_i64 SparseFile::getSparseSize()
|
|
{
|
|
return sparse_extents_size;
|
|
}
|
|
|
|
void SparseFile::initWithSparseExtents(const std::vector<IFsFile::SSparseExtent>& sparse_extents, bool read_only, int64 blocksize)
|
|
{
|
|
int64 fpos = 0;
|
|
int64 last_offset = 0;
|
|
int64 max_offset = 0;
|
|
sparse_extents_size = 0;
|
|
for (size_t i = 0; i < sparse_extents.size(); ++i)
|
|
{
|
|
IFsFile::SSparseExtent curr_extent = sparse_extents[i];
|
|
|
|
int64 offset = roundUp(curr_extent.offset, blocksize);
|
|
int64 offset_end = roundDown(curr_extent.offset + curr_extent.size, blocksize);
|
|
|
|
if (offset < offset_end)
|
|
{
|
|
int64 extent_size = offset_end - offset;
|
|
|
|
fpos += offset - last_offset;
|
|
last_offset = offset + extent_size;
|
|
|
|
sparse_extents_size += extent_size;
|
|
|
|
sparse_offsets.push_back(SPosMap(fpos, offset + extent_size));
|
|
|
|
max_offset = (std::max)(max_offset, curr_extent.offset + curr_extent.size);
|
|
}
|
|
}
|
|
|
|
if (max_size > 0)
|
|
{
|
|
max_offset = (std::min)(max_size, max_offset);
|
|
}
|
|
|
|
if (!sparse_offsets.empty() && sparse_offsets[0].offset == 0)
|
|
{
|
|
backing_pos = sparse_offsets[0].backing_offset;
|
|
}
|
|
|
|
if (max_offset > backing_file->Size())
|
|
{
|
|
if (!backing_file->Resize(max_offset))
|
|
{
|
|
has_error = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!backing_file->Seek(backing_pos))
|
|
{
|
|
has_error = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
SparseFile::SPosMap SparseFile::nextBackingOffset()
|
|
{
|
|
std::vector<SPosMap>::iterator it = std::upper_bound(sparse_offsets.begin(), sparse_offsets.end(), SPosMap(seek_pos, -1));
|
|
if (it != sparse_offsets.end())
|
|
{
|
|
return *it;
|
|
}
|
|
else
|
|
{
|
|
return SPosMap();
|
|
}
|
|
}
|
|
|
|
SparseFile::SPosMap SparseFile::lastBackingOffset(int64 offset)
|
|
{
|
|
std::vector<SPosMap>::iterator it = std::upper_bound(sparse_offsets.begin(), sparse_offsets.end(), SPosMap(offset, -1));
|
|
if (it != sparse_offsets.begin())
|
|
{
|
|
--it;
|
|
return *it;
|
|
}
|
|
else
|
|
{
|
|
return SPosMap();
|
|
}
|
|
}
|
|
|
|
_u32 SparseFile::mappedOrigOp(IOrigOp * orig_op, _u32 op_size, bool * has_error)
|
|
{
|
|
_u32 op_size_orig = op_size;
|
|
while (op_size > 0)
|
|
{
|
|
SPosMap next = nextBackingOffset();
|
|
_u32 max_size = static_cast<_u32>(op_size);
|
|
if (next.offset != -1)
|
|
{
|
|
max_size = static_cast<_u32>((std::min)(static_cast<int64>(op_size), next.offset - seek_pos));
|
|
}
|
|
|
|
_u32 buffer_offset = op_size_orig - op_size;
|
|
|
|
_u32 backing_op_size = orig_op->origOp(buffer_offset, max_size, has_error);
|
|
|
|
seek_pos += backing_op_size;
|
|
op_size -= backing_op_size;
|
|
|
|
if (has_error != NULL && *has_error)
|
|
{
|
|
return op_size_orig - op_size;
|
|
}
|
|
|
|
if (seek_pos == next.offset)
|
|
{
|
|
backing_pos = next.backing_offset;
|
|
if (!backing_file->Seek(backing_pos))
|
|
{
|
|
if (has_error != NULL)
|
|
{
|
|
*has_error = true;
|
|
}
|
|
return op_size_orig - op_size;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return op_size_orig - op_size;
|
|
}
|
|
|
|
IVdlVolCache* SparseFile::createVdlVolCache()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int64 SparseFile::getValidDataLength(IVdlVolCache* vol_cache)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int64 SparseFile::mapToBackingOffset(int64 offset)
|
|
{
|
|
SPosMap last = lastBackingOffset(offset);
|
|
if (last.offset == -1)
|
|
{
|
|
return offset;
|
|
}
|
|
else
|
|
{
|
|
last.backing_offset += offset - last.offset;
|
|
return last.backing_offset;
|
|
}
|
|
}
|
|
|
|
void SparseFile::resetSparseExtentIter()
|
|
{
|
|
}
|
|
|
|
IFsFile::SSparseExtent SparseFile::nextSparseExtent()
|
|
{
|
|
return SSparseExtent();
|
|
}
|
|
|
|
bool SparseFile::Resize(int64 new_size, bool set_sparse)
|
|
{
|
|
return backing_file->Resize(mapToBackingOffset(new_size), set_sparse);
|
|
}
|
|
|
|
std::vector<IFsFile::SFileExtent> SparseFile::getFileExtents(int64 starting_offset, int64 block_size, bool & more_data, unsigned int flags)
|
|
{
|
|
return std::vector<SFileExtent>();
|
|
}
|