mirror of
https://github.com/usatiuk/backup.git
synced 2025-10-26 17:37:47 +01:00
Compare commits
22 Commits
3ef8c796a4
...
cleaning
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c1e9fe4bf | |||
| 1dad7d1501 | |||
| 0b2040603e | |||
| 476904b5aa | |||
| 061e164c6a | |||
| e996e93431 | |||
| 8e9ed2b715 | |||
| 0095f9ff69 | |||
| a14eb1c501 | |||
| 5ea76e566e | |||
| 275f1208c5 | |||
| d424adc389 | |||
| 1f72814b99 | |||
| 8f63891aee | |||
| b3123bb70d | |||
| 77cb49a32c | |||
| e0ea1c1ce9 | |||
| c838ac0238 | |||
| 507f4d0dc0 | |||
| 57f2f331ec | |||
| 53f908f05f | |||
| 802c2d70e0 |
@@ -34,7 +34,7 @@ BreakBeforeBinaryOperators: None
|
|||||||
BreakBeforeTernaryOperators: true
|
BreakBeforeTernaryOperators: true
|
||||||
BreakConstructorInitializers: BeforeColon
|
BreakConstructorInitializers: BeforeColon
|
||||||
BreakInheritanceList: BeforeColon
|
BreakInheritanceList: BeforeColon
|
||||||
ColumnLimit: 0
|
ColumnLimit: 120
|
||||||
CompactNamespaces: true
|
CompactNamespaces: true
|
||||||
ContinuationIndentWidth: 8
|
ContinuationIndentWidth: 8
|
||||||
IndentCaseLabels: true
|
IndentCaseLabels: true
|
||||||
|
|||||||
10
.github/workflows/cmake.yml
vendored
10
.github/workflows/cmake.yml
vendored
@@ -21,8 +21,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- run: apt-get update && apt-get install -y sudo
|
||||||
|
if: env.ACT=='true'
|
||||||
|
|
||||||
- name: install everything
|
- name: install everything
|
||||||
run: sudo apt-get install -y fuse libfuse-dev
|
run: sudo apt-get update && sudo apt-get install -y fuse libfuse-dev cmake build-essential gcc g++ libssl-dev zlib1g-dev
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||||
@@ -31,10 +34,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
# Build your program with the given configuration
|
# Build your program with the given configuration
|
||||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(( $(nproc) - 2 ))
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
working-directory: ${{github.workspace}}/build
|
|
||||||
# Execute tests defined by the CMake configuration.
|
# Execute tests defined by the CMake configuration.
|
||||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||||
run: ctest --verbose --parallel $(nproc) -C ${{env.BUILD_TYPE}}
|
run: cd ${{github.workspace}}/build && ctest --test-dir ${{github.workspace}}/build --verbose --parallel $(nproc) -C ${{env.BUILD_TYPE}}
|
||||||
|
|||||||
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@@ -1,4 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
<component name="CidrRootsConfiguration">
|
||||||
|
<excludeRoots>
|
||||||
|
<file path="$PROJECT_DIR$/cmake-build-debug" />
|
||||||
|
<file path="$PROJECT_DIR$/cmake-build-relwithdebinfo" />
|
||||||
|
<file path="$PROJECT_DIR$/cmake-build-relwithsan" />
|
||||||
|
</excludeRoots>
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
project(backup)
|
project(backup)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
add_subdirectory(change_detectors)
|
add_subdirectory(change_detectors)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_library(change_detectors srcs/ChangeDetector.cpp srcs/ChangeDetectorContainer.cpp srcs/ChangeDetectorFactory.cpp srcs/ComparableFile.cpp srcs/ContentsChangeDetector.cpp srcs/EditTimeChangeDetector.cpp srcs/SizeChangeDetector.cpp srcs/TypeChangeDetector.cpp)
|
add_library(change_detectors srcs/ChangeDetector.cpp srcs/ChangeDetectorContainer.cpp srcs/ChangeDetectorFactory.cpp srcs/ComparableFile.cpp srcs/ContentsChangeDetector.cpp srcs/EditTimeChangeDetector.cpp srcs/SizeChangeDetector.cpp srcs/TypeChangeDetector.cpp)
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,12 @@ struct ComparableFile {
|
|||||||
/// \param base Constant reference to a base path against which #path will be set
|
/// \param base Constant reference to a base path against which #path will be set
|
||||||
ComparableFile(const std::filesystem::path &p, const std::filesystem::path &base);
|
ComparableFile(const std::filesystem::path &p, const std::filesystem::path &base);
|
||||||
|
|
||||||
const std::string path; ///< Relative path to the file
|
const std::string path; ///< Relative path to the file
|
||||||
const File::Type type; ///< File type
|
const File::Type type; ///< File type
|
||||||
const unsigned long long bytes; ///< Number of bytes in the file
|
const unsigned long long bytes;///< Number of bytes in the file
|
||||||
const unsigned long long mtime; ///< Timestamp of last file modification
|
const unsigned long long mtime;///< Timestamp of last file modification
|
||||||
const std::function<std::unique_ptr<std::streambuf>()> contents;///< Function that returns a unique pointer to a std::streambuf instance linked to the contents of the file
|
const std::function<std::unique_ptr<std::streambuf>()>
|
||||||
|
contents;///< Function that returns a unique pointer to a std::streambuf instance linked to the contents of the file
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,8 @@
|
|||||||
|
|
||||||
bool ChangeDetectorContainer::check(const ComparableFile &f1, const ComparableFile &f2) const {
|
bool ChangeDetectorContainer::check(const ComparableFile &f1, const ComparableFile &f2) const {
|
||||||
return std::any_of(changeDetectors.begin(), changeDetectors.end(),
|
return std::any_of(changeDetectors.begin(), changeDetectors.end(),
|
||||||
[&](const auto &changeDetector) {
|
[&](const auto &changeDetector) { return changeDetector->check(f1, f2); });
|
||||||
return changeDetector->check(f1, f2);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangeDetectorContainer::ChangeDetectorContainer(std::vector<std::unique_ptr<ChangeDetector>> &&changeDetectors) : changeDetectors(std::move(changeDetectors)) {}
|
ChangeDetectorContainer::ChangeDetectorContainer(std::vector<std::unique_ptr<ChangeDetector>> &&changeDetectors)
|
||||||
|
: changeDetectors(std::move(changeDetectors)) {}
|
||||||
|
|||||||
@@ -12,31 +12,22 @@
|
|||||||
#include "objects/FileBuffer.h"
|
#include "objects/FileBuffer.h"
|
||||||
|
|
||||||
ComparableFile::ComparableFile(const File &file, const Repository *repo)
|
ComparableFile::ComparableFile(const File &file, const Repository *repo)
|
||||||
: path(file.name),
|
: path(file.name), type(file.fileType), bytes(file.bytes), mtime(file.mtime),
|
||||||
type(file.fileType),
|
contents([file, repo]() { return std::make_unique<FileBuffer>(repo, file.id); }) {}
|
||||||
bytes(file.bytes),
|
|
||||||
mtime(file.mtime),
|
|
||||||
contents(
|
|
||||||
[file, repo]() {
|
|
||||||
return std::make_unique<FileBuffer>(repo, file.id);
|
|
||||||
}) {}
|
|
||||||
|
|
||||||
ComparableFile::ComparableFile(const std::filesystem::path &p, const std::filesystem::path &base)
|
ComparableFile::ComparableFile(const std::filesystem::path &p, const std::filesystem::path &base)
|
||||||
: path(p.lexically_relative(base).u8string()),
|
: path(p.lexically_relative(base).string()), type(File::getFileType(p)), bytes(File::getFileSize(p)),
|
||||||
type(File::getFileType(p)),
|
|
||||||
bytes(File::getFileSize(p)),
|
|
||||||
mtime(File::getFileMtime(p)),
|
mtime(File::getFileMtime(p)),
|
||||||
contents(
|
contents([p, path = this->path, type = this->type]() -> std::unique_ptr<std::streambuf> {
|
||||||
[p, path = this->path, type = this->type]() -> std::unique_ptr<std::streambuf> {
|
if (type == File::Type::Normal) {
|
||||||
if (type == File::Type::Normal) {
|
auto fb = std::make_unique<std::filebuf>();
|
||||||
auto fb = std::make_unique<std::filebuf>();
|
fb->open(p, std::ios::in | std::ios::binary);
|
||||||
fb->open(p, std::ios::in | std::ios::binary);
|
if (!fb->is_open()) throw Exception("Can't open " + p.string() + " for reading!");
|
||||||
if (!fb->is_open()) throw Exception("Can't open " + p.u8string() + " for reading!");
|
return fb;
|
||||||
return fb;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto contentsVector = File::getFileContents(p);
|
auto contentsVector = File::getFileContents(p);
|
||||||
std::string contents = {contentsVector.begin(), contentsVector.end()};
|
std::string contents = {contentsVector.begin(), contentsVector.end()};
|
||||||
|
|
||||||
return std::make_unique<std::stringbuf>(contents, std::ios::in | std::ios::binary);
|
return std::make_unique<std::stringbuf>(contents, std::ios::in | std::ios::binary);
|
||||||
}) {}
|
}) {}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ bool ContentsChangeDetector::check(const ComparableFile &f1, const ComparableFil
|
|||||||
auto b1 = f1.contents();
|
auto b1 = f1.contents();
|
||||||
auto b2 = f2.contents();
|
auto b2 = f2.contents();
|
||||||
|
|
||||||
return !std::equal(std::istreambuf_iterator<char>(b1.get()),
|
return !std::equal(std::istreambuf_iterator<char>(b1.get()), std::istreambuf_iterator<char>(),
|
||||||
std::istreambuf_iterator<char>(),
|
std::istreambuf_iterator<char>(b2.get()), std::istreambuf_iterator<char>());
|
||||||
std::istreambuf_iterator<char>(b2.get()),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,4 @@
|
|||||||
|
|
||||||
#include "TypeChangeDetector.h"
|
#include "TypeChangeDetector.h"
|
||||||
|
|
||||||
bool TypeChangeDetector::check(const ComparableFile &f1, const ComparableFile &f2) const {
|
bool TypeChangeDetector::check(const ComparableFile &f1, const ComparableFile &f2) const { return f1.type != f2.type; }
|
||||||
return f1.type != f2.type;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_library(chunkers srcs/Buzhash.cpp srcs/BuzhashChunker.cpp srcs/Chunker.cpp srcs/ChunkerFactory.cpp srcs/ConstChunker.cpp)
|
add_library(chunkers srcs/Buzhash.cpp srcs/BuzhashChunker.cpp srcs/Chunker.cpp srcs/ChunkerFactory.cpp srcs/ConstChunker.cpp)
|
||||||
|
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ public:
|
|||||||
uint32_t feed(uint8_t in);
|
uint32_t feed(uint8_t in);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t cur = 0; ///< Current hash value
|
uint32_t cur = 0; ///< Current hash value
|
||||||
const uint32_t blockSize; ///< Hashing window size
|
const uint32_t blockSize;///< Hashing window size
|
||||||
std::deque<uint32_t> history;///< Bytes used to calculate current hash, used to compute the hash in a rolling fashion (to remove the oldest byte from the hash when blockSize is reached)
|
std::deque<uint32_t>
|
||||||
|
history;///< Bytes used to calculate current hash, used to compute the hash in a rolling fashion (to remove the oldest byte from the hash when blockSize is reached)
|
||||||
|
|
||||||
// Circular shift taken from: https://en.wikipedia.org/wiki/Circular_shift
|
// Circular shift taken from: https://en.wikipedia.org/wiki/Circular_shift
|
||||||
/// Shift \p value \p count bits to the right circularly
|
/// Shift \p value \p count bits to the right circularly
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ public:
|
|||||||
/// \param minBytes Minimum amount of bytes in returned chunks
|
/// \param minBytes Minimum amount of bytes in returned chunks
|
||||||
/// \param mask Amount of trailing zeroes in the rolling hash at which the file is cut (results in average chunk size of 2^mask bytes)
|
/// \param mask Amount of trailing zeroes in the rolling hash at which the file is cut (results in average chunk size of 2^mask bytes)
|
||||||
/// \param window Rolling hash window (how many of chunks last bytes are included in the hash, the default is recommended)
|
/// \param window Rolling hash window (how many of chunks last bytes are included in the hash, the default is recommended)
|
||||||
BuzhashChunker(std::streambuf *buf, unsigned long long minBytes, unsigned long long maxBytes, unsigned long long mask, uint32_t window = 4095);
|
BuzhashChunker(std::streambuf *buf, unsigned long long minBytes, unsigned long long maxBytes,
|
||||||
|
unsigned long long mask, uint32_t window = 4095);
|
||||||
|
|
||||||
/// \copydoc Chunker::getNext
|
/// \copydoc Chunker::getNext
|
||||||
std::pair<std::string, std::vector<char>> getNext() override;
|
std::pair<std::string, std::vector<char>> getNext() override;
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
|
|
||||||
Buzhash::Buzhash(uint32_t blockSize) : blockSize(blockSize), history() {}
|
Buzhash::Buzhash(uint32_t blockSize) : blockSize(blockSize), history() {}
|
||||||
|
|
||||||
uint32_t Buzhash::get() const {
|
uint32_t Buzhash::get() const { return cur; }
|
||||||
return cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Buzhash::feed(uint8_t in) {
|
uint32_t Buzhash::feed(uint8_t in) {
|
||||||
cur = rotr32(cur, 1);
|
cur = rotr32(cur, 1);
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "SHA.h"
|
#include "SHA.h"
|
||||||
|
|
||||||
BuzhashChunker::BuzhashChunker(std::streambuf *buf, unsigned long long minBytes, unsigned long long maxBytes, unsigned long long mask, uint32_t window) : Chunker(buf, maxBytes), window(window), minBytes(minBytes), mask(mask), buzhash(window) {}
|
BuzhashChunker::BuzhashChunker(std::streambuf *buf, unsigned long long minBytes, unsigned long long maxBytes,
|
||||||
|
unsigned long long mask, uint32_t window)
|
||||||
|
: Chunker(buf, maxBytes), window(window), minBytes(minBytes), mask(mask), buzhash(window) {}
|
||||||
|
|
||||||
std::pair<std::string, std::vector<char>> BuzhashChunker::getNext() {
|
std::pair<std::string, std::vector<char>> BuzhashChunker::getNext() {
|
||||||
if (eof) throw Exception("Trying to read from a file that is finished!");
|
if (eof) throw Exception("Trying to read from a file that is finished!");
|
||||||
@@ -21,9 +23,7 @@ std::pair<std::string, std::vector<char>> BuzhashChunker::getNext() {
|
|||||||
return {SHA::calculate(rbuf), rbuf};
|
return {SHA::calculate(rbuf), rbuf};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto c: rbuf) {
|
for (auto c: rbuf) { buzhash.feed(static_cast<uint8_t>(c)); }
|
||||||
buzhash.feed(static_cast<uint8_t>(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue reading the file until either the last mask bits are zero of we exceed the maxSize
|
// Continue reading the file until either the last mask bits are zero of we exceed the maxSize
|
||||||
while (((buzhash.get() & (~0UL >> (sizeof(unsigned long long) * 8 - mask))) != 0) && rbuf.size() < maxBytes) {
|
while (((buzhash.get() & (~0UL >> (sizeof(unsigned long long) * 8 - mask))) != 0) && rbuf.size() < maxBytes) {
|
||||||
|
|||||||
@@ -8,44 +8,31 @@
|
|||||||
|
|
||||||
Chunker::Chunker(std::streambuf *buf, unsigned long long maxBytes) : buf(buf), maxBytes(maxBytes) {}
|
Chunker::Chunker(std::streambuf *buf, unsigned long long maxBytes) : buf(buf), maxBytes(maxBytes) {}
|
||||||
|
|
||||||
bool Chunker::getEof() const {
|
bool Chunker::getEof() const { return eof; }
|
||||||
return eof;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunker::~Chunker() = default;
|
Chunker::~Chunker() = default;
|
||||||
|
|
||||||
Chunker::ChunkerIterator Chunker::begin() {
|
Chunker::ChunkerIterator Chunker::begin() { return {this}; }
|
||||||
return {this};
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunker::ChunkerIterator Chunker::end() {
|
Chunker::ChunkerIterator Chunker::end() { return {nullptr}; }
|
||||||
return {nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunker::ChunkerIterator &Chunker::ChunkerIterator::operator++() {
|
Chunker::ChunkerIterator &Chunker::ChunkerIterator::operator++() {
|
||||||
if (pastEOF) throw Exception("Trying to increment pastEOF ChunkerIterator!");
|
if (pastEOF) throw Exception("Trying to increment pastEOF ChunkerIterator!");
|
||||||
if (source->getEof())
|
if (source->getEof()) pastEOF = true;
|
||||||
pastEOF = true;
|
|
||||||
else
|
else
|
||||||
buf = source->getNext();
|
buf = source->getNext();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunker::ChunkerIterator::operator!=(const Chunker::ChunkerIterator &rhs) const {
|
bool Chunker::ChunkerIterator::operator!=(const Chunker::ChunkerIterator &rhs) const { return pastEOF != rhs.pastEOF; }
|
||||||
return pastEOF != rhs.pastEOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunker::ChunkerIterator::value_type Chunker::ChunkerIterator::operator*() const {
|
Chunker::ChunkerIterator::value_type Chunker::ChunkerIterator::operator*() const {
|
||||||
if (pastEOF) throw Exception("Trying to dereference pastEOF ChunkerIterator!");
|
if (pastEOF) throw Exception("Trying to dereference pastEOF ChunkerIterator!");
|
||||||
return buf.value();
|
return buf.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunker::ChunkerIterator::operator==(const Chunker::ChunkerIterator &rhs) const {
|
bool Chunker::ChunkerIterator::operator==(const Chunker::ChunkerIterator &rhs) const { return pastEOF == rhs.pastEOF; }
|
||||||
return pastEOF == rhs.pastEOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
Chunker::ChunkerIterator::ChunkerIterator(Chunker *source)
|
Chunker::ChunkerIterator::ChunkerIterator(Chunker *source) : source(source), pastEOF(source == nullptr) {
|
||||||
: source(source), pastEOF(source == nullptr) {
|
if (source) operator++();
|
||||||
if (source)
|
|
||||||
operator++();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ std::unique_ptr<Chunker> ChunkerFactory::getChunker(const Config &config, std::s
|
|||||||
if (config.getStr("chunker") == "const") {
|
if (config.getStr("chunker") == "const") {
|
||||||
return std::make_unique<ConstChunker>(buf, config.getInt("chunker-max") * 1024);
|
return std::make_unique<ConstChunker>(buf, config.getInt("chunker-max") * 1024);
|
||||||
} else if (config.getStr("chunker") == "buzhash") {
|
} else if (config.getStr("chunker") == "buzhash") {
|
||||||
return std::make_unique<BuzhashChunker>(buf, config.getInt("chunker-min") * 1024, config.getInt("chunker-max") * 1024, config.getInt("chunker-mask"));
|
return std::make_unique<BuzhashChunker>(buf, config.getInt("chunker-min") * 1024,
|
||||||
|
config.getInt("chunker-max") * 1024, config.getInt("chunker-mask"));
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Unknown chunker type!");
|
throw Exception("Unknown chunker type!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_library(commands srcs/CommandDiff.cpp srcs/CommandList.cpp srcs/CommandListFiles.cpp srcs/CommandRestore.cpp srcs/CommandRun.cpp srcs/CommandsCommon.cpp srcs/Diff.cpp srcs/CommandMount.cpp)
|
add_library(commands
|
||||||
|
srcs/CommandDiff.cpp
|
||||||
|
srcs/CommandList.cpp
|
||||||
|
srcs/CommandListFiles.cpp
|
||||||
|
srcs/CommandRestore.cpp
|
||||||
|
srcs/CommandRun.cpp
|
||||||
|
srcs/CommandsCommon.cpp
|
||||||
|
srcs/Diff.cpp
|
||||||
|
srcs/CommandMount.cpp
|
||||||
|
srcs/CommandDelete.cpp
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(commands PUBLIC includes)
|
target_include_directories(commands PUBLIC includes)
|
||||||
|
|
||||||
|
|||||||
18
src/commands/includes/CommandDelete.h
Normal file
18
src/commands/includes/CommandDelete.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// Created by Stepan Usatiuk on 06.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BACKUP_COMMANDDELETE_H
|
||||||
|
#define BACKUP_COMMANDDELETE_H
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
class CommandDelete : public Command {
|
||||||
|
public:
|
||||||
|
CommandDelete();
|
||||||
|
void run(Context ctx) override;
|
||||||
|
static constexpr std::string_view name{"delete"};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif//BACKUP_COMMANDDELETE_H
|
||||||
@@ -24,7 +24,8 @@ private:
|
|||||||
/// \param base Base directory to restore to
|
/// \param base Base directory to restore to
|
||||||
/// \param callback Stats callback
|
/// \param callback Stats callback
|
||||||
/// \return Name of the restored file
|
/// \return Name of the restored file
|
||||||
std::string backupRestoreFile(const File &file, const std::filesystem::path &base, CommandsCommon::workerStatsFunction &callback, Context ctx);
|
std::string backupRestoreFile(const File &file, const std::filesystem::path &base,
|
||||||
|
CommandsCommon::workerStatsFunction &callback, Context ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ private:
|
|||||||
/// \param saveAs UTF-8 encoded file name to save as
|
/// \param saveAs UTF-8 encoded file name to save as
|
||||||
/// \param callback Stats callback
|
/// \param callback Stats callback
|
||||||
/// \return ID of the saved file
|
/// \return ID of the saved file
|
||||||
Object::idType backupChunkFile(const std::filesystem::path &orig, const std::string &saveAs, CommandsCommon::workerStatsFunction &callback, Context ctx);
|
Object::idType backupChunkFile(const std::filesystem::path &orig, const std::string &saveAs,
|
||||||
|
CommandsCommon::workerStatsFunction &callback, Context ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ namespace CommandsCommon {
|
|||||||
/// \param ignore List of files to ignore
|
/// \param ignore List of files to ignore
|
||||||
/// \param spawner Function to spawn other tasks
|
/// \param spawner Function to spawn other tasks
|
||||||
/// \param processFile Task to spawn on found files
|
/// \param processFile Task to spawn on found files
|
||||||
void processDirWithIgnore(const std::filesystem::path &dir, std::vector<std::string> ignore, const std::function<void(std::function<void()>)> &spawner, std::function<void(std::filesystem::directory_entry)> processFile);
|
void processDirWithIgnore(const std::filesystem::path &dir, std::vector<std::string> ignore,
|
||||||
|
const std::function<void(std::function<void()>)> &spawner,
|
||||||
|
std::function<void(std::filesystem::directory_entry)> processFile);
|
||||||
|
|
||||||
struct WorkerStats {
|
struct WorkerStats {
|
||||||
public:
|
public:
|
||||||
@@ -40,7 +42,8 @@ namespace CommandsCommon {
|
|||||||
/// \return True if \p p contains \p prefix at its prefix, False otherwise
|
/// \return True if \p p contains \p prefix at its prefix, False otherwise
|
||||||
bool isSubpath(const std::filesystem::path &prefix, const std::filesystem::path &p);
|
bool isSubpath(const std::filesystem::path &prefix, const std::filesystem::path &p);
|
||||||
|
|
||||||
void workerCallback(unsigned long long bytesWritten, unsigned long long bytesSkipped, unsigned long long filesWritten, WorkerStats &to);
|
void workerCallback(unsigned long long bytesWritten, unsigned long long bytesSkipped,
|
||||||
|
unsigned long long filesWritten, WorkerStats &to);
|
||||||
|
|
||||||
};// namespace CommandsCommon
|
};// namespace CommandsCommon
|
||||||
|
|
||||||
|
|||||||
15
src/commands/srcs/CommandDelete.cpp
Normal file
15
src/commands/srcs/CommandDelete.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// Created by Stepan Usatiuk on 06.08.2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "CommandDelete.h"
|
||||||
|
|
||||||
|
#include "CommandsCommon.h"
|
||||||
|
|
||||||
|
using namespace CommandsCommon;
|
||||||
|
|
||||||
|
CommandDelete::CommandDelete() {}
|
||||||
|
|
||||||
|
void CommandDelete::run(Context ctx) {
|
||||||
|
ctx.repo->deleteObjects({static_cast<unsigned long long>(ctx.repo->getConfig().getInt("aid"))});
|
||||||
|
}
|
||||||
@@ -27,24 +27,24 @@ void CommandDiff::run(Context ctx) {
|
|||||||
Object::idType archive1;
|
Object::idType archive1;
|
||||||
if (!ctx.repo->getConfig().exists("aid")) {
|
if (!ctx.repo->getConfig().exists("aid")) {
|
||||||
auto archives = ctx.repo->getObjects(Object::ObjectType::Archive);
|
auto archives = ctx.repo->getObjects(Object::ObjectType::Archive);
|
||||||
archive1 = std::max_element(archives.begin(), archives.end(), [](const auto &a1, const auto &a2) { return a1.second < a2.second; })->second;
|
archive1 = std::max_element(archives.begin(), archives.end(), [](const auto &a1, const auto &a2) {
|
||||||
|
return a1.second < a2.second;
|
||||||
|
})->second;
|
||||||
} else {
|
} else {
|
||||||
archive1 = ctx.repo->getConfig().getInt("aid");
|
archive1 = ctx.repo->getConfig().getInt("aid");
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool threadPool([&](const std::string &error) {
|
ThreadPool threadPool([&](const std::string &error) { ctx.logger->write("Error: " + error, 0); },
|
||||||
ctx.logger->write("Error: " + error, 0);
|
ctx.repo->getConfig().exists("threads") ? ctx.repo->getConfig().getInt("threads")
|
||||||
},
|
: std::thread::hardware_concurrency());
|
||||||
ctx.repo->getConfig().exists("threads") ? ctx.repo->getConfig().getInt("threads") : std::thread::hardware_concurrency());
|
|
||||||
|
|
||||||
auto archiveO1 = Serialize::deserialize<Archive>(ctx.repo->getObject(archive1));
|
auto archiveO1 = Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(archive1));
|
||||||
std::mutex filesLock;
|
std::mutex filesLock;
|
||||||
std::map<std::filesystem::path, File> files;///< Files in the first archive
|
std::map<std::filesystem::path, File> files;///< Files in the first archive
|
||||||
for (auto id: archiveO1.files) {
|
for (auto id: archiveO1.files) {
|
||||||
auto file = Serialize::deserialize<File>(ctx.repo->getObject(id));
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(id));
|
||||||
auto path = std::filesystem::u8path(file.name);
|
auto path = std::filesystem::path(file.name);
|
||||||
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), path))
|
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), path)) files.emplace(file.getKey(), std::move(file));
|
||||||
files.emplace(file.getKey(), std::move(file));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container of ChangeDetectors built using the config of the repository
|
/// Container of ChangeDetectors built using the config of the repository
|
||||||
@@ -63,8 +63,7 @@ void CommandDiff::run(Context ctx) {
|
|||||||
if (changeDetector.check({repoFile, ctx.repo}, p)) {
|
if (changeDetector.check({repoFile, ctx.repo}, p)) {
|
||||||
ctx.logger->write(relPath + " is different " + Diff::diff({repoFile, ctx.repo}, p) + "\n", 1);
|
ctx.logger->write(relPath + " is different " + Diff::diff({repoFile, ctx.repo}, p) + "\n", 1);
|
||||||
} else {
|
} else {
|
||||||
if (diffMode == "file")
|
if (diffMode == "file") ctx.logger->write(relPath + " are same ", 0);
|
||||||
ctx.logger->write(relPath + " are same ", 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,17 +75,16 @@ void CommandDiff::run(Context ctx) {
|
|||||||
if (diffMode == "normal") {
|
if (diffMode == "normal") {
|
||||||
/// If a second archive is given, run the task for each of its files, otherwise use the "from" config option
|
/// If a second archive is given, run the task for each of its files, otherwise use the "from" config option
|
||||||
if (ctx.repo->getConfig().exists("aid2")) {
|
if (ctx.repo->getConfig().exists("aid2")) {
|
||||||
archiveO2.emplace(Serialize::deserialize<Archive>(ctx.repo->getObject(ctx.repo->getConfig().getInt("aid2"))));
|
archiveO2.emplace(
|
||||||
|
Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(ctx.repo->getConfig().getInt("aid2"))));
|
||||||
|
|
||||||
threadPool.push([&]() {
|
threadPool.push([&]() {
|
||||||
for (auto id: archiveO2.value().files) {
|
for (auto id: archiveO2.value().files) {
|
||||||
/// Exit when asked to
|
/// Exit when asked to
|
||||||
if (Signals::shouldQuit) throw Exception("Quitting");
|
if (Signals::shouldQuit) throw Exception("Quitting");
|
||||||
auto file = Serialize::deserialize<File>(ctx.repo->getObject(id));
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(id));
|
||||||
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), std::filesystem::u8path(file.name)))
|
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), std::filesystem::path(file.name)))
|
||||||
threadPool.push([&, file]() {
|
threadPool.push([&, file]() { processFile(ComparableFile{file, ctx.repo}); });
|
||||||
processFile(ComparableFile{file, ctx.repo});
|
|
||||||
});
|
|
||||||
if (Signals::shouldQuit) break;
|
if (Signals::shouldQuit) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,10 +95,9 @@ void CommandDiff::run(Context ctx) {
|
|||||||
/// Start the diff with the root directory and empty ignore list
|
/// Start the diff with the root directory and empty ignore list
|
||||||
threadPool.push([&, from]() {
|
threadPool.push([&, from]() {
|
||||||
processDirWithIgnore(
|
processDirWithIgnore(
|
||||||
from,
|
from, {}, [&](std::function<void()> f) { threadPool.push(std::move(f)); },
|
||||||
{},
|
[processFile, from, prefix = ctx.repo->getConfig().getStr("prefix")](
|
||||||
[&](std::function<void()> f) { threadPool.push(std::move(f)); },
|
const std::filesystem::directory_entry &dirEntry) {
|
||||||
[processFile, from, prefix = ctx.repo->getConfig().getStr("prefix")](const std::filesystem::directory_entry &dirEntry) {
|
|
||||||
if (isSubpath(prefix, dirEntry.path().lexically_relative(from)))
|
if (isSubpath(prefix, dirEntry.path().lexically_relative(from)))
|
||||||
processFile(ComparableFile{dirEntry, from});
|
processFile(ComparableFile{dirEntry, from});
|
||||||
});
|
});
|
||||||
@@ -113,11 +110,12 @@ void CommandDiff::run(Context ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.repo->getConfig().exists("aid2")) {
|
if (ctx.repo->getConfig().exists("aid2")) {
|
||||||
archiveO2.emplace(Serialize::deserialize<Archive>(ctx.repo->getObject(ctx.repo->getConfig().getInt("aid2"))));
|
archiveO2.emplace(
|
||||||
|
Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(ctx.repo->getConfig().getInt("aid2"))));
|
||||||
std::map<std::filesystem::path, File> files2;///< Files in the first archive
|
std::map<std::filesystem::path, File> files2;///< Files in the first archive
|
||||||
for (auto id: archiveO2->files) {
|
for (auto id: archiveO2->files) {
|
||||||
auto file = Serialize::deserialize<File>(ctx.repo->getObject(id));
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(id));
|
||||||
auto path = std::filesystem::u8path(file.name);
|
auto path = std::filesystem::path(file.name);
|
||||||
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), path))
|
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), path))
|
||||||
files2.emplace(file.getKey(), std::move(file));
|
files2.emplace(file.getKey(), std::move(file));
|
||||||
}
|
}
|
||||||
@@ -146,7 +144,5 @@ void CommandDiff::run(Context ctx) {
|
|||||||
std::unique_lock finishedLock(threadPool.finishedLock);
|
std::unique_lock finishedLock(threadPool.finishedLock);
|
||||||
threadPool.finished.wait(finishedLock, [&threadPool] { return threadPool.empty(); });
|
threadPool.finished.wait(finishedLock, [&threadPool] { return threadPool.empty(); });
|
||||||
if (diffMode == "normal")
|
if (diffMode == "normal")
|
||||||
for (auto const &s: files) {
|
for (auto const &s: files) { ctx.logger->write(s.first.string() + " is removed\n", 0); }
|
||||||
ctx.logger->write(s.first.u8string() + " is removed\n", 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,10 @@
|
|||||||
|
|
||||||
#include "CommandList.h"
|
#include "CommandList.h"
|
||||||
|
|
||||||
CommandList::CommandList() : Command() {
|
CommandList::CommandList() : Command() {}
|
||||||
}
|
|
||||||
|
|
||||||
void CommandList::run(Context ctx) {
|
void CommandList::run(Context ctx) {
|
||||||
auto list = ctx.repo->getObjects(Object::ObjectType::Archive);
|
auto list = ctx.repo->getObjects(Object::ObjectType::Archive);
|
||||||
std::sort(list.begin(), list.end(), [](const auto &l, const auto &r) { return l.second < r.second; });
|
std::sort(list.begin(), list.end(), [](const auto &l, const auto &r) { return l.second < r.second; });
|
||||||
for (auto const &aid: list) {
|
for (auto const &aid: list) { std::cout << "Name: " << aid.first << " Id: " << aid.second << std::endl; }
|
||||||
std::cout << "Name: " << aid.first << " Id: " << aid.second << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,13 @@
|
|||||||
#include "objects/Chunk.h"
|
#include "objects/Chunk.h"
|
||||||
#include "objects/File.h"
|
#include "objects/File.h"
|
||||||
|
|
||||||
CommandListFiles::CommandListFiles() : Command() {
|
CommandListFiles::CommandListFiles() : Command() {}
|
||||||
}
|
|
||||||
|
|
||||||
void CommandListFiles::run(Context ctx) {
|
void CommandListFiles::run(Context ctx) {
|
||||||
auto archive = Serialize::deserialize<Archive>(ctx.repo->getObject(ctx.repo->getConfig().getInt("aid")));
|
auto archive = Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(ctx.repo->getConfig().getInt("aid")));
|
||||||
for (auto const &fid: archive.files) {
|
for (auto const &fid: archive.files) {
|
||||||
auto file = Serialize::deserialize<File>(ctx.repo->getObject(fid));
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(fid));
|
||||||
std::cout << "Name: " << file.name << " type: " << File::TypeToStr.at(file.fileType) << " size: " << BytesFormatter::formatStr(file.bytes) << std::endl;
|
std::cout << "Name: " << file.name << " type: " << File::TypeToStr.at(file.fileType)
|
||||||
|
<< " size: " << BytesFormatter::formatStr(file.bytes) << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
#include "CommandMount.h"
|
#include "CommandMount.h"
|
||||||
#include "RepoFS.h"
|
#include "RepoFS.h"
|
||||||
|
|
||||||
CommandMount::CommandMount() : Command() {
|
CommandMount::CommandMount() : Command() {}
|
||||||
}
|
|
||||||
|
|
||||||
void CommandMount::run(Context ctx) {
|
void CommandMount::run(Context ctx) { RepoFS::start(ctx.repo, ctx.repo->getConfig().getStr("to")); }
|
||||||
RepoFS::start(ctx.repo, ctx.repo->getConfig().getStr("to"));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,12 +20,11 @@
|
|||||||
|
|
||||||
using namespace CommandsCommon;
|
using namespace CommandsCommon;
|
||||||
|
|
||||||
CommandRestore::CommandRestore() : Command() {
|
CommandRestore::CommandRestore() : Command() {}
|
||||||
}
|
|
||||||
|
|
||||||
void CommandRestore::run(Context ctx) {
|
void CommandRestore::run(Context ctx) {
|
||||||
Object::idType archive = ctx.repo->getConfig().getInt("aid");
|
Object::idType archive = ctx.repo->getConfig().getInt("aid");
|
||||||
std::filesystem::path to = std::filesystem::u8path(ctx.repo->getConfig().getStr("to"));
|
std::filesystem::path to = std::filesystem::path(ctx.repo->getConfig().getStr("to"));
|
||||||
|
|
||||||
std::atomic<unsigned long long> filesToRestoreCount = 0;
|
std::atomic<unsigned long long> filesToRestoreCount = 0;
|
||||||
std::atomic<unsigned long long> bytesToRestore = 0;
|
std::atomic<unsigned long long> bytesToRestore = 0;
|
||||||
@@ -33,14 +32,14 @@ void CommandRestore::run(Context ctx) {
|
|||||||
WorkerStats workerStats;///< Backup statistics of the worker threads
|
WorkerStats workerStats;///< Backup statistics of the worker threads
|
||||||
|
|
||||||
/// Worker callback, bound to the local workerStats variable
|
/// Worker callback, bound to the local workerStats variable
|
||||||
workerStatsFunction workerCallback = [&workerStats](unsigned long long bytesWritten, unsigned long long bytesSkipped, unsigned long long filesWritten) {
|
workerStatsFunction workerCallback = [&workerStats](unsigned long long bytesWritten,
|
||||||
|
unsigned long long bytesSkipped,
|
||||||
|
unsigned long long filesWritten) {
|
||||||
CommandsCommon::workerCallback(bytesWritten, bytesSkipped, filesWritten, workerStats);
|
CommandsCommon::workerCallback(bytesWritten, bytesSkipped, filesWritten, workerStats);
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
/// Calculate the average speed of backup
|
/// Calculate the average speed of backup
|
||||||
RunningDiffAverage avg(
|
RunningDiffAverage avg([&]() { return workerStats.bytesWritten.load(); }, 100, 100);
|
||||||
[&]() { return workerStats.bytesWritten.load(); },
|
|
||||||
100, 100);
|
|
||||||
|
|
||||||
/// Show restore progress
|
/// Show restore progress
|
||||||
Progress progress([this, ctx](const std::string &s, int l) { ctx.logger->write(s, l); },
|
Progress progress([this, ctx](const std::string &s, int l) { ctx.logger->write(s, l); },
|
||||||
@@ -49,7 +48,10 @@ void CommandRestore::run(Context ctx) {
|
|||||||
"/",
|
"/",
|
||||||
[&filesToRestoreCount]() { return std::to_string(filesToRestoreCount); },
|
[&filesToRestoreCount]() { return std::to_string(filesToRestoreCount); },
|
||||||
" files saved, ",
|
" files saved, ",
|
||||||
[&workerStats]() { return BytesFormatter::formatStr(workerStats.bytesWritten.load() + workerStats.bytesSkipped.load()); },
|
[&workerStats]() {
|
||||||
|
return BytesFormatter::formatStr(workerStats.bytesWritten.load() +
|
||||||
|
workerStats.bytesSkipped.load());
|
||||||
|
},
|
||||||
" / ",
|
" / ",
|
||||||
[&bytesToRestore]() { return BytesFormatter::formatStr(bytesToRestore); },
|
[&bytesToRestore]() { return BytesFormatter::formatStr(bytesToRestore); },
|
||||||
" saved @ ",
|
" saved @ ",
|
||||||
@@ -59,22 +61,21 @@ void CommandRestore::run(Context ctx) {
|
|||||||
ctx.repo->getConfig());
|
ctx.repo->getConfig());
|
||||||
|
|
||||||
/// Thread pool for restore tasks
|
/// Thread pool for restore tasks
|
||||||
ThreadPool threadPool([&](const std::string &error) {
|
ThreadPool threadPool([&](const std::string &error) { progress.print("Error: " + error, 0); },
|
||||||
progress.print("Error: " + error, 0);
|
ctx.repo->getConfig().exists("threads") ? ctx.repo->getConfig().getInt("threads")
|
||||||
},
|
: std::thread::hardware_concurrency());
|
||||||
ctx.repo->getConfig().exists("threads") ? ctx.repo->getConfig().getInt("threads") : std::thread::hardware_concurrency());
|
|
||||||
|
|
||||||
/// Add the main restore task
|
/// Add the main restore task
|
||||||
threadPool.push([&, this]() {
|
threadPool.push([&, this]() {
|
||||||
/// Get the archive and its file IDs
|
/// Get the archive and its file IDs
|
||||||
auto archiveO = Serialize::deserialize<Archive>(ctx.repo->getObject(archive));
|
auto archiveO = Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(archive));
|
||||||
std::vector<Object::idType> files = archiveO.files;
|
std::vector<Object::idType> files = archiveO.files;
|
||||||
/// For each file...
|
/// For each file...
|
||||||
for (const auto fid: files) {
|
for (const auto fid: files) {
|
||||||
/// Stop when asked to
|
/// Stop when asked to
|
||||||
if (Signals::shouldQuit) break;
|
if (Signals::shouldQuit) break;
|
||||||
|
|
||||||
auto file = Serialize::deserialize<File>(ctx.repo->getObject(fid));
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(fid));
|
||||||
filesToRestoreCount++;
|
filesToRestoreCount++;
|
||||||
bytesToRestore += file.bytes;
|
bytesToRestore += file.bytes;
|
||||||
/// Spawn a restore task
|
/// Spawn a restore task
|
||||||
@@ -92,28 +93,30 @@ void CommandRestore::run(Context ctx) {
|
|||||||
ctx.logger->write("\n", 1);
|
ctx.logger->write("\n", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CommandRestore::backupRestoreFile(const File &file, const std::filesystem::path &baseDir, workerStatsFunction &callback, Context ctx) {
|
std::string CommandRestore::backupRestoreFile(const File &file, const std::filesystem::path &baseDir,
|
||||||
auto fullpath = baseDir / std::filesystem::u8path(file.name);
|
workerStatsFunction &callback, Context ctx) {
|
||||||
|
auto fullpath = baseDir / std::filesystem::path(file.name);
|
||||||
|
|
||||||
std::filesystem::create_directories(fullpath.parent_path());
|
std::filesystem::create_directories(fullpath.parent_path());
|
||||||
|
|
||||||
if (file.fileType == File::Type::Directory) {
|
if (file.fileType == File::Type::Directory) {
|
||||||
std::filesystem::create_directory(fullpath);
|
std::filesystem::create_directory(fullpath);
|
||||||
callback(0, 0, 1);
|
callback(0, 0, 1);
|
||||||
return fullpath.u8string();
|
return fullpath.string();
|
||||||
}
|
}
|
||||||
if (file.fileType == File::Type::Symlink) {
|
if (file.fileType == File::Type::Symlink) {
|
||||||
auto dest = Serialize::deserialize<Chunk>(ctx.repo->getObject(file.chunks.at(0)));
|
auto dest = Serialize::deserialize<Chunk>(ctx.repo->getObjectRaw(file.chunks.at(0)));
|
||||||
std::filesystem::create_symlink(std::filesystem::u8path(std::string{dest.data.begin(), dest.data.end()}), fullpath);
|
std::filesystem::create_symlink(std::filesystem::path(std::string{dest.data.begin(), dest.data.end()}),
|
||||||
|
fullpath);
|
||||||
callback(0, 0, 1);
|
callback(0, 0, 1);
|
||||||
return fullpath.u8string();
|
return fullpath.string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream ostream(fullpath, std::ios::binary | std::ios::out | std::ios::trunc);
|
std::ofstream ostream(fullpath, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
for (const auto cid: file.chunks) {
|
for (const auto cid: file.chunks) {
|
||||||
if (Signals::shouldQuit) throw Exception("Quitting!");
|
if (Signals::shouldQuit) throw Exception("Quitting!");
|
||||||
|
|
||||||
Chunk c = Serialize::deserialize<Chunk>(ctx.repo->getObject(cid.second));
|
Chunk c = Serialize::deserialize<Chunk>(ctx.repo->getObjectRaw(cid.second));
|
||||||
if (!c.data.empty()) {
|
if (!c.data.empty()) {
|
||||||
ostream.rdbuf()->sputn(c.data.data(), c.data.size());
|
ostream.rdbuf()->sputn(c.data.data(), c.data.size());
|
||||||
callback(c.data.size(), 0, 0);
|
callback(c.data.size(), 0, 0);
|
||||||
@@ -121,5 +124,5 @@ std::string CommandRestore::backupRestoreFile(const File &file, const std::files
|
|||||||
}
|
}
|
||||||
callback(0, 0, 1);
|
callback(0, 0, 1);
|
||||||
|
|
||||||
return fullpath.u8string();
|
return fullpath.string();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,7 @@
|
|||||||
|
|
||||||
using namespace CommandsCommon;
|
using namespace CommandsCommon;
|
||||||
|
|
||||||
CommandRun::CommandRun() : Command() {
|
CommandRun::CommandRun() : Command() {}
|
||||||
}
|
|
||||||
|
|
||||||
void CommandRun::run(Context ctx) {
|
void CommandRun::run(Context ctx) {
|
||||||
WorkerStats workerStats;///< Backup statistics of the worker threads
|
WorkerStats workerStats;///< Backup statistics of the worker threads
|
||||||
@@ -36,82 +35,77 @@ void CommandRun::run(Context ctx) {
|
|||||||
std::filesystem::path from = ctx.repo->getConfig().getStr("from");///< Directory to back up from
|
std::filesystem::path from = ctx.repo->getConfig().getStr("from");///< Directory to back up from
|
||||||
|
|
||||||
/// Worker callback, bound to the local workerStats variable
|
/// Worker callback, bound to the local workerStats variable
|
||||||
workerStatsFunction workerCallback = [&](unsigned long long bytesWritten, unsigned long long bytesSkipped, unsigned long long filesWritten) {
|
workerStatsFunction workerCallback = [&](unsigned long long bytesWritten, unsigned long long bytesSkipped,
|
||||||
|
unsigned long long filesWritten) {
|
||||||
CommandsCommon::workerCallback(bytesWritten, bytesSkipped, filesWritten, workerStats);
|
CommandsCommon::workerCallback(bytesWritten, bytesSkipped, filesWritten, workerStats);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Object::idType> files;///< File ids so far added to the archive
|
std::vector<Object::idType> files;///< File ids so far added to the archive
|
||||||
std::mutex filesLock; ///< Files vector lock
|
std::mutex filesLock; ///< Files vector lock
|
||||||
/// Function to safely add new file ids to `files`
|
/// Function to safely add new file ids to `files`
|
||||||
std::function addFile = [&](Object::idType id) {std::lock_guard lock(filesLock); files.emplace_back(id); };
|
std::function addFile = [&](Object::idType id) {
|
||||||
|
std::lock_guard lock(filesLock);
|
||||||
|
files.emplace_back(id);
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
/// Calculate the average speed of backup
|
/// Calculate the average speed of backup
|
||||||
RunningDiffAverage avg(
|
RunningDiffAverage avg([&]() { return workerStats.bytesWritten.load(); }, 100, 100);
|
||||||
[&]() { return workerStats.bytesWritten.load(); },
|
|
||||||
100, 100);
|
|
||||||
|
|
||||||
/// Show the progress of backup
|
/// Show the progress of backup
|
||||||
Progress progress([this, ctx](const std::string &s, int l) { ctx.logger->write(s, l); },
|
Progress progress([this, ctx](const std::string &s, int l) { ctx.logger->write(s, l); },
|
||||||
{[&]() { return std::to_string(workerStats.filesWritten.load()); },
|
{[&]() { return std::to_string(workerStats.filesWritten.load()); }, "/",
|
||||||
"/",
|
[&]() { return std::to_string(runnerStats.filesToSaveCount); }, " files saved, ",
|
||||||
[&]() { return std::to_string(runnerStats.filesToSaveCount); },
|
[&]() { return std::to_string(runnerStats.filesSkipped); }, " files skipped, ",
|
||||||
" files saved, ",
|
[&]() {
|
||||||
[&]() { return std::to_string(runnerStats.filesSkipped); },
|
return BytesFormatter::formatStr(
|
||||||
" files skipped, ",
|
(workerStats.bytesWritten.load() + workerStats.bytesSkipped.load()));
|
||||||
[&]() { return BytesFormatter::formatStr((workerStats.bytesWritten.load() + workerStats.bytesSkipped.load())); },
|
},
|
||||||
" / ",
|
" / ", [&]() { return BytesFormatter::formatStr(runnerStats.bytesToSave); }, " read @ ",
|
||||||
[&]() { return BytesFormatter::formatStr(runnerStats.bytesToSave); },
|
[&]() { return BytesFormatter::formatStr(avg.get() * 10); }, "/s"},
|
||||||
" read @ ",
|
|
||||||
[&]() { return BytesFormatter::formatStr(avg.get() * 10); },
|
|
||||||
"/s"},
|
|
||||||
ctx.repo->getConfig());
|
ctx.repo->getConfig());
|
||||||
|
|
||||||
/// Thread pool for backup tasks, prints to progress on any errors
|
/// Thread pool for backup tasks, prints to progress on any errors
|
||||||
ThreadPool threadPool([&](const std::string &error) {
|
ThreadPool threadPool([&](const std::string &error) { progress.print("Error: " + error, 0); },
|
||||||
progress.print("Error: " + error, 0);
|
ctx.repo->getConfig().exists("threads") ? ctx.repo->getConfig().getInt("threads")
|
||||||
},
|
: std::thread::hardware_concurrency());
|
||||||
ctx.repo->getConfig().exists("threads") ? ctx.repo->getConfig().getInt("threads") : std::thread::hardware_concurrency());
|
|
||||||
|
|
||||||
/// Container of ChangeDetectors built using the config of the repository
|
/// Container of ChangeDetectors built using the config of the repository
|
||||||
ChangeDetectorContainer changeDetector = ChangeDetectorFactory::getChangeDetectors(ctx.repo->getConfig());
|
ChangeDetectorContainer changeDetector = ChangeDetectorFactory::getChangeDetectors(ctx.repo->getConfig());
|
||||||
|
|
||||||
/// Function to spawn a rechunking task
|
/// Function to spawn a rechunking task
|
||||||
auto saveFile = [&, this](const std::filesystem::path &absPath, const std::filesystem::path &relPath) {
|
auto saveFile = [&, this](const std::filesystem::path &absPath, const std::filesystem::path &relPath) {
|
||||||
runnerStats.bytesToSave += File::getFileType(absPath) == File::Type::Normal ? std::filesystem::file_size(absPath) : 0;
|
runnerStats.bytesToSave +=
|
||||||
|
File::getFileType(absPath) == File::Type::Normal ? std::filesystem::file_size(absPath) : 0;
|
||||||
runnerStats.filesToSaveCount++;
|
runnerStats.filesToSaveCount++;
|
||||||
threadPool.push([&, relPath, absPath]() {
|
threadPool.push([&, relPath, absPath]() {
|
||||||
addFile(backupChunkFile(absPath, relPath.u8string(), workerCallback, ctx));
|
addFile(backupChunkFile(absPath, relPath.string(), workerCallback, ctx));
|
||||||
progress.print("Copied: " + relPath.u8string(), 1);
|
progress.print("Copied: " + relPath.string(), 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Task to process an individual file in the backup
|
/// Task to process an individual file in the backup
|
||||||
std::function<void(std::filesystem::path)> processFile =
|
std::function<void(std::filesystem::path)> processFile = [&, this](const std::filesystem::path &p) {
|
||||||
[&, this](const std::filesystem::path &p) {
|
auto relPath = p.lexically_relative(from).string();
|
||||||
auto relPath = p.lexically_relative(from).u8string();
|
|
||||||
|
|
||||||
if (ctx.repo->exists(Object::ObjectType::File, relPath) != 0) {
|
if (ctx.repo->exists(Object::ObjectType::File, relPath) != 0) {
|
||||||
File repoFile = Serialize::deserialize<File>(ctx.repo->getObject(Object::ObjectType::File, relPath));
|
File repoFile = Serialize::deserialize<File>(ctx.repo->getObjectRaw(Object::ObjectType::File, relPath));
|
||||||
if (!changeDetector.check({repoFile, ctx.repo}, {p, from})) {
|
if (!changeDetector.check({repoFile, ctx.repo}, {p, from})) {
|
||||||
addFile(repoFile.id);
|
addFile(repoFile.id);
|
||||||
progress.print("Skipped: " + relPath, 1);
|
progress.print("Skipped: " + relPath, 1);
|
||||||
runnerStats.filesSkipped++;
|
runnerStats.filesSkipped++;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saveFile(p, relPath);
|
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFile(p, relPath);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
/// Start the backup with the root directory and empty ignore list
|
/// Start the backup with the root directory and empty ignore list
|
||||||
threadPool.push([&]() {
|
threadPool.push([&]() {
|
||||||
processDirWithIgnore(
|
processDirWithIgnore(
|
||||||
from,
|
from, {}, [&](std::function<void()> f) { threadPool.push(std::move(f)); }, processFile);
|
||||||
{},
|
|
||||||
[&](std::function<void()> f) { threadPool.push(std::move(f)); },
|
|
||||||
processFile);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Wait for all the tasks to finish
|
/// Wait for all the tasks to finish
|
||||||
@@ -137,22 +131,23 @@ void CommandRun::run(Context ctx) {
|
|||||||
ctx.repo->putObject(a);
|
ctx.repo->putObject(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::idType CommandRun::backupChunkFile(const std::filesystem::path &orig, const std::string &saveAs, workerStatsFunction &callback, Context ctx) {
|
Object::idType CommandRun::backupChunkFile(const std::filesystem::path &orig, const std::string &saveAs,
|
||||||
|
workerStatsFunction &callback, Context ctx) {
|
||||||
/// If it's a symlink or directory, treat it specially
|
/// If it's a symlink or directory, treat it specially
|
||||||
/// The order of checks is important, because is_directory follows the symlink
|
/// The order of checks is important, because is_directory follows the symlink
|
||||||
if (std::filesystem::is_symlink(orig) || std::filesystem::is_directory(orig)) {
|
if (std::filesystem::is_symlink(orig) || std::filesystem::is_directory(orig)) {
|
||||||
auto contents = File::getFileContents(orig);
|
auto contents = File::getFileContents(orig);
|
||||||
Chunk c(ctx.repo->getId(), SHA::calculate(contents), contents);
|
Chunk c(ctx.repo->getId(), SHA::calculate(contents), contents);
|
||||||
File f(ctx.repo->getId(), saveAs, c.length, File::getFileMtime(orig), c.SHA, {{0, c.id}}, File::getFileType(orig));
|
File f(ctx.repo->getId(), saveAs, c.length, File::getFileMtime(orig), c.SHA, {{0, c.id}},
|
||||||
|
File::getFileType(orig));
|
||||||
ctx.repo->putObject(c);
|
ctx.repo->putObject(c);
|
||||||
ctx.repo->putObject(f);
|
ctx.repo->putObject(f);
|
||||||
return f.id;
|
return f.id;
|
||||||
}
|
}
|
||||||
if (!std::filesystem::is_regular_file(orig))
|
if (!std::filesystem::is_regular_file(orig)) throw Exception(orig.string() + "is a special file, not saving");
|
||||||
throw Exception(orig.u8string() + "is a special file, not saving");
|
|
||||||
|
|
||||||
std::ifstream ifstream(orig, std::ios::in | std::ios::binary);
|
std::ifstream ifstream(orig, std::ios::in | std::ios::binary);
|
||||||
if (!ifstream) throw Exception("Couldn't open " + orig.u8string() + " for reading");
|
if (!ifstream) throw Exception("Couldn't open " + orig.string() + " for reading");
|
||||||
std::unique_ptr<Chunker> chunker = ChunkerFactory::getChunker(ctx.repo->getConfig(), ifstream.rdbuf());
|
std::unique_ptr<Chunker> chunker = ChunkerFactory::getChunker(ctx.repo->getConfig(), ifstream.rdbuf());
|
||||||
|
|
||||||
SHA fileHash;
|
SHA fileHash;
|
||||||
@@ -184,9 +179,10 @@ Object::idType CommandRun::backupChunkFile(const std::filesystem::path &orig, co
|
|||||||
/// We might have exited in the loop before, so we don't save an incomplete file
|
/// We might have exited in the loop before, so we don't save an incomplete file
|
||||||
if (Signals::shouldQuit) throw Exception("Quitting!");
|
if (Signals::shouldQuit) throw Exception("Quitting!");
|
||||||
if (size != File::getFileSize(orig)) {
|
if (size != File::getFileSize(orig)) {
|
||||||
throw Exception("Something really bad happened or file " + orig.u8string() + " changed during backup");
|
throw Exception("Something really bad happened or file " + orig.string() + " changed during backup");
|
||||||
}
|
}
|
||||||
File f(ctx.repo->getId(), saveAs, size, File::getFileMtime(orig), fileHash.getHash(), fileChunks, File::getFileType(orig));
|
File f(ctx.repo->getId(), saveAs, size, File::getFileMtime(orig), fileHash.getHash(), fileChunks,
|
||||||
|
File::getFileType(orig));
|
||||||
ctx.repo->putObject(f);
|
ctx.repo->putObject(f);
|
||||||
callback(0, 0, 1);
|
callback(0, 0, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -10,21 +10,24 @@
|
|||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "Signals.h"
|
#include "Signals.h"
|
||||||
|
|
||||||
void CommandsCommon::workerCallback(unsigned long long int bytesWritten, unsigned long long int bytesSkipped, unsigned long long int filesWritten, WorkerStats &to) {
|
void CommandsCommon::workerCallback(unsigned long long int bytesWritten, unsigned long long int bytesSkipped,
|
||||||
|
unsigned long long int filesWritten, WorkerStats &to) {
|
||||||
to.bytesWritten += bytesWritten;
|
to.bytesWritten += bytesWritten;
|
||||||
to.bytesSkipped += bytesSkipped;
|
to.bytesSkipped += bytesSkipped;
|
||||||
to.filesWritten += filesWritten;
|
to.filesWritten += filesWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommandsCommon::isSubpath(const std::filesystem::path &prefix, const std::filesystem::path &p) {
|
bool CommandsCommon::isSubpath(const std::filesystem::path &prefix, const std::filesystem::path &p) {
|
||||||
if (prefix.u8string().size() > p.u8string().size()) return false;
|
if (prefix.string().size() > p.string().size()) return false;
|
||||||
for (int i = 0; i < prefix.u8string().size(); i++)
|
for (int i = 0; i < prefix.string().size(); i++)
|
||||||
if (p.u8string()[i] != prefix.u8string()[i]) return false;
|
if (p.string()[i] != prefix.string()[i]) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandsCommon::processDirWithIgnore(const std::filesystem::path &dir, std::vector<std::string> ignore, const std::function<void(std::function<void()>)> &spawner, std::function<void(std::filesystem::directory_entry)> processFile) {
|
void CommandsCommon::processDirWithIgnore(const std::filesystem::path &dir, std::vector<std::string> ignore,
|
||||||
if (!std::filesystem::is_directory(dir)) throw Exception(dir.u8string() + " is not a directory!");
|
const std::function<void(std::function<void()>)> &spawner,
|
||||||
|
std::function<void(std::filesystem::directory_entry)> processFile) {
|
||||||
|
if (!std::filesystem::is_directory(dir)) throw Exception(dir.string() + " is not a directory!");
|
||||||
|
|
||||||
/// Don't process the directory if it has a ".nobackup" file
|
/// Don't process the directory if it has a ".nobackup" file
|
||||||
if (std::filesystem::exists(dir / ".nobackup")) return;
|
if (std::filesystem::exists(dir / ".nobackup")) return;
|
||||||
@@ -33,9 +36,7 @@ void CommandsCommon::processDirWithIgnore(const std::filesystem::path &dir, std:
|
|||||||
if (std::filesystem::exists(dir / ".ignore")) {
|
if (std::filesystem::exists(dir / ".ignore")) {
|
||||||
std::ifstream ignorefile(dir / ".ignore", std::ios::in);
|
std::ifstream ignorefile(dir / ".ignore", std::ios::in);
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(ignorefile, line)) {
|
while (std::getline(ignorefile, line)) { ignore.emplace_back(line); }
|
||||||
ignore.emplace_back(line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For each directory entry...
|
/// For each directory entry...
|
||||||
@@ -46,9 +47,10 @@ void CommandsCommon::processDirWithIgnore(const std::filesystem::path &dir, std:
|
|||||||
/// Don't process the entry if it matches any of the ignore rules
|
/// Don't process the entry if it matches any of the ignore rules
|
||||||
if (std::any_of(ignore.begin(), ignore.end(), [dirEntry](auto pred) {
|
if (std::any_of(ignore.begin(), ignore.end(), [dirEntry](auto pred) {
|
||||||
std::smatch m;
|
std::smatch m;
|
||||||
auto s = dirEntry.path().filename().u8string();
|
auto s = dirEntry.path().filename().string();
|
||||||
return std::regex_match(s, m, std::regex(pred));
|
return std::regex_match(s, m, std::regex(pred));
|
||||||
})) continue;
|
}))
|
||||||
|
continue;
|
||||||
|
|
||||||
/// If it's a directory, spawn a task to process the entries in it
|
/// If it's a directory, spawn a task to process the entries in it
|
||||||
if (!dirEntry.is_symlink() && dirEntry.is_directory()) {
|
if (!dirEntry.is_symlink() && dirEntry.is_directory()) {
|
||||||
@@ -60,8 +62,6 @@ void CommandsCommon::processDirWithIgnore(const std::filesystem::path &dir, std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a task to process each individual file
|
/// Spawn a task to process each individual file
|
||||||
spawner([processFile, dirEntry]() {
|
spawner([processFile, dirEntry]() { processFile(dirEntry); });
|
||||||
processFile(dirEntry);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,13 +51,9 @@ std::string Diff::diff(const ComparableFile &c1, const ComparableFile &c2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
out << "\nLines only in first file: " << std::endl;
|
out << "\nLines only in first file: " << std::endl;
|
||||||
for (const auto &s: f1lines) {
|
for (const auto &s: f1lines) { out << s.second << "<" << s.first << std::endl; }
|
||||||
out << s.second << "<" << s.first << std::endl;
|
|
||||||
}
|
|
||||||
out << "Lines only in second file: " << std::endl;
|
out << "Lines only in second file: " << std::endl;
|
||||||
for (const auto &s: f2diff) {
|
for (const auto &s: f2diff) { out << s.second << ">" << s.first << std::endl; }
|
||||||
out << s.second << ">" << s.first << std::endl;
|
|
||||||
}
|
|
||||||
out << "^^^\n";
|
out << "^^^\n";
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
@@ -92,12 +88,8 @@ std::string Diff::diffPercent(const ComparableFile &c1, const ComparableFile &c2
|
|||||||
|
|
||||||
unsigned long long diff = 0;
|
unsigned long long diff = 0;
|
||||||
|
|
||||||
for (const auto &c: ch1hashes) {
|
for (const auto &c: ch1hashes) { diff += hashsize[c]; }
|
||||||
diff += hashsize[c];
|
for (const auto &c: ch2diff) { diff += hashsize[c]; }
|
||||||
}
|
|
||||||
for (const auto &c: ch2diff) {
|
|
||||||
diff += hashsize[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
return "at most " + BytesFormatter::formatStr(diff);
|
return "at most " + BytesFormatter::formatStr(diff);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ public:
|
|||||||
std::string getHash();
|
std::string getHash();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> mdctx{EVP_MD_CTX_new(), &EVP_MD_CTX_free};///< Current hashing context
|
const std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> mdctx{EVP_MD_CTX_new(),
|
||||||
|
&EVP_MD_CTX_free};///< Current hashing context
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,15 +23,16 @@ std::vector<char> AES::encrypt(const std::vector<char> &in, const std::array<uin
|
|||||||
if (!ctx) throw Exception("Error initializing encryption context!");
|
if (!ctx) throw Exception("Error initializing encryption context!");
|
||||||
|
|
||||||
std::vector<char> out(in.size() + AES_BLOCK_SIZE + 32);
|
std::vector<char> out(in.size() + AES_BLOCK_SIZE + 32);
|
||||||
if (!RAND_bytes(reinterpret_cast<unsigned char *>(out.data()), 32))
|
if (!RAND_bytes(reinterpret_cast<unsigned char *>(out.data()), 32)) throw Exception("Error generating IV!");
|
||||||
throw Exception("Error generating IV!");
|
|
||||||
|
|
||||||
if (!EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_cbc(), nullptr, key.data(), reinterpret_cast<const unsigned char *>(out.data())))
|
if (!EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_cbc(), nullptr, key.data(),
|
||||||
|
reinterpret_cast<const unsigned char *>(out.data())))
|
||||||
throw Exception("Error encrypting!");
|
throw Exception("Error encrypting!");
|
||||||
|
|
||||||
int outlen = static_cast<int>(out.size()) - 32;
|
int outlen = static_cast<int>(out.size()) - 32;
|
||||||
|
|
||||||
if (!EVP_EncryptUpdate(ctx.get(), reinterpret_cast<unsigned char *>(out.data() + 32), &outlen, reinterpret_cast<const unsigned char *>(in.data()), static_cast<int>(in.size())))
|
if (!EVP_EncryptUpdate(ctx.get(), reinterpret_cast<unsigned char *>(out.data() + 32), &outlen,
|
||||||
|
reinterpret_cast<const unsigned char *>(in.data()), static_cast<int>(in.size())))
|
||||||
throw Exception("Error encrypting!");
|
throw Exception("Error encrypting!");
|
||||||
|
|
||||||
int finlen = 0;
|
int finlen = 0;
|
||||||
@@ -52,11 +53,13 @@ std::vector<char> AES::decrypt(const std::vector<char> &in, const std::array<uin
|
|||||||
std::vector<char> out(in.size() - 32);
|
std::vector<char> out(in.size() - 32);
|
||||||
int outlen = static_cast<int>(out.size());
|
int outlen = static_cast<int>(out.size());
|
||||||
|
|
||||||
if (!EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_cbc(), nullptr, key.data(), reinterpret_cast<const unsigned char *>(in.data())))
|
if (!EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_cbc(), nullptr, key.data(),
|
||||||
|
reinterpret_cast<const unsigned char *>(in.data())))
|
||||||
throw Exception("Error decrypting!");
|
throw Exception("Error decrypting!");
|
||||||
|
|
||||||
|
|
||||||
if (!EVP_DecryptUpdate(ctx.get(), reinterpret_cast<unsigned char *>(out.data()), &outlen, reinterpret_cast<const unsigned char *>(in.data() + 32), static_cast<int>(in.size() - 32)))
|
if (!EVP_DecryptUpdate(ctx.get(), reinterpret_cast<unsigned char *>(out.data()), &outlen,
|
||||||
|
reinterpret_cast<const unsigned char *>(in.data() + 32), static_cast<int>(in.size() - 32)))
|
||||||
throw Exception("Error decrypting!");
|
throw Exception("Error decrypting!");
|
||||||
|
|
||||||
int finlen = 0;
|
int finlen = 0;
|
||||||
@@ -70,13 +73,9 @@ std::vector<char> AES::decrypt(const std::vector<char> &in, const std::array<uin
|
|||||||
|
|
||||||
std::array<uint8_t, 32> AES::deriveKey(const std::string &password, const std::string &salt) {
|
std::array<uint8_t, 32> AES::deriveKey(const std::string &password, const std::string &salt) {
|
||||||
std::array<uint8_t, 32> key;//NOLINT
|
std::array<uint8_t, 32> key;//NOLINT
|
||||||
if (!PKCS5_PBKDF2_HMAC_SHA1(password.data(),
|
if (!PKCS5_PBKDF2_HMAC_SHA1(password.data(), static_cast<int>(password.length()),
|
||||||
static_cast<int>(password.length()),
|
reinterpret_cast<const unsigned char *>(salt.data()), static_cast<int>(salt.length()),
|
||||||
reinterpret_cast<const unsigned char *>(salt.data()),
|
10000, 32, key.data()))
|
||||||
static_cast<int>(salt.length()),
|
|
||||||
10000,
|
|
||||||
32,
|
|
||||||
key.data()))
|
|
||||||
throw Exception("Error deriving key!");
|
throw Exception("Error deriving key!");
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,14 @@ std::string SHA::calculate(const std::vector<char> &in) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SHA::SHA() {
|
SHA::SHA() {
|
||||||
if (!mdctx)
|
if (!mdctx) throw Exception("Can't create hashing context!");
|
||||||
throw Exception("Can't create hashing context!");
|
|
||||||
|
|
||||||
if (!EVP_DigestInit_ex(mdctx.get(), EVP_sha256(), nullptr))
|
if (!EVP_DigestInit_ex(mdctx.get(), EVP_sha256(), nullptr)) throw Exception("Can't create hashing context!");
|
||||||
throw Exception("Can't create hashing context!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SHA::feedData(const std::vector<char> &in) {
|
void SHA::feedData(const std::vector<char> &in) {
|
||||||
if (in.empty()) return;
|
if (in.empty()) return;
|
||||||
if (!EVP_DigestUpdate(mdctx.get(), in.data(), in.size()))
|
if (!EVP_DigestUpdate(mdctx.get(), in.data(), in.size())) throw Exception("Error hashing!");
|
||||||
throw Exception("Error hashing!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SHA::getHash() {
|
std::string SHA::getHash() {
|
||||||
@@ -33,11 +30,9 @@ std::string SHA::getHash() {
|
|||||||
if (!EVP_DigestFinal_ex(mdctx.get(), reinterpret_cast<unsigned char *>(out.data()), &s))
|
if (!EVP_DigestFinal_ex(mdctx.get(), reinterpret_cast<unsigned char *>(out.data()), &s))
|
||||||
throw Exception("Error hashing!");
|
throw Exception("Error hashing!");
|
||||||
|
|
||||||
if (s != out.size())
|
if (s != out.size()) throw Exception("Error hashing!");
|
||||||
throw Exception("Error hashing!");
|
|
||||||
|
|
||||||
if (!EVP_MD_CTX_reset(mdctx.get()))
|
if (!EVP_MD_CTX_reset(mdctx.get())) throw Exception("Error hashing!");
|
||||||
throw Exception("Error hashing!");
|
|
||||||
|
|
||||||
return {out.begin(), out.end()};
|
return {out.begin(), out.end()};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
|
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
|||||||
@@ -6,13 +6,9 @@
|
|||||||
#include "CRC32.h"
|
#include "CRC32.h"
|
||||||
#include "Serialize.h"
|
#include "Serialize.h"
|
||||||
|
|
||||||
std::vector<char> CheckFilter::filterWrite(std::vector<char> from) const {
|
std::vector<char> CheckFilter::filterWrite(std::vector<char> from) const { return filterWriteStatic(std::move(from)); }
|
||||||
return filterWriteStatic(std::move(from));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> CheckFilter::filterRead(std::vector<char> from) const {
|
std::vector<char> CheckFilter::filterRead(std::vector<char> from) const { return filterReadStatic(std::move(from)); }
|
||||||
return filterReadStatic(std::move(from));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> CheckFilter::filterWriteStatic(std::vector<char> from) {
|
std::vector<char> CheckFilter::filterWriteStatic(std::vector<char> from) {
|
||||||
auto out = magic;
|
auto out = magic;
|
||||||
|
|||||||
@@ -6,12 +6,8 @@
|
|||||||
|
|
||||||
#include "AES.h"
|
#include "AES.h"
|
||||||
|
|
||||||
std::vector<char> FilterAES::filterWrite(std::vector<char> from) const {
|
std::vector<char> FilterAES::filterWrite(std::vector<char> from) const { return AES::encrypt(from, key); }
|
||||||
return AES::encrypt(from, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> FilterAES::filterRead(std::vector<char> from) const {
|
std::vector<char> FilterAES::filterRead(std::vector<char> from) const { return AES::decrypt(from, key); }
|
||||||
return AES::decrypt(from, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
FilterAES::FilterAES(const std::string &password, const std::string &salt) : key(AES::deriveKey(password, salt)) {}
|
FilterAES::FilterAES(const std::string &password, const std::string &salt) : key(AES::deriveKey(password, salt)) {}
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ FilterContainer &FilterContainer::addFilter(std::unique_ptr<Filter> &&f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> FilterContainer::filterWrite(std::vector<char> from) const {
|
std::vector<char> FilterContainer::filterWrite(std::vector<char> from) const {
|
||||||
for (auto const &f: filters) from = f->filterWrite(std::move(from));
|
for (auto const &f: filters) from = std::move(f->filterWrite(std::move(from)));
|
||||||
return from;
|
return from;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> FilterContainer::filterRead(std::vector<char> from) const {
|
std::vector<char> FilterContainer::filterRead(std::vector<char> from) const {
|
||||||
for (auto f = filters.crbegin(); f != filters.crend(); f++)
|
for (auto f = filters.crbegin(); f != filters.crend(); f++) from = std::move((*f)->filterRead(std::move(from)));
|
||||||
from = (*f)->filterRead(std::move(from));
|
|
||||||
return from;
|
return from;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ std::vector<char> FilterZlib::filterWrite(std::vector<char> from) const {
|
|||||||
|
|
||||||
out.resize(sizeSize + outSize);
|
out.resize(sizeSize + outSize);
|
||||||
|
|
||||||
if (compress2(reinterpret_cast<Bytef *>(out.data() + sizeSize), &outSize, reinterpret_cast<const Bytef *>(from.data()), from.size(), level) !=
|
if (compress2(reinterpret_cast<Bytef *>(out.data() + sizeSize), &outSize,
|
||||||
Z_OK)
|
reinterpret_cast<const Bytef *>(from.data()), from.size(), level) != Z_OK)
|
||||||
throw Exception("Error compressing!");
|
throw Exception("Error compressing!");
|
||||||
|
|
||||||
out.resize(outSize + sizeSize);
|
out.resize(outSize + sizeSize);
|
||||||
@@ -40,8 +40,8 @@ std::vector<char> FilterZlib::filterRead(std::vector<char> from) const {
|
|||||||
|
|
||||||
if (desI >= from.cend()) throw Exception("Unexpected end of archive!");
|
if (desI >= from.cend()) throw Exception("Unexpected end of archive!");
|
||||||
|
|
||||||
if (uncompress(reinterpret_cast<Bytef *>(out.data()), &size, reinterpret_cast<const Bytef *>(&(*desI)), std::distance(desI, from.cend())) !=
|
if (uncompress(reinterpret_cast<Bytef *>(out.data()), &size, reinterpret_cast<const Bytef *>(&(*desI)),
|
||||||
Z_OK)
|
std::distance(desI, from.cend())) != Z_OK)
|
||||||
throw Exception("Error decompressing!");
|
throw Exception("Error decompressing!");
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,10 @@
|
|||||||
#include "objects/Chunk.h"
|
#include "objects/Chunk.h"
|
||||||
|
|
||||||
DirEntry *getf(std::string path) {
|
DirEntry *getf(std::string path) {
|
||||||
auto p = std::filesystem::relative(std::filesystem::u8path(path), "/");
|
auto p = std::filesystem::relative(std::filesystem::path(path), "/");
|
||||||
DirEntry *entry = RepoFS::root.get();
|
DirEntry *entry = RepoFS::root.get();
|
||||||
if (p != ".")
|
if (p != ".")
|
||||||
for (auto const &subp: p) {
|
for (auto const &subp: p) { entry = entry->children.at(subp).get(); }
|
||||||
entry = entry->children.at(subp).get();
|
|
||||||
}
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,14 +51,12 @@ static int rfsGetattr(const char *path, struct stat *stbuf) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rfsReaddir(const char *path, void *buf, fuse_fill_dir_t filler,
|
static int rfsReaddir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
|
||||||
off_t offset, struct fuse_file_info *fi) {
|
|
||||||
(void) offset;
|
(void) offset;
|
||||||
(void) fi;
|
(void) fi;
|
||||||
|
|
||||||
DirEntry *entry = RepoFS::root.get();
|
DirEntry *entry = RepoFS::root.get();
|
||||||
if (std::string(path) != "/")
|
if (std::string(path) != "/") try {
|
||||||
try {
|
|
||||||
entry = getf(path);
|
entry = getf(path);
|
||||||
} catch (...) { return -ENOENT; }
|
} catch (...) { return -ENOENT; }
|
||||||
|
|
||||||
@@ -77,42 +73,35 @@ static int rfsReaddir(const char *path, void *buf, fuse_fill_dir_t filler,
|
|||||||
|
|
||||||
static int rfsOpen(const char *path, struct fuse_file_info *fi) {
|
static int rfsOpen(const char *path, struct fuse_file_info *fi) {
|
||||||
DirEntry *entry = RepoFS::root.get();
|
DirEntry *entry = RepoFS::root.get();
|
||||||
if (std::string(path) != "/")
|
if (std::string(path) != "/") try {
|
||||||
try {
|
|
||||||
entry = getf(path);
|
entry = getf(path);
|
||||||
} catch (...) { return -ENOENT; }
|
} catch (...) { return -ENOENT; }
|
||||||
|
|
||||||
if ((fi->flags & 3) != O_RDONLY)
|
if ((fi->flags & 3) != O_RDONLY) return -EACCES;
|
||||||
return -EACCES;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rfsRead(const char *path, char *buf, size_t size, off_t offset,
|
static int rfsRead(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
|
||||||
struct fuse_file_info *fi) {
|
|
||||||
size_t len;
|
size_t len;
|
||||||
(void) fi;
|
(void) fi;
|
||||||
DirEntry *entry = RepoFS::root.get();
|
DirEntry *entry = RepoFS::root.get();
|
||||||
if (std::string(path) != "/")
|
if (std::string(path) != "/") try {
|
||||||
try {
|
|
||||||
entry = getf(path);
|
entry = getf(path);
|
||||||
} catch (...) { return -ENOENT; }
|
} catch (...) { return -ENOENT; }
|
||||||
|
|
||||||
|
|
||||||
len = entry->file->bytes;
|
len = entry->file->bytes;
|
||||||
if (offset < len) {
|
if (offset < len) {
|
||||||
if (offset + size > len)
|
if (offset + size > len) size = len - offset;
|
||||||
size = len - offset;
|
|
||||||
|
|
||||||
auto curchunk = entry->file->chunks.upper_bound(offset);
|
auto curchunk = entry->file->chunks.upper_bound(offset);
|
||||||
if (curchunk == entry->file->chunks.begin()) {
|
if (curchunk == entry->file->chunks.begin()) { std::abort(); }
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
--curchunk;
|
--curchunk;
|
||||||
size_t curInBuf = 0;
|
size_t curInBuf = 0;
|
||||||
size_t curInChunk = offset - curchunk->first;
|
size_t curInChunk = offset - curchunk->first;
|
||||||
while (curInBuf < size) {
|
while (curInBuf < size) {
|
||||||
auto chunk = Serialize::deserialize<Chunk>(RepoFS::repo->getObject(curchunk->second));
|
auto chunk = Serialize::deserialize<Chunk>(RepoFS::repo->getObjectRaw(curchunk->second));
|
||||||
size_t read = std::min((size_t) chunk.length - curInChunk, size - curInBuf);
|
size_t read = std::min((size_t) chunk.length - curInChunk, size - curInBuf);
|
||||||
memcpy(buf + curInBuf, chunk.data.data() + curInChunk, read);
|
memcpy(buf + curInBuf, chunk.data.data() + curInChunk, read);
|
||||||
curInBuf += read;
|
curInBuf += read;
|
||||||
@@ -127,13 +116,12 @@ static int rfsRead(const char *path, char *buf, size_t size, off_t offset,
|
|||||||
|
|
||||||
static int rfsReadlink(const char *path, char *buf, size_t size) {
|
static int rfsReadlink(const char *path, char *buf, size_t size) {
|
||||||
DirEntry *entry = RepoFS::root.get();
|
DirEntry *entry = RepoFS::root.get();
|
||||||
if (std::string(path) != "/")
|
if (std::string(path) != "/") try {
|
||||||
try {
|
|
||||||
entry = getf(path);
|
entry = getf(path);
|
||||||
} catch (...) { return -ENOENT; }
|
} catch (...) { return -ENOENT; }
|
||||||
|
|
||||||
if (entry->file->fileType != File::Type::Symlink) return -ENOENT;
|
if (entry->file->fileType != File::Type::Symlink) return -ENOENT;
|
||||||
auto dst = Serialize::deserialize<Chunk>(RepoFS::repo->getObject(entry->file->chunks.at(0)));
|
auto dst = Serialize::deserialize<Chunk>(RepoFS::repo->getObjectRaw(entry->file->chunks.at(0)));
|
||||||
strncpy(buf, dst.data.data(), std::min(dst.data.size(), size));
|
strncpy(buf, dst.data.data(), std::min(dst.data.size(), size));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -151,22 +139,21 @@ void RepoFS::start(Repository *repo, std::string path) {
|
|||||||
RepoFS::repo = repo;
|
RepoFS::repo = repo;
|
||||||
auto ars = repo->getObjects(Object::ObjectType::Archive);
|
auto ars = repo->getObjects(Object::ObjectType::Archive);
|
||||||
for (auto const &r: ars) {
|
for (auto const &r: ars) {
|
||||||
auto a = Serialize::deserialize<Archive>(repo->getObject(r.second));
|
auto a = Serialize::deserialize<Archive>(repo->getObjectRaw(r.second));
|
||||||
for (auto const &f: a.files) {
|
for (auto const &f: a.files) {
|
||||||
auto file = Serialize::deserialize<File>(repo->getObject(f));
|
auto file = Serialize::deserialize<File>(repo->getObjectRaw(f));
|
||||||
auto path = std::filesystem::u8path(file.name);
|
auto path = std::filesystem::path(file.name);
|
||||||
DirEntry *entry = root->children[std::to_string(a.id)].get()
|
DirEntry *entry = root->children[std::to_string(a.id)].get()
|
||||||
? root->children[std::to_string(a.id)].get()
|
? root->children[std::to_string(a.id)].get()
|
||||||
: (root->children[std::to_string(a.id)] = std::make_unique<DirEntry>()).get();
|
: (root->children[std::to_string(a.id)] = std::make_unique<DirEntry>()).get();
|
||||||
entry->isFakeDir = true;
|
entry->isFakeDir = true;
|
||||||
entry->name = std::to_string(a.id);
|
entry->name = std::to_string(a.id);
|
||||||
for (auto const &subp: path) {
|
for (auto const &subp: path) {
|
||||||
entry = entry->children[subp].get()
|
entry = entry->children[subp].get() ? entry->children[subp].get()
|
||||||
? entry->children[subp].get()
|
: (entry->children[subp] = std::make_unique<DirEntry>()).get();
|
||||||
: (entry->children[subp] = std::make_unique<DirEntry>()).get();
|
|
||||||
}
|
}
|
||||||
entry->file.emplace(file);
|
entry->file.emplace(file);
|
||||||
entry->name = std::filesystem::u8path(file.name).filename().u8string();
|
entry->name = std::filesystem::path(file.name).filename().string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
src/main.cpp
21
src/main.cpp
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "BytesFormatter.h"
|
#include "BytesFormatter.h"
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
|
#include "CommandDelete.h"
|
||||||
#include "CommandDiff.h"
|
#include "CommandDiff.h"
|
||||||
#include "CommandList.h"
|
#include "CommandList.h"
|
||||||
#include "CommandListFiles.h"
|
#include "CommandListFiles.h"
|
||||||
@@ -36,8 +37,7 @@ Config getConf(int argc, char *argv[]) {
|
|||||||
int help() {
|
int help() {
|
||||||
for (auto const &o: Config::keys) {
|
for (auto const &o: Config::keys) {
|
||||||
std::cout << "--" << o.first << " <" << Config::KeyTypeToStr.at(o.second.type) << ">" << std::endl;
|
std::cout << "--" << o.first << " <" << Config::KeyTypeToStr.at(o.second.type) << ">" << std::endl;
|
||||||
if (o.second.defaultval.has_value())
|
if (o.second.defaultval.has_value()) std::cout << " Default: " << o.second.defaultval.value() << std::endl;
|
||||||
std::cout << " Default: " << o.second.defaultval.value() << std::endl;
|
|
||||||
std::cout << " Is saved in repository: " << (o.second.remember ? "yes" : "no") << std::endl;
|
std::cout << " Is saved in repository: " << (o.second.remember ? "yes" : "no") << std::endl;
|
||||||
std::cout << " Info: " << o.second.info << std::endl;
|
std::cout << " Info: " << o.second.info << std::endl;
|
||||||
}
|
}
|
||||||
@@ -80,23 +80,18 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string opt = argv[1];
|
std::string opt = argv[1];
|
||||||
if (opt == "help") {
|
if (opt == "help") { return help(); }
|
||||||
return help();
|
|
||||||
}
|
|
||||||
|
|
||||||
Config conf;
|
Config conf;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
conf = getConf(argc - 2, argv + 2);
|
conf = getConf(argc - 2, argv + 2);
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
std::cerr << "Error reading config!" << std::endl
|
std::cerr << "Error reading config!" << std::endl << e.what() << std::endl;
|
||||||
<< e.what() << std::endl;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt == "init") {
|
if (opt == "init") { return init(conf); }
|
||||||
return init(conf);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto repo = openRepo(conf);
|
auto repo = openRepo(conf);
|
||||||
|
|
||||||
@@ -115,6 +110,7 @@ int main(int argc, char *argv[]) {
|
|||||||
commands.emplace(CommandListFiles::name, std::make_unique<CommandListFiles>());
|
commands.emplace(CommandListFiles::name, std::make_unique<CommandListFiles>());
|
||||||
commands.emplace(CommandList::name, std::make_unique<CommandList>());
|
commands.emplace(CommandList::name, std::make_unique<CommandList>());
|
||||||
commands.emplace(CommandMount::name, std::make_unique<CommandMount>());
|
commands.emplace(CommandMount::name, std::make_unique<CommandMount>());
|
||||||
|
commands.emplace(CommandDelete::name, std::make_unique<CommandDelete>());
|
||||||
|
|
||||||
if (commands.count(opt) == 0) {
|
if (commands.count(opt) == 0) {
|
||||||
std::cerr << "Unknown argument" << std::endl;
|
std::cerr << "Unknown argument" << std::endl;
|
||||||
@@ -122,10 +118,7 @@ int main(int argc, char *argv[]) {
|
|||||||
} else {
|
} else {
|
||||||
commands.at(opt)->run(ctx);
|
commands.at(opt)->run(ctx);
|
||||||
}
|
}
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) { std::cerr << "Error!" << std::endl << e.what() << std::endl; } catch (...) {
|
||||||
std::cerr << "Error!" << std::endl
|
|
||||||
<< e.what() << std::endl;
|
|
||||||
} catch (...) {
|
|
||||||
std::cerr << "Something very bad happened!" << std::endl;
|
std::cerr << "Something very bad happened!" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_library(repo srcs/FileRepository.cpp srcs/Object.cpp srcs/Repository.cpp srcs/objects/Archive.cpp srcs/objects/Chunk.cpp srcs/objects/File.cpp srcs/objects/FileBuffer.cpp)
|
add_library(repo srcs/FileRepository.cpp srcs/Object.cpp srcs/Repository.cpp srcs/objects/Archive.cpp srcs/objects/Chunk.cpp srcs/objects/File.cpp srcs/objects/FileBuffer.cpp)
|
||||||
|
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ public:
|
|||||||
bool init() override;
|
bool init() override;
|
||||||
bool flush() override;
|
bool flush() override;
|
||||||
|
|
||||||
std::vector<char> getObject(Object::idType id) const override;
|
std::vector<char> getObjectRaw(Object::idType id) const override;
|
||||||
bool putObject(const Object &obj) override;
|
bool putObject(const Object &obj) override;
|
||||||
bool deleteObject(const Object &obj) override;
|
bool deleteObjects(const std::vector<Object::idType> &objs) override;
|
||||||
|
|
||||||
std::vector<char> getObject(Object::ObjectType type, const std::string &key) const override;
|
std::vector<char> getObjectRaw(Object::ObjectType type, const std::string &key) const override;
|
||||||
Object::idType getObjectId(Object::ObjectType type, const std::string &key) const override;
|
Object::idType getObjectId(Object::ObjectType type, const std::string &key) const override;
|
||||||
std::vector<std::pair<std::string, Object::idType>> getObjects(Object::ObjectType type) const override;
|
std::vector<std::pair<std::string, Object::idType>> getObjects(Object::ObjectType type) const override;
|
||||||
|
|
||||||
@@ -72,7 +72,8 @@ private:
|
|||||||
/// \param size Amount of bytes to read (no more than absoluteMaxFileLimit)
|
/// \param size Amount of bytes to read (no more than absoluteMaxFileLimit)
|
||||||
/// \return Vector of bytes of the file
|
/// \return Vector of bytes of the file
|
||||||
/// \throws Exception on any error, or when absoluteMaxFileLimit is reached
|
/// \throws Exception on any error, or when absoluteMaxFileLimit is reached
|
||||||
std::vector<char> readFile(const std::filesystem::path &file, unsigned long long offset, unsigned long long size) const;
|
std::vector<char> readFile(const std::filesystem::path &file, unsigned long long offset,
|
||||||
|
unsigned long long size) const;
|
||||||
static constexpr unsigned long long absoluteMaxFileLimit{4ULL * 1024 * 1024 * 1024};///<Max file read size (4GB)
|
static constexpr unsigned long long absoluteMaxFileLimit{4ULL * 1024 * 1024 * 1024};///<Max file read size (4GB)
|
||||||
|
|
||||||
/// Writes \p data to \p file
|
/// Writes \p data to \p file
|
||||||
@@ -103,19 +104,26 @@ private:
|
|||||||
|
|
||||||
unsigned long long maxFileId = 1; ///< Largest ID of object storage file
|
unsigned long long maxFileId = 1; ///< Largest ID of object storage file
|
||||||
std::unordered_map<Object::idType, OffsetEntry> offsetIndex;///< Used to locate Object%s in the filesystem
|
std::unordered_map<Object::idType, OffsetEntry> offsetIndex;///< Used to locate Object%s in the filesystem
|
||||||
|
std::unordered_map<Object::idType, std::set<Object::idType>>
|
||||||
|
fileToObjs;///< Used to locate Object%s in the filesystem
|
||||||
|
|
||||||
std::mutex writeCacheLock; ///< Write cache lock
|
std::mutex writeCacheLock; ///< Write cache lock
|
||||||
std::map<Object::idType, std::vector<char>> writeCache;///< Write cache, map of Object ids and their serialized data
|
std::map<Object::idType, std::vector<char>> writeCache;///< Write cache, map of Object ids and their serialized data
|
||||||
unsigned long long writeCacheSize = 0; ///< Current byte size of the write cache
|
unsigned long long writeCacheSize = 0; ///< Current byte size of the write cache
|
||||||
const unsigned long long writeCacheMax; ///< Target size of the write cache, it is automatically flushed after this is reached
|
const unsigned long long
|
||||||
|
writeCacheMax;///< Target size of the write cache, it is automatically flushed after this is reached
|
||||||
|
|
||||||
/// Flushes the write cache
|
/// Flushes the write cache
|
||||||
/// Takes the cache lock, swaps the cache with an empty one and unlocks it
|
/// Takes the cache lock, swaps the cache with an empty one and unlocks it
|
||||||
/// \param lockW Write cache lock
|
/// \param lockW Write cache lock
|
||||||
void flushWriteCache(std::unique_lock<std::mutex> &&lockW);
|
void flushWriteCache(std::unique_lock<std::mutex> &&lockW);
|
||||||
|
|
||||||
Object::idType largestUnusedId = 1; ///< Largest available objectID
|
Object::idType largestUnusedId = 1; ///< Largest available objectID
|
||||||
std::unordered_map<Object::ObjectType, std::unordered_map<std::string, Object::idType>> keyIndex;///< Maps Object%'s keys to their ID's
|
std::vector<Object::idType> unusedIds;///< Vector of unused IDs
|
||||||
|
std::unordered_map<Object::ObjectType, std::unordered_map<std::string, Object::idType>>
|
||||||
|
keyIndex;///< Maps Object%'s keys to their ID's
|
||||||
|
|
||||||
|
std::unordered_map<Object::idType, uint64_t> refCounts;///< Count of references to an object per its id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,7 @@ class Object {
|
|||||||
public:
|
public:
|
||||||
using idType = uint64_t;///< Type alias for Object%'s ID
|
using idType = uint64_t;///< Type alias for Object%'s ID
|
||||||
|
|
||||||
enum class ObjectType {
|
enum class ObjectType { Archive, File, Chunk, END };
|
||||||
Archive,
|
|
||||||
File,
|
|
||||||
Chunk,
|
|
||||||
END
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Serializes the object to \p out
|
/// Serializes the object to \p out
|
||||||
virtual void serialize(std::vector<char> &out) const;
|
virtual void serialize(std::vector<char> &out) const;
|
||||||
@@ -36,9 +31,16 @@ public:
|
|||||||
/// All derived objects should implement this method
|
/// All derived objects should implement this method
|
||||||
virtual std::string getKey() const = 0;
|
virtual std::string getKey() const = 0;
|
||||||
|
|
||||||
|
/// Returns the keys of that this object refers to
|
||||||
|
virtual const std::vector<idType> &getRefs() const;
|
||||||
|
|
||||||
const idType id; ///< Unique numerical of the object
|
const idType id; ///< Unique numerical of the object
|
||||||
const ObjectType type;///< Type of the object
|
const ObjectType type;///< Type of the object
|
||||||
|
|
||||||
|
static std::unique_ptr<Object> deserialize(const std::vector<char> &src);
|
||||||
|
static std::unique_ptr<Object> deserialize(std::vector<char>::const_iterator &in,
|
||||||
|
const std::vector<char>::const_iterator &end);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Default constructor
|
/// Default constructor
|
||||||
/// \param id Object ID
|
/// \param id Object ID
|
||||||
|
|||||||
@@ -45,7 +45,13 @@ public:
|
|||||||
/// \param id ID of object to return
|
/// \param id ID of object to return
|
||||||
/// \return Serialized object
|
/// \return Serialized object
|
||||||
/// \throws Exception on any error or if object doesn't exist
|
/// \throws Exception on any error or if object doesn't exist
|
||||||
virtual std::vector<char> getObject(Object::idType id) const = 0;
|
virtual std::vector<char> getObjectRaw(Object::idType id) const = 0;
|
||||||
|
|
||||||
|
/// Returns the Object with id \p id
|
||||||
|
/// \param id ID of object to return
|
||||||
|
/// \return Serialized object
|
||||||
|
/// \throws Exception on any error or if object doesn't exist
|
||||||
|
std::unique_ptr<Object> getObject(Object::idType id) const;
|
||||||
|
|
||||||
/// Adds the Object \p obj to the Repository
|
/// Adds the Object \p obj to the Repository
|
||||||
/// \param obj Constant reference to the object
|
/// \param obj Constant reference to the object
|
||||||
@@ -54,17 +60,17 @@ public:
|
|||||||
virtual bool putObject(const Object &obj) = 0;
|
virtual bool putObject(const Object &obj) = 0;
|
||||||
|
|
||||||
/// Deletes Object \p obj from the Repository
|
/// Deletes Object \p obj from the Repository
|
||||||
/// \param obj Constant reference to the object
|
/// \param obj Constant reference to the vector with ids of objects to delete
|
||||||
/// \return True if successful, False if it didn't exist
|
/// \return True if successful, False if it didn't exist
|
||||||
/// \throws Exception on any error
|
/// \throws Exception on any error
|
||||||
virtual bool deleteObject(const Object &obj) = 0;
|
virtual bool deleteObjects(const std::vector<Object::idType> &objs) = 0;
|
||||||
|
|
||||||
/// Returns the Object of type \p type and with key \p key
|
/// Returns the Object of type \p type and with key \p key
|
||||||
/// \param type Type of the object
|
/// \param type Type of the object
|
||||||
/// \param key Constant reference to the key of the object
|
/// \param key Constant reference to the key of the object
|
||||||
/// \return Serialized object
|
/// \return Serialized object
|
||||||
/// \throws Exception on any error or if object doesn't exist
|
/// \throws Exception on any error or if object doesn't exist
|
||||||
virtual std::vector<char> getObject(Object::ObjectType type, const std::string &key) const = 0;
|
virtual std::vector<char> getObjectRaw(Object::ObjectType type, const std::string &key) const = 0;
|
||||||
|
|
||||||
/// Returns the id of an Object of type \p type and with key \p key
|
/// Returns the id of an Object of type \p type and with key \p key
|
||||||
/// \param type Type of the object
|
/// \param type Type of the object
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include "../Object.h"
|
#include "../Object.h"
|
||||||
|
|
||||||
/// Object representing a backup
|
/// Object representing a backup
|
||||||
class Archive : public Object {
|
class Archive final : public Object {
|
||||||
public:
|
public:
|
||||||
Archive(Object::idType id, std::string name, unsigned long long mtime, std::vector<idType> files);
|
Archive(Object::idType id, std::string name, unsigned long long mtime, std::vector<idType> files);
|
||||||
|
|
||||||
@@ -22,6 +22,9 @@ public:
|
|||||||
/// Returns the name of the archive
|
/// Returns the name of the archive
|
||||||
std::string getKey() const override;
|
std::string getKey() const override;
|
||||||
|
|
||||||
|
/// Returns the files in this archive
|
||||||
|
const std::vector<Object::idType> &getRefs() const override;
|
||||||
|
|
||||||
const std::string name; ///< Archive name
|
const std::string name; ///< Archive name
|
||||||
const unsigned long long mtime; ///< Time of creation
|
const unsigned long long mtime; ///< Time of creation
|
||||||
const std::vector<idType> files;///< List of ids of File objects in the Archive
|
const std::vector<idType> files;///< List of ids of File objects in the Archive
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include "../Object.h"
|
#include "../Object.h"
|
||||||
|
|
||||||
/// Object representing a part of a File
|
/// Object representing a part of a File
|
||||||
class Chunk : public Object {
|
class Chunk final : public Object {
|
||||||
public:
|
public:
|
||||||
Chunk(idType id, std::string, std::vector<char> data);
|
Chunk(idType id, std::string, std::vector<char> data);
|
||||||
|
|
||||||
|
|||||||
@@ -15,18 +15,16 @@
|
|||||||
#include "../Object.h"
|
#include "../Object.h"
|
||||||
|
|
||||||
/// Object representing a saved file
|
/// Object representing a saved file
|
||||||
class File : public Object {
|
class File final : public Object {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type { Normal, Symlink, Directory, END };
|
||||||
Normal,
|
|
||||||
Symlink,
|
|
||||||
Directory,
|
|
||||||
END
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline const std::unordered_map<Type, std::string> TypeToStr{{Type::Normal, "normal"}, {Type::Symlink, "symlink"}, {Type::Directory, "directory"}};
|
static inline const std::unordered_map<Type, std::string> TypeToStr{{Type::Normal, "normal"},
|
||||||
|
{Type::Symlink, "symlink"},
|
||||||
|
{Type::Directory, "directory"}};
|
||||||
|
|
||||||
File(Object::idType id, std::string name, unsigned long long bytes, unsigned long long mtime, std::string SHA, std::map<size_t, idType> chunks, Type fileType);
|
File(Object::idType id, std::string name, unsigned long long bytes, unsigned long long mtime, std::string SHA,
|
||||||
|
std::map<size_t, idType> chunks, Type fileType);
|
||||||
|
|
||||||
/// Deserialization constructor
|
/// Deserialization constructor
|
||||||
File(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end);
|
File(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end);
|
||||||
@@ -70,6 +68,12 @@ public:
|
|||||||
/// List of the chunks in file
|
/// List of the chunks in file
|
||||||
/// Normal file has normal chunks as its contents, for Directory it's empty, Symlink has a chunk with its target path
|
/// Normal file has normal chunks as its contents, for Directory it's empty, Symlink has a chunk with its target path
|
||||||
const std::map<size_t, idType> chunks;
|
const std::map<size_t, idType> chunks;
|
||||||
|
|
||||||
|
const std::vector<idType> &getRefs() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void makeChunksList() const;
|
||||||
|
mutable std::optional<std::vector<idType>> chunksList{std::nullopt};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,19 +5,21 @@
|
|||||||
#include "FileRepository.h"
|
#include "FileRepository.h"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include "CheckFilter.h"
|
#include "CheckFilter.h"
|
||||||
#include "FilterFactory.h"
|
#include "FilterFactory.h"
|
||||||
#include "Object.h"
|
#include "Object.h"
|
||||||
#include "Serialize.h"
|
#include "Serialize.h"
|
||||||
|
|
||||||
FileRepository::FileRepository(Config config) : Repository(std::move(config)), root(std::filesystem::path(this->config.getStr("repo"))), writeCacheMax(config.getInt("repo-target") * 1024 * 1024) {}
|
FileRepository::FileRepository(Config config)
|
||||||
|
: Repository(std::move(config)), root(std::filesystem::path(this->config.getStr("repo"))),
|
||||||
|
writeCacheMax(this->config.getInt("repo-target") * 1024 * 1024) {}
|
||||||
|
|
||||||
bool FileRepository::exists() {
|
bool FileRepository::exists() { return std::filesystem::is_directory(root) && std::filesystem::exists(root / "info"); }
|
||||||
return std::filesystem::is_directory(root) && std::filesystem::exists(root / "info");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileRepository::flush() {
|
bool FileRepository::flush() {
|
||||||
flushWriteCache(std::unique_lock(writeCacheLock));
|
flushWriteCache(std::unique_lock(writeCacheLock));
|
||||||
@@ -31,14 +33,23 @@ bool FileRepository::open() {
|
|||||||
std::swap(config, readConf);
|
std::swap(config, readConf);
|
||||||
config.merge(readConf);
|
config.merge(readConf);
|
||||||
|
|
||||||
if (config.getStr("compression") != "none") filters.addFilter(FilterFactory::makeFilter(config.getStr("compression"), config));
|
if (config.getStr("compression") != "none")
|
||||||
if (config.getStr("encryption") != "none") filters.addFilter(FilterFactory::makeFilter(config.getStr("encryption"), config));
|
filters.addFilter(FilterFactory::makeFilter(config.getStr("compression"), config));
|
||||||
|
if (config.getStr("encryption") != "none")
|
||||||
|
filters.addFilter(FilterFactory::makeFilter(config.getStr("encryption"), config));
|
||||||
filters.addFilter(FilterFactory::makeFilter("crc", config));
|
filters.addFilter(FilterFactory::makeFilter("crc", config));
|
||||||
|
|
||||||
ready = true;
|
ready = true;
|
||||||
try {
|
try {
|
||||||
std::tie(maxFileId, offsetIndex) = Serialize::deserialize<std::pair<decltype(maxFileId), decltype(offsetIndex)>>(filters.filterRead(readFile(root / "offsets")));
|
std::tie(maxFileId, offsetIndex) =
|
||||||
std::tie(keyIndex, largestUnusedId) = Serialize::deserialize<std::pair<decltype(keyIndex), decltype(largestUnusedId)>>(filters.filterRead(readFile(root / "index")));
|
Serialize::deserialize<std::pair<decltype(maxFileId), decltype(offsetIndex)>>(
|
||||||
|
filters.filterRead(readFile(root / "offsets")));
|
||||||
|
std::tie(keyIndex, largestUnusedId) =
|
||||||
|
Serialize::deserialize<std::pair<decltype(keyIndex), decltype(largestUnusedId)>>(
|
||||||
|
filters.filterRead(readFile(root / "index")));
|
||||||
|
refCounts = Serialize::deserialize<decltype(refCounts)>(filters.filterRead(readFile(root / "refcounts")));
|
||||||
|
unusedIds = Serialize::deserialize<decltype(unusedIds)>(filters.filterRead(readFile(root / "unusedIds")));
|
||||||
|
fileToObjs = Serialize::deserialize<decltype(fileToObjs)>(filters.filterRead(readFile(root / "fileToObjs")));
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
ready = false;
|
ready = false;
|
||||||
throw;
|
throw;
|
||||||
@@ -52,12 +63,14 @@ bool FileRepository::init() {
|
|||||||
if (exists()) throw Exception("Trying to initialize already existing repository!");
|
if (exists()) throw Exception("Trying to initialize already existing repository!");
|
||||||
|
|
||||||
if (!std::filesystem::is_directory(root) && !std::filesystem::create_directories(root))
|
if (!std::filesystem::is_directory(root) && !std::filesystem::create_directories(root))
|
||||||
throw Exception("Can't create directory " + root.u8string());
|
throw Exception("Can't create directory " + root.string());
|
||||||
|
|
||||||
writeFile(root / "info", CheckFilter::filterWriteStatic(Serialize::serialize(config)));
|
writeFile(root / "info", CheckFilter::filterWriteStatic(Serialize::serialize(config)));
|
||||||
|
|
||||||
if (config.getStr("compression") != "none") filters.addFilter(FilterFactory::makeFilter(config.getStr("compression"), config));
|
if (config.getStr("compression") != "none")
|
||||||
if (config.getStr("encryption") != "none") filters.addFilter(FilterFactory::makeFilter(config.getStr("encryption"), config));
|
filters.addFilter(FilterFactory::makeFilter(config.getStr("compression"), config));
|
||||||
|
if (config.getStr("encryption") != "none")
|
||||||
|
filters.addFilter(FilterFactory::makeFilter(config.getStr("encryption"), config));
|
||||||
filters.addFilter(FilterFactory::makeFilter("crc", config));
|
filters.addFilter(FilterFactory::makeFilter("crc", config));
|
||||||
|
|
||||||
ready = true;
|
ready = true;
|
||||||
@@ -71,15 +84,17 @@ FileRepository::~FileRepository() {
|
|||||||
|
|
||||||
writeFile(root / "offsets", filters.filterWrite(Serialize::serialize(std::make_pair(maxFileId, offsetIndex))));
|
writeFile(root / "offsets", filters.filterWrite(Serialize::serialize(std::make_pair(maxFileId, offsetIndex))));
|
||||||
writeFile(root / "index", filters.filterWrite(Serialize::serialize(std::make_pair(keyIndex, largestUnusedId))));
|
writeFile(root / "index", filters.filterWrite(Serialize::serialize(std::make_pair(keyIndex, largestUnusedId))));
|
||||||
|
writeFile(root / "unusedIds", filters.filterWrite(Serialize::serialize(unusedIds)));
|
||||||
|
writeFile(root / "refcounts", filters.filterWrite(Serialize::serialize(refCounts)));
|
||||||
|
writeFile(root / "fileToObjs", filters.filterWrite(Serialize::serialize(fileToObjs)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> FileRepository::getObject(Object::idType id) const {
|
std::vector<char> FileRepository::getObjectRaw(Object::idType id) const {
|
||||||
if (!ready) throw Exception("Tried working with uninitialized repo!");
|
if (!ready) throw Exception("Tried working with uninitialized repo!");
|
||||||
|
|
||||||
std::unique_lock lock(repoLock);
|
std::unique_lock lock(repoLock);
|
||||||
if (offsetIndex.count(id) == 0)
|
if (offsetIndex.count(id) == 0) throw Exception("Object with id " + std::to_string(id) + " doesn't exist!");
|
||||||
throw Exception("Object with id " + std::to_string(id) + " doesn't exist!");
|
|
||||||
auto entry = offsetIndex.at(id);
|
auto entry = offsetIndex.at(id);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@@ -95,9 +110,7 @@ bool FileRepository::writeObject(const Object &obj) {
|
|||||||
writeCache[obj.id] = std::move(tmp);
|
writeCache[obj.id] = std::move(tmp);
|
||||||
|
|
||||||
// If we have reached the target file size, flush the cache
|
// If we have reached the target file size, flush the cache
|
||||||
if (writeCacheSize >= writeCacheMax) {
|
if (writeCacheSize >= writeCacheMax) { flushWriteCache(std::move(lockW)); }
|
||||||
flushWriteCache(std::move(lockW));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -128,6 +141,7 @@ void FileRepository::flushWriteCache(std::unique_lock<std::mutex> &&lockW) {
|
|||||||
{
|
{
|
||||||
std::lock_guard lockI(repoLock);
|
std::lock_guard lockI(repoLock);
|
||||||
offsetIndex.emplace(i.first, OffsetEntry(currentFileId, offset, i.second.size()));
|
offsetIndex.emplace(i.first, OffsetEntry(currentFileId, offset, i.second.size()));
|
||||||
|
fileToObjs[currentFileId].emplace(i.first);
|
||||||
}
|
}
|
||||||
offset += i.second.size();
|
offset += i.second.size();
|
||||||
ofstream.rdbuf()->sputn(i.second.data(), i.second.size());
|
ofstream.rdbuf()->sputn(i.second.data(), i.second.size());
|
||||||
@@ -139,34 +153,103 @@ bool FileRepository::putObject(const Object &obj) {
|
|||||||
{
|
{
|
||||||
std::lock_guard lock(repoLock);
|
std::lock_guard lock(repoLock);
|
||||||
keyIndex[obj.type][obj.getKey()] = obj.id;
|
keyIndex[obj.type][obj.getKey()] = obj.id;
|
||||||
|
for (auto const &i: obj.getRefs()) refCounts[i]++;
|
||||||
}
|
}
|
||||||
writeObject(obj);
|
writeObject(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileRepository::deleteObject(const Object &obj) {
|
bool FileRepository::deleteObjects(const std::vector<Object::idType> &objs) {
|
||||||
if (!ready) throw Exception("Tried working with uninitialized repo!");
|
if (!ready) throw Exception("Tried working with uninitialized repo!");
|
||||||
throw Exception("Deletion not implemented!");
|
|
||||||
|
std::queue<Object::idType> toVisit;
|
||||||
|
std::set<Object::idType> toDelete;
|
||||||
|
|
||||||
|
for (auto const &o: objs) {
|
||||||
|
toVisit.emplace(o);
|
||||||
|
toDelete.emplace(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Scanning for objects" << std::endl;
|
||||||
|
|
||||||
|
while (!toVisit.empty()) {
|
||||||
|
auto o = toVisit.back();
|
||||||
|
toVisit.pop();
|
||||||
|
|
||||||
|
auto obj = getObject(o);
|
||||||
|
for (const auto &id: obj->getRefs()) {
|
||||||
|
std::unique_lock lock(repoLock);
|
||||||
|
refCounts[id]--;
|
||||||
|
if (refCounts.at(id) == 0) {
|
||||||
|
toDelete.emplace(id);
|
||||||
|
toVisit.emplace(id);
|
||||||
|
refCounts.erase(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Found " << toDelete.size() << " to delete " << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
std::unordered_map<uint64_t, Object::idType> fileToObj;
|
||||||
|
std::set<uint64_t> touchedFiles;
|
||||||
|
|
||||||
|
for (auto const &id: toDelete) {
|
||||||
|
fileToObj.emplace(offsetIndex.at(id).fileId, id);
|
||||||
|
touchedFiles.emplace(offsetIndex.at(id).fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Will rewrite " << touchedFiles.size() << " files" << std::endl;
|
||||||
|
|
||||||
|
for (auto const &f: touchedFiles) {
|
||||||
|
std::cout << "Rewriting file " << f << std::endl;
|
||||||
|
const auto &objs = fileToObjs.at(f);
|
||||||
|
std::vector<std::unique_ptr<Object>> objects;
|
||||||
|
for (auto const &o: objs) {
|
||||||
|
auto obj = getObject(o);
|
||||||
|
{
|
||||||
|
std::unique_lock lock(repoLock);
|
||||||
|
offsetIndex.erase(o);
|
||||||
|
}
|
||||||
|
if (toDelete.find(o) == toDelete.end()) putObject(*obj);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::unique_lock lock(repoLock);
|
||||||
|
fileToObjs.erase(f);
|
||||||
|
}
|
||||||
|
std::filesystem::remove(root / std::to_string(f));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::unique_lock lock(repoLock);
|
||||||
|
for (auto const &id: toDelete) {
|
||||||
|
unusedIds.emplace_back(id);
|
||||||
|
// FIXME: this is a bit inefficient
|
||||||
|
for (auto &m: keyIndex) erase_if(m.second, [&](const auto &t) { return toDelete.contains(t.second); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> FileRepository::readFile(const std::filesystem::path &file, unsigned long long offset, unsigned long long size) const {
|
std::vector<char> FileRepository::readFile(const std::filesystem::path &file, unsigned long long offset,
|
||||||
if (size > absoluteMaxFileLimit) throw Exception("Tried to read " + std::to_string(size) +
|
unsigned long long size) const {
|
||||||
" bytes from " + file.u8string() +
|
if (size > absoluteMaxFileLimit)
|
||||||
" which is more than absoluteMaxFileLimit");
|
throw Exception("Tried to read " + std::to_string(size) + " bytes from " + file.string() +
|
||||||
|
" which is more than absoluteMaxFileLimit");
|
||||||
|
|
||||||
std::ifstream ifstream(file, std::ios::binary | std::ios::in);
|
std::ifstream ifstream(file, std::ios::binary | std::ios::in);
|
||||||
if (!ifstream.is_open()) throw Exception("Can't open file " + file.u8string() + " for reading!");
|
if (!ifstream.is_open()) throw Exception("Can't open file " + file.string() + " for reading!");
|
||||||
|
|
||||||
std::vector<char> buf(size);
|
std::vector<char> buf(size);
|
||||||
|
|
||||||
if (ifstream.rdbuf()->pubseekpos(offset) == std::streampos(std::streamoff(-1))) throw Exception("Unexpected end of file " + file.u8string());
|
if (ifstream.rdbuf()->pubseekpos(offset) == std::streampos(std::streamoff(-1)))
|
||||||
if (ifstream.rdbuf()->sgetn(buf.data(), size) != size) throw Exception("Unexpected end of file " + file.u8string());
|
throw Exception("Unexpected end of file " + file.string());
|
||||||
|
if (ifstream.rdbuf()->sgetn(buf.data(), size) != size) throw Exception("Unexpected end of file " + file.string());
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> FileRepository::readFile(const std::filesystem::path &file) const {
|
std::vector<char> FileRepository::readFile(const std::filesystem::path &file) const {
|
||||||
if (!std::filesystem::is_regular_file(file)) throw Exception("File " + file.u8string() + " is not a regular file!");
|
if (!std::filesystem::is_regular_file(file)) throw Exception("File " + file.string() + " is not a regular file!");
|
||||||
auto fileSize = std::filesystem::file_size(file);
|
auto fileSize = std::filesystem::file_size(file);
|
||||||
if (fileSize == 0) return {};
|
if (fileSize == 0) return {};
|
||||||
return readFile(file, 0, fileSize);
|
return readFile(file, 0, fileSize);
|
||||||
@@ -174,15 +257,15 @@ std::vector<char> FileRepository::readFile(const std::filesystem::path &file) co
|
|||||||
|
|
||||||
bool FileRepository::writeFile(const std::filesystem::path &file, const std::vector<char> &data) {
|
bool FileRepository::writeFile(const std::filesystem::path &file, const std::vector<char> &data) {
|
||||||
std::ofstream ofstream(file, std::ios::binary | std::ios::trunc | std::ios::out);
|
std::ofstream ofstream(file, std::ios::binary | std::ios::trunc | std::ios::out);
|
||||||
if (!ofstream.is_open()) throw Exception("Can't open file " + file.u8string() + " for writing!");
|
if (!ofstream.is_open()) throw Exception("Can't open file " + file.string() + " for writing!");
|
||||||
|
|
||||||
if (ofstream.rdbuf()->sputn(data.data(), data.size()) != data.size())
|
if (ofstream.rdbuf()->sputn(data.data(), data.size()) != data.size())
|
||||||
throw Exception("Couldn't write all the data for " + file.u8string());
|
throw Exception("Couldn't write all the data for " + file.string());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> FileRepository::getObject(Object::ObjectType type, const std::string &key) const {
|
std::vector<char> FileRepository::getObjectRaw(Object::ObjectType type, const std::string &key) const {
|
||||||
return getObject(getObjectId(type, key));
|
return getObjectRaw(getObjectId(type, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::idType FileRepository::getObjectId(Object::ObjectType type, const std::string &key) const {
|
Object::idType FileRepository::getObjectId(Object::ObjectType type, const std::string &key) const {
|
||||||
@@ -195,8 +278,7 @@ std::vector<std::pair<std::string, Object::idType>> FileRepository::getObjects(O
|
|||||||
std::lock_guard lock(repoLock);
|
std::lock_guard lock(repoLock);
|
||||||
std::vector<std::pair<std::string, Object::idType>> out;
|
std::vector<std::pair<std::string, Object::idType>> out;
|
||||||
if (keyIndex.count(type) == 0) return {};
|
if (keyIndex.count(type) == 0) return {};
|
||||||
for (auto const &i: keyIndex.at(type))
|
for (auto const &i: keyIndex.at(type)) out.emplace_back(i);
|
||||||
out.emplace_back(i);
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,14 +290,19 @@ bool FileRepository::exists(Object::ObjectType type, const std::string &key) con
|
|||||||
|
|
||||||
Object::idType FileRepository::getId() {
|
Object::idType FileRepository::getId() {
|
||||||
std::lock_guard lock(repoLock);
|
std::lock_guard lock(repoLock);
|
||||||
|
if (!unusedIds.empty()) {
|
||||||
|
auto ret = unusedIds.back();
|
||||||
|
unusedIds.pop_back();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
return largestUnusedId++;
|
return largestUnusedId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileRepository::OffsetEntry::OffsetEntry(std::vector<char, std::allocator<char>>::const_iterator &in, const std::vector<char, std::allocator<char>>::const_iterator &end)
|
FileRepository::OffsetEntry::OffsetEntry(std::vector<char, std::allocator<char>>::const_iterator &in,
|
||||||
|
const std::vector<char, std::allocator<char>>::const_iterator &end)
|
||||||
: fileId(Serialize::deserialize<decltype(fileId)>(in, end)),
|
: fileId(Serialize::deserialize<decltype(fileId)>(in, end)),
|
||||||
offset(Serialize::deserialize<decltype(offset)>(in, end)),
|
offset(Serialize::deserialize<decltype(offset)>(in, end)),
|
||||||
length(Serialize::deserialize<decltype(length)>(in, end)) {
|
length(Serialize::deserialize<decltype(length)>(in, end)) {}
|
||||||
}
|
|
||||||
|
|
||||||
void FileRepository::OffsetEntry::serialize(std::vector<char> &out) const {
|
void FileRepository::OffsetEntry::serialize(std::vector<char> &out) const {
|
||||||
Serialize::serialize(fileId, out);
|
Serialize::serialize(fileId, out);
|
||||||
@@ -223,5 +310,6 @@ void FileRepository::OffsetEntry::serialize(std::vector<char> &out) const {
|
|||||||
Serialize::serialize(length, out);
|
Serialize::serialize(length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileRepository::OffsetEntry::OffsetEntry(unsigned long long int fileId, unsigned long long int offset, unsigned long long int length)
|
FileRepository::OffsetEntry::OffsetEntry(unsigned long long int fileId, unsigned long long int offset,
|
||||||
|
unsigned long long int length)
|
||||||
: fileId(fileId), offset(offset), length(length) {}
|
: fileId(fileId), offset(offset), length(length) {}
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
|
|
||||||
#include "Serialize.h"
|
#include "Serialize.h"
|
||||||
|
|
||||||
|
#include "objects/Archive.h"
|
||||||
|
#include "objects/Chunk.h"
|
||||||
|
#include "objects/File.h"
|
||||||
|
|
||||||
Object::Object(idType id, ObjectType type) : id(id), type(type) {}
|
Object::Object(idType id, ObjectType type) : id(id), type(type) {}
|
||||||
|
|
||||||
Object::Object(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
Object::Object(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
||||||
: id(Serialize::deserialize<idType>(in, end)),
|
: id(Serialize::deserialize<idType>(in, end)), type(Serialize::deserialize<ObjectType>(in, end)) {}
|
||||||
type(Serialize::deserialize<ObjectType>(in, end)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Object::serialize(std::vector<char> &out) const {
|
void Object::serialize(std::vector<char> &out) const {
|
||||||
Serialize::serialize(id, out);
|
Serialize::serialize(id, out);
|
||||||
@@ -19,3 +21,32 @@ void Object::serialize(std::vector<char> &out) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object::~Object() = default;
|
Object::~Object() = default;
|
||||||
|
|
||||||
|
static std::vector<Object::idType> emptyRef{};
|
||||||
|
|
||||||
|
const std::vector<Object::idType> &Object::getRefs() const { return emptyRef; }
|
||||||
|
|
||||||
|
std::unique_ptr<Object> Object::deserialize(std::vector<char>::const_iterator &in,
|
||||||
|
const std::vector<char>::const_iterator &end) {
|
||||||
|
auto inCpy = in;
|
||||||
|
auto id = Serialize::deserialize<idType>(in, end);
|
||||||
|
auto type = Serialize::deserialize<ObjectType>(in, end);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ObjectType::Archive:
|
||||||
|
return std::make_unique<Archive>(Serialize::deserialize<Archive>(inCpy, end));
|
||||||
|
case ObjectType::File:
|
||||||
|
return std::make_unique<File>(Serialize::deserialize<File>(inCpy, end));
|
||||||
|
case ObjectType::Chunk:
|
||||||
|
return std::make_unique<Chunk>(Serialize::deserialize<Chunk>(inCpy, end));
|
||||||
|
case ObjectType::END:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw Exception("Bad object!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Object> Object::deserialize(const std::vector<char> &src) {
|
||||||
|
auto srcIterator = src.cbegin();
|
||||||
|
return deserialize(srcIterator, src.end());
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ Repository::~Repository() = default;
|
|||||||
|
|
||||||
Repository::Repository(Config config) : config(std::move(config)) {}
|
Repository::Repository(Config config) : config(std::move(config)) {}
|
||||||
|
|
||||||
const Config &Repository::getConfig() const {
|
const Config &Repository::getConfig() const { return config; }
|
||||||
return config;
|
|
||||||
|
std::unique_ptr<Object> Repository::getObject(Object::idType id) const {
|
||||||
|
return Object::deserialize(this->getObjectRaw(id));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ Archive::Archive(Object::idType id, std::string name, unsigned long long mtime,
|
|||||||
: Object(id, ObjectType::Archive), name(name), mtime(mtime), files(files) {}
|
: Object(id, ObjectType::Archive), name(name), mtime(mtime), files(files) {}
|
||||||
|
|
||||||
Archive::Archive(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
Archive::Archive(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
||||||
: Object(in, end),
|
: Object(in, end), name(Serialize::deserialize<std::string>(in, end)),
|
||||||
name(Serialize::deserialize<std::string>(in, end)),
|
|
||||||
mtime(Serialize::deserialize<unsigned long long>(in, end)),
|
mtime(Serialize::deserialize<unsigned long long>(in, end)),
|
||||||
files(Serialize::deserialize<std::remove_const<decltype(files)>::type>(in, end)) {
|
files(Serialize::deserialize<std::remove_const<decltype(files)>::type>(in, end)) {
|
||||||
if (type != ObjectType::Archive) throw Exception("Type mismatch for Archive!");
|
if (type != ObjectType::Archive) throw Exception("Type mismatch for Archive!");
|
||||||
@@ -28,6 +27,6 @@ void Archive::serialize(std::vector<char> &out) const {
|
|||||||
Serialize::serialize(files.size(), out);
|
Serialize::serialize(files.size(), out);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Archive::getKey() const {
|
std::string Archive::getKey() const { return name; }
|
||||||
return name;
|
|
||||||
}
|
const std::vector<Object::idType> &Archive::getRefs() const { return files; }
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "Serialize.h"
|
#include "Serialize.h"
|
||||||
|
|
||||||
Chunk::Chunk(idType id, std::string SHA, std::vector<char> data) : Object(id, ObjectType::Chunk), data(std::move(data)), SHA(std::move(SHA)), length(this->data.size()) {}
|
Chunk::Chunk(idType id, std::string SHA, std::vector<char> data)
|
||||||
|
: Object(id, ObjectType::Chunk), data(std::move(data)), SHA(std::move(SHA)), length(this->data.size()) {}
|
||||||
|
|
||||||
Chunk::Chunk(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
Chunk::Chunk(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
||||||
: Object(in, end),
|
: Object(in, end), SHA(Serialize::deserialize<std::remove_const<decltype(SHA)>::type>(in, end)),
|
||||||
SHA(Serialize::deserialize<std::remove_const<decltype(SHA)>::type>(in, end)),
|
|
||||||
data(Serialize::deserialize<std::remove_const<decltype(data)>::type>(in, end)),
|
data(Serialize::deserialize<std::remove_const<decltype(data)>::type>(in, end)),
|
||||||
length(Serialize::deserialize<std::remove_const<decltype(length)>::type>(in, end)) {
|
length(Serialize::deserialize<std::remove_const<decltype(length)>::type>(in, end)) {
|
||||||
if (type != ObjectType::Chunk) throw Exception("Type mismatch for Chunk!");
|
if (type != ObjectType::Chunk) throw Exception("Type mismatch for Chunk!");
|
||||||
@@ -25,6 +25,4 @@ void Chunk::serialize(std::vector<char> &out) const {
|
|||||||
Serialize::serialize(length, out);
|
Serialize::serialize(length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Chunk::getKey() const {
|
std::string Chunk::getKey() const { return SHA; }
|
||||||
return SHA;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,12 +11,13 @@
|
|||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "Serialize.h"
|
#include "Serialize.h"
|
||||||
|
|
||||||
File::File(Object::idType id, std::string name, unsigned long long bytes, unsigned long long mtime, std::string SHA, std::map<size_t, idType> chunks, Type fileType)
|
File::File(Object::idType id, std::string name, unsigned long long bytes, unsigned long long mtime, std::string SHA,
|
||||||
: Object(id, ObjectType::File), name(name), bytes(bytes), mtime(mtime), SHA(SHA), fileType(fileType), chunks(std::move(chunks)) {}
|
std::map<size_t, idType> chunks, Type fileType)
|
||||||
|
: Object(id, ObjectType::File), name(name), bytes(bytes), mtime(mtime), SHA(SHA), fileType(fileType),
|
||||||
|
chunks(std::move(chunks)) {}
|
||||||
|
|
||||||
File::File(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
File::File(std::vector<char>::const_iterator &in, const std::vector<char>::const_iterator &end)
|
||||||
: Object(in, end),
|
: Object(in, end), name(Serialize::deserialize<std::string>(in, end)),
|
||||||
name(Serialize::deserialize<std::string>(in, end)),
|
|
||||||
bytes(Serialize::deserialize<unsigned long long>(in, end)),
|
bytes(Serialize::deserialize<unsigned long long>(in, end)),
|
||||||
mtime(Serialize::deserialize<unsigned long long>(in, end)),
|
mtime(Serialize::deserialize<unsigned long long>(in, end)),
|
||||||
SHA(Serialize::deserialize<std::remove_const<decltype(SHA)>::type>(in, end)),
|
SHA(Serialize::deserialize<std::remove_const<decltype(SHA)>::type>(in, end)),
|
||||||
@@ -35,47 +36,45 @@ void File::serialize(std::vector<char> &out) const {
|
|||||||
Serialize::serialize(chunks, out);
|
Serialize::serialize(chunks, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string File::getKey() const {
|
std::string File::getKey() const { return name; }
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
File::Type File::getFileType(const std::filesystem::path &p) {
|
File::Type File::getFileType(const std::filesystem::path &p) {
|
||||||
if (std::filesystem::is_symlink(p)) return Type::Symlink;
|
if (std::filesystem::is_symlink(p)) return Type::Symlink;
|
||||||
if (std::filesystem::is_directory(p)) return Type::Directory;
|
if (std::filesystem::is_directory(p)) return Type::Directory;
|
||||||
if (std::filesystem::is_regular_file(p)) return Type::Normal;
|
if (std::filesystem::is_regular_file(p)) return Type::Normal;
|
||||||
throw Exception("Unsupported file type! " + p.u8string());
|
throw Exception("Unsupported file type! " + p.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> File::getFileContents(const std::filesystem::path &p) {
|
std::vector<char> File::getFileContents(const std::filesystem::path &p) {
|
||||||
auto type = getFileType(p);
|
auto type = getFileType(p);
|
||||||
if (type == Type::Normal) throw Exception(p.u8string() + " is a normal file!");
|
if (type == Type::Normal) throw Exception(p.string() + " is a normal file!");
|
||||||
if (type == Type::Directory) {
|
if (type == Type::Directory) { return {}; }
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (type == Type::Symlink) {
|
if (type == Type::Symlink) {
|
||||||
auto target = std::filesystem::read_symlink(p).u8string();
|
auto target = std::filesystem::read_symlink(p).string();
|
||||||
std::vector<char> target_null_term = {target.begin(), target.end()};
|
std::vector<char> target_null_term = {target.begin(), target.end()};
|
||||||
target_null_term.emplace_back('\0');
|
target_null_term.emplace_back('\0');
|
||||||
return target_null_term;
|
return target_null_term;
|
||||||
}
|
}
|
||||||
throw Exception("Error with file " + p.u8string());
|
throw Exception("Error with file " + p.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long File::getFileMtime(const std::filesystem::path &p) {
|
unsigned long long File::getFileMtime(const std::filesystem::path &p) {
|
||||||
auto type = getFileType(p);
|
auto type = getFileType(p);
|
||||||
if (type == Type::Normal || type == Type::Directory)
|
if (type == Type::Normal || type == Type::Directory)
|
||||||
return static_cast<const unsigned long long int>(std::chrono::duration_cast<std::chrono::seconds>(std::filesystem::last_write_time(p).time_since_epoch()).count());
|
return static_cast<const unsigned long long int>(
|
||||||
|
std::chrono::duration_cast<std::chrono::seconds>(std::filesystem::last_write_time(p).time_since_epoch())
|
||||||
|
.count());
|
||||||
else if (type == Type::Symlink) {
|
else if (type == Type::Symlink) {
|
||||||
auto path = p.u8string();
|
auto path = p.string();
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
if (lstat(path.c_str(), &sb) != 0) throw Exception("Error reading mtime for " + p.u8string());
|
if (lstat(path.c_str(), &sb) != 0) throw Exception("Error reading mtime for " + p.string());
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
return sb.st_mtimespec.tv_sec;
|
return sb.st_mtimespec.tv_sec;
|
||||||
#else
|
#else
|
||||||
return sb.st_mtime;
|
return sb.st_mtime;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
throw Exception("Error with file " + p.u8string());
|
throw Exception("Error with file " + p.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long File::getFileSize(const std::filesystem::path &p) {
|
unsigned long long File::getFileSize(const std::filesystem::path &p) {
|
||||||
@@ -84,3 +83,16 @@ unsigned long long File::getFileSize(const std::filesystem::path &p) {
|
|||||||
else
|
else
|
||||||
return getFileContents(p).size();
|
return getFileContents(p).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void File::makeChunksList() const {
|
||||||
|
if (chunksList) return;
|
||||||
|
chunksList.emplace();
|
||||||
|
|
||||||
|
chunksList->reserve(chunks.size());
|
||||||
|
for (auto const &c: chunks) chunksList->emplace_back(c.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Object::idType> &File::getRefs() const {
|
||||||
|
if (!chunksList) makeChunksList();
|
||||||
|
return *chunksList;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,13 +6,12 @@
|
|||||||
|
|
||||||
#include "Serialize.h"
|
#include "Serialize.h"
|
||||||
|
|
||||||
FileBuffer::FileBuffer(const Repository *repo, Object::idType fileId) : repo(repo), file(Serialize::deserialize<File>(repo->getObject(fileId))), chunksQueue() {
|
FileBuffer::FileBuffer(const Repository *repo, Object::idType fileId)
|
||||||
|
: repo(repo), file(Serialize::deserialize<File>(repo->getObjectRaw(fileId))), chunksQueue() {
|
||||||
for (auto const &id: file.chunks) chunksQueue.emplace(id.second);
|
for (auto const &id: file.chunks) chunksQueue.emplace(id.second);
|
||||||
};
|
};
|
||||||
|
|
||||||
int FileBuffer::sync() {
|
int FileBuffer::sync() { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize FileBuffer::xsgetn(char *s, std::streamsize countr) {
|
std::streamsize FileBuffer::xsgetn(char *s, std::streamsize countr) {
|
||||||
if (underflow() == std::char_traits<char>::eof()) return 0;
|
if (underflow() == std::char_traits<char>::eof()) return 0;
|
||||||
@@ -28,8 +27,7 @@ std::streamsize FileBuffer::xsgetn(char *s, std::streamsize countr) {
|
|||||||
|
|
||||||
int FileBuffer::uflow() {
|
int FileBuffer::uflow() {
|
||||||
auto out = underflow();
|
auto out = underflow();
|
||||||
if (out != traits_type::eof())
|
if (out != traits_type::eof()) curGetBufPos++;
|
||||||
curGetBufPos++;
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,15 +35,14 @@ int FileBuffer::underflow() {
|
|||||||
if (getBuf.empty() || curGetBufPos == getBuf.size()) {
|
if (getBuf.empty() || curGetBufPos == getBuf.size()) {
|
||||||
if (chunksQueue.empty()) return traits_type::eof();
|
if (chunksQueue.empty()) return traits_type::eof();
|
||||||
else {
|
else {
|
||||||
auto chunk = Serialize::deserialize<Chunk>(repo->getObject(chunksQueue.front()));
|
auto chunk = Serialize::deserialize<Chunk>(repo->getObjectRaw(chunksQueue.front()));
|
||||||
getBuf = chunk.data;
|
getBuf = chunk.data;
|
||||||
chunksQueue.pop();
|
chunksQueue.pop();
|
||||||
curGetBufPos = 0;
|
curGetBufPos = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getBuf.empty())
|
if (!getBuf.empty()) return traits_type::to_int_type(getBuf[curGetBufPos]);
|
||||||
return traits_type::to_int_type(getBuf[curGetBufPos]);
|
|
||||||
else
|
else
|
||||||
return traits_type::eof();
|
return traits_type::eof();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_library(utils srcs/BytesFormatter.cpp srcs/Config.cpp srcs/Exception.cpp srcs/Logger.cpp srcs/Progress.cpp srcs/RunningAverage.cpp srcs/RunningDiffAverage.cpp srcs/Signals.cpp srcs/ThreadPool.cpp)
|
add_library(utils srcs/BytesFormatter.cpp srcs/Config.cpp srcs/Exception.cpp srcs/Logger.cpp srcs/Progress.cpp srcs/RunningAverage.cpp srcs/RunningDiffAverage.cpp srcs/Signals.cpp srcs/ThreadPool.cpp)
|
||||||
|
|
||||||
|
|||||||
@@ -64,11 +64,7 @@ public:
|
|||||||
|
|
||||||
using serializable = std::true_type;
|
using serializable = std::true_type;
|
||||||
|
|
||||||
enum class KeyType {
|
enum class KeyType { STRING, INT, LIST };
|
||||||
STRING,
|
|
||||||
INT,
|
|
||||||
LIST
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Struct to record key options
|
/// Struct to record key options
|
||||||
struct keyopts {
|
struct keyopts {
|
||||||
@@ -79,7 +75,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Used for printing help
|
/// Used for printing help
|
||||||
const static inline std::unordered_map<KeyType, std::string> KeyTypeToStr{{KeyType::STRING, "string"}, {KeyType::INT, "number"}, {KeyType::LIST, "comma-separated list"}};
|
const static inline std::unordered_map<KeyType, std::string> KeyTypeToStr{{KeyType::STRING, "string"},
|
||||||
|
{KeyType::INT, "number"},
|
||||||
|
{KeyType::LIST, "comma-separated list"}};
|
||||||
|
|
||||||
/// Default values and their metadata
|
/// Default values and their metadata
|
||||||
const static inline std::unordered_map<std::string, keyopts> keys{
|
const static inline std::unordered_map<std::string, keyopts> keys{
|
||||||
@@ -98,7 +96,9 @@ public:
|
|||||||
{"chunker", {"buzhash", KeyType::STRING, true, "Chunker to use (const, buzhash)"}},
|
{"chunker", {"buzhash", KeyType::STRING, true, "Chunker to use (const, buzhash)"}},
|
||||||
{"chunker-min", {"256", KeyType::INT, true, "Min chunk size in KB"}},
|
{"chunker-min", {"256", KeyType::INT, true, "Min chunk size in KB"}},
|
||||||
{"chunker-max", {"4096", KeyType::INT, true, "Max chunk size in KB"}},
|
{"chunker-max", {"4096", KeyType::INT, true, "Max chunk size in KB"}},
|
||||||
{"chunker-mask", {"20", KeyType::INT, true, "Chunker hash bit mask (mask of n bits results in average chunk size of 2^n bytes)"}},
|
{"chunker-mask",
|
||||||
|
{"20", KeyType::INT, true,
|
||||||
|
"Chunker hash bit mask (mask of n bits results in average chunk size of 2^n bytes)"}},
|
||||||
{"repo-target", {"128", KeyType::INT, true, "Target size of files for FileRepository"}},
|
{"repo-target", {"128", KeyType::INT, true, "Target size of files for FileRepository"}},
|
||||||
{"progress", {"pretty", KeyType::STRING, false, "How to print progress (simple, pretty, none)"}},
|
{"progress", {"pretty", KeyType::STRING, false, "How to print progress (simple, pretty, none)"}},
|
||||||
{"verbose", {"1", KeyType::INT, false, "Message verbosity (0 - error, 1 - info, -1 - quiet)"}},
|
{"verbose", {"1", KeyType::INT, false, "Message verbosity (0 - error, 1 - info, -1 - quiet)"}},
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ public:
|
|||||||
/// \param out Function to call for output
|
/// \param out Function to call for output
|
||||||
/// \param format Format of the progress string, vector of strings or functions that return strings
|
/// \param format Format of the progress string, vector of strings or functions that return strings
|
||||||
/// \param conf Config, used to specify format (`pretty` for line rewriting, `simple` for normal line printing, or `none`)
|
/// \param conf Config, used to specify format (`pretty` for line rewriting, `simple` for normal line printing, or `none`)
|
||||||
Progress(std::function<void(std::string, int)> out, std::vector<std::variant<std::function<std::string()>, std::string>> format, const Config &conf, int level = 1);
|
Progress(std::function<void(std::string, int)> out,
|
||||||
|
std::vector<std::variant<std::function<std::string()>, std::string>> format, const Config &conf,
|
||||||
|
int level = 1);
|
||||||
|
|
||||||
Progress &operator=(Progress rhs) = delete;
|
Progress &operator=(Progress rhs) = delete;
|
||||||
Progress(const Progress &orig) = delete;
|
Progress(const Progress &orig) = delete;
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ namespace Serialize {
|
|||||||
struct is_pair : std::false_type {};
|
struct is_pair : std::false_type {};
|
||||||
|
|
||||||
template<typename P>
|
template<typename P>
|
||||||
struct is_pair<P, std::void_t<decltype(std::declval<P>().first)>, std::void_t<decltype(std::declval<P>().second)>> : std::true_type {};
|
struct is_pair<P, std::void_t<decltype(std::declval<P>().first)>, std::void_t<decltype(std::declval<P>().second)>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
template<typename, typename, typename = void>
|
template<typename, typename, typename = void>
|
||||||
struct has_emplace_back : std::false_type {};
|
struct has_emplace_back : std::false_type {};
|
||||||
@@ -104,22 +105,19 @@ namespace Serialize {
|
|||||||
} else if constexpr (std::is_enum<T>::value) {
|
} else if constexpr (std::is_enum<T>::value) {
|
||||||
// If the object is an enum, deserialize an int and cast it to the enum
|
// If the object is an enum, deserialize an int and cast it to the enum
|
||||||
auto tmp = deserialize<uint32_t>(in, end);
|
auto tmp = deserialize<uint32_t>(in, end);
|
||||||
if (tmp >= 0 && tmp < static_cast<uint32_t>(T::END))
|
if (tmp >= 0 && tmp < static_cast<uint32_t>(T::END)) return static_cast<T>(tmp);
|
||||||
return static_cast<T>(tmp);
|
|
||||||
else
|
else
|
||||||
throw Exception("Enum out of range!");
|
throw Exception("Enum out of range!");
|
||||||
} else if constexpr (sizeof(T) == 1) {
|
} else if constexpr (sizeof(T) == 1) {
|
||||||
// If it's a single byte, just copy it
|
// If it's a single byte, just copy it
|
||||||
if (std::distance(in, end) < sizeof(T))
|
if (std::distance(in, end) < sizeof(T)) throw Exception("Unexpected end of object!");
|
||||||
throw Exception("Unexpected end of object!");
|
|
||||||
return *(in++);
|
return *(in++);
|
||||||
} else if constexpr (std::is_integral<T>::value) {
|
} else if constexpr (std::is_integral<T>::value) {
|
||||||
uint64_t tmp;
|
uint64_t tmp;
|
||||||
static_assert(sizeof(tmp) == 8);
|
static_assert(sizeof(tmp) == 8);
|
||||||
|
|
||||||
// If the object is a number, copy it byte-by-byte
|
// If the object is a number, copy it byte-by-byte
|
||||||
if (std::distance(in, end) < sizeof(tmp))
|
if (std::distance(in, end) < sizeof(tmp)) throw Exception("Unexpected end of object!");
|
||||||
throw Exception("Unexpected end of object!");
|
|
||||||
|
|
||||||
std::copy(in, in + sizeof(tmp), reinterpret_cast<char *>(&tmp));
|
std::copy(in, in + sizeof(tmp), reinterpret_cast<char *>(&tmp));
|
||||||
in += sizeof(tmp);
|
in += sizeof(tmp);
|
||||||
@@ -134,8 +132,7 @@ namespace Serialize {
|
|||||||
T out;
|
T out;
|
||||||
if constexpr (sizeof(typename T::value_type) == 1) {
|
if constexpr (sizeof(typename T::value_type) == 1) {
|
||||||
// Optimization for char vectors
|
// Optimization for char vectors
|
||||||
if (std::distance(in, end) < size)
|
if (std::distance(in, end) < size) throw Exception("Unexpected end of object!");
|
||||||
throw Exception("Unexpected end of object!");
|
|
||||||
out.insert(out.end(), in, in + size);
|
out.insert(out.end(), in, in + size);
|
||||||
in += size;
|
in += size;
|
||||||
} else
|
} else
|
||||||
@@ -143,8 +140,7 @@ namespace Serialize {
|
|||||||
using V = typename T::value_type;
|
using V = typename T::value_type;
|
||||||
V v = deserialize<V>(in, end);
|
V v = deserialize<V>(in, end);
|
||||||
// Try either emplace_back or emplace if it doesn't exist
|
// Try either emplace_back or emplace if it doesn't exist
|
||||||
if constexpr (has_emplace_back<T, V>::value)
|
if constexpr (has_emplace_back<T, V>::value) out.emplace_back(std::move(v));
|
||||||
out.emplace_back(std::move(v));
|
|
||||||
else
|
else
|
||||||
out.emplace(std::move(v));
|
out.emplace(std::move(v));
|
||||||
}
|
}
|
||||||
@@ -175,7 +171,8 @@ namespace Serialize {
|
|||||||
// If the object is a number, copy it byte-by-byte
|
// If the object is a number, copy it byte-by-byte
|
||||||
uint64_t tmp = htobe64(static_cast<uint64_t>(what));
|
uint64_t tmp = htobe64(static_cast<uint64_t>(what));
|
||||||
static_assert(sizeof(tmp) == 8);
|
static_assert(sizeof(tmp) == 8);
|
||||||
out.insert(out.end(), (reinterpret_cast<const char *>(&tmp)), (reinterpret_cast<const char *>(&tmp) + sizeof(tmp)));
|
out.insert(out.end(), (reinterpret_cast<const char *>(&tmp)),
|
||||||
|
(reinterpret_cast<const char *>(&tmp) + sizeof(tmp)));
|
||||||
} else {
|
} else {
|
||||||
// Otherwise we treat it as a container, in format of <number of elements>b<elements>e
|
// Otherwise we treat it as a container, in format of <number of elements>b<elements>e
|
||||||
serialize(what.size(), out);
|
serialize(what.size(), out);
|
||||||
@@ -184,9 +181,7 @@ namespace Serialize {
|
|||||||
// Optimization for char vectors
|
// Optimization for char vectors
|
||||||
out.insert(out.end(), what.begin(), what.end());
|
out.insert(out.end(), what.begin(), what.end());
|
||||||
} else
|
} else
|
||||||
for (auto const &i: what) {
|
for (auto const &i: what) { serialize(i, out); }
|
||||||
serialize(i, out);
|
|
||||||
}
|
|
||||||
serialize('e', out);
|
serialize('e', out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ Config &Config::add(const std::string &k, const std::string &v) {
|
|||||||
case KeyType::INT:
|
case KeyType::INT:
|
||||||
try {
|
try {
|
||||||
std::stoi(v);
|
std::stoi(v);
|
||||||
} catch (...) {
|
} catch (...) { throw Exception("Can't convert " + k + " to integer!"); }
|
||||||
throw Exception("Can't convert " + k + " to integer!");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case KeyType::LIST:
|
case KeyType::LIST:
|
||||||
break;
|
break;
|
||||||
@@ -32,17 +30,14 @@ Config &Config::add(const std::string &k, const std::string &v) {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Config::getInt(const std::string &k) const {
|
int Config::getInt(const std::string &k) const { return std::stoi(getStr(k)); }
|
||||||
return std::stoi(getStr(k));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> Config::getList(const std::string &k) const {
|
std::vector<std::string> Config::getList(const std::string &k) const {
|
||||||
std::vector<std::string> out;
|
std::vector<std::string> out;
|
||||||
std::string next;
|
std::string next;
|
||||||
std::stringstream inss(getStr(k));
|
std::stringstream inss(getStr(k));
|
||||||
while (std::getline(inss, next, ',')) {
|
while (std::getline(inss, next, ',')) {
|
||||||
if (next != "")
|
if (next != "") out.emplace_back(next);
|
||||||
out.emplace_back(next);
|
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -54,28 +49,23 @@ std::string Config::getStr(const std::string &k) const {
|
|||||||
throw Exception("Option " + k + " not specified and no default value exists!");
|
throw Exception("Option " + k + " not specified and no default value exists!");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Config::exists(const std::string &k) const {
|
bool Config::exists(const std::string &k) const { return (data.count(k) > 0) || (keys.at(k).defaultval.has_value()); }
|
||||||
return (data.count(k) > 0) || (keys.at(k).defaultval.has_value());
|
|
||||||
}
|
|
||||||
|
|
||||||
Config::Config() = default;
|
Config::Config() = default;
|
||||||
|
|
||||||
Config::Config(std::vector<char, std::allocator<char>>::const_iterator &in, const std::vector<char, std::allocator<char>>::const_iterator &end) {
|
Config::Config(std::vector<char, std::allocator<char>>::const_iterator &in,
|
||||||
|
const std::vector<char, std::allocator<char>>::const_iterator &end) {
|
||||||
data = Serialize::deserialize<decltype(data)>(in, end);
|
data = Serialize::deserialize<decltype(data)>(in, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::serialize(std::vector<char> &out) const {
|
void Config::serialize(std::vector<char> &out) const {
|
||||||
std::vector<decltype(data)::value_type> temp;
|
std::vector<decltype(data)::value_type> temp;
|
||||||
for (const auto &d: data) {
|
for (const auto &d: data) {
|
||||||
if (keys.at(d.first).remember) {
|
if (keys.at(d.first).remember) { temp.emplace_back(d); }
|
||||||
temp.emplace_back(d);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Serialize::serialize(temp, out);
|
Serialize::serialize(temp, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::merge(const Config &config) {
|
void Config::merge(const Config &config) {
|
||||||
for (const auto &d: config.data) {
|
for (const auto &d: config.data) { add(d.first, d.second); }
|
||||||
add(d.first, d.second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ std::string Exception::getStacktrace() {
|
|||||||
|
|
||||||
if (strings != nullptr) {
|
if (strings != nullptr) {
|
||||||
out << "Stacktrace:" << std::endl;
|
out << "Stacktrace:" << std::endl;
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++) out << strings[i] << std::endl;
|
||||||
out << strings[i] << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(strings);
|
free(strings);
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
Logger::Logger(int level, std::ostream &out) : loglevel(level), out(out) {
|
Logger::Logger(int level, std::ostream &out) : loglevel(level), out(out) {}
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::write(const std::string &what, int whatlevel) {
|
void Logger::write(const std::string &what, int whatlevel) {
|
||||||
if (whatlevel <= loglevel) {
|
if (whatlevel <= loglevel) {
|
||||||
@@ -14,6 +13,4 @@ void Logger::write(const std::string &what, int whatlevel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::setLevel(int level) {
|
void Logger::setLevel(int level) { loglevel = level; }
|
||||||
loglevel = level;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
Progress::Progress(std::function<void(std::string, int)> out, std::vector<std::variant<std::function<std::string()>, std::string>> format, const Config &conf, int level) : format(std::move(format)), out(std::move(out)), type(conf.getStr("progress")), progresslevel(level) {
|
Progress::Progress(std::function<void(std::string, int)> out,
|
||||||
|
std::vector<std::variant<std::function<std::string()>, std::string>> format, const Config &conf,
|
||||||
|
int level)
|
||||||
|
: format(std::move(format)), out(std::move(out)), type(conf.getStr("progress")), progresslevel(level) {
|
||||||
if (type != "none") {
|
if (type != "none") {
|
||||||
this->out("\n\n", level);
|
this->out("\n\n", level);
|
||||||
thread = std::thread(&Progress::showProgress, this);
|
thread = std::thread(&Progress::showProgress, this);
|
||||||
@@ -16,16 +19,13 @@ Progress::Progress(std::function<void(std::string, int)> out, std::vector<std::v
|
|||||||
|
|
||||||
Progress::~Progress() {
|
Progress::~Progress() {
|
||||||
stop = true;
|
stop = true;
|
||||||
if (thread.joinable())
|
if (thread.joinable()) thread.join();
|
||||||
thread.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Progress::showProgress() {
|
void Progress::showProgress() {
|
||||||
while (!stop) {
|
while (!stop) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
{
|
{ update(std::unique_lock(refreshM)); }
|
||||||
update(std::unique_lock(refreshM));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,8 +38,7 @@ void Progress::print(const std::string &s, int level) {
|
|||||||
void Progress::update(std::unique_lock<std::mutex> &&lock) {
|
void Progress::update(std::unique_lock<std::mutex> &&lock) {
|
||||||
std::stringstream outs;
|
std::stringstream outs;
|
||||||
|
|
||||||
if (type == "pretty")
|
if (type == "pretty") outs << "\r\33[2K ";
|
||||||
outs << "\r\33[2K ";
|
|
||||||
|
|
||||||
for (auto const &l: format) {
|
for (auto const &l: format) {
|
||||||
if (std::holds_alternative<std::string>(l)) outs << std::get<std::string>(l);
|
if (std::holds_alternative<std::string>(l)) outs << std::get<std::string>(l);
|
||||||
@@ -47,8 +46,7 @@ void Progress::update(std::unique_lock<std::mutex> &&lock) {
|
|||||||
outs << std::get<std::function<std::string()>>(l)();
|
outs << std::get<std::function<std::string()>>(l)();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == "pretty")
|
if (type == "pretty") outs << "\r";
|
||||||
outs << "\r";
|
|
||||||
else
|
else
|
||||||
outs << "\n";
|
outs << "\n";
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
#include "RunningAverage.h"
|
#include "RunningAverage.h"
|
||||||
|
|
||||||
RunningAverage::RunningAverage(std::function<unsigned long long int()> getFunc, int max, int ms)
|
RunningAverage::RunningAverage(std::function<unsigned long long int()> getFunc, int max, int ms)
|
||||||
: getFunc(std::move(getFunc)), max(max), ms(ms), thread(&RunningAverage::loop, this) {
|
: getFunc(std::move(getFunc)), max(max), ms(ms), thread(&RunningAverage::loop, this) {}
|
||||||
}
|
|
||||||
|
|
||||||
void RunningAverage::loop() {
|
void RunningAverage::loop() {
|
||||||
while (!stop) {
|
while (!stop) {
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ RunningDiffAverage::RunningDiffAverage(std::function<unsigned long long int()> g
|
|||||||
prev = cur;
|
prev = cur;
|
||||||
return calc;
|
return calc;
|
||||||
},
|
},
|
||||||
max, ms) {
|
max, ms) {}
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long long RunningDiffAverage::get() {
|
unsigned long long RunningDiffAverage::get() { return runningAverage.get(); }
|
||||||
return runningAverage.get();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,10 +3,6 @@
|
|||||||
//
|
//
|
||||||
#include "Signals.h"
|
#include "Signals.h"
|
||||||
|
|
||||||
void Signals::setup() {
|
void Signals::setup() { signal(SIGINT, handle); }
|
||||||
signal(SIGINT, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Signals::handle(int signum) {
|
void Signals::handle(int signum) { shouldQuit = true; }
|
||||||
shouldQuit = true;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,18 +8,17 @@ ThreadPool::ThreadPool(std::function<void(std::string)> onError, std::size_t wor
|
|||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::~ThreadPool() {
|
ThreadPool::~ThreadPool() {
|
||||||
stop = true;
|
{
|
||||||
somethingNew.notify_all();
|
std::lock_guard lock(queueLock);
|
||||||
for (auto &t: threads) {
|
stop = true;
|
||||||
t.join();
|
somethingNew.notify_all();
|
||||||
}
|
}
|
||||||
|
for (auto &t: threads) { t.join(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::push(std::function<void()> &&func) {
|
void ThreadPool::push(std::function<void()> &&func) {
|
||||||
{
|
std::lock_guard lock(queueLock);
|
||||||
std::lock_guard lock(queueLock);
|
queue.push(std::move(func));
|
||||||
queue.push(std::move(func));
|
|
||||||
}
|
|
||||||
somethingNew.notify_one();
|
somethingNew.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,18 +44,13 @@ void ThreadPool::loop() {
|
|||||||
queue.pop();
|
queue.pop();
|
||||||
|
|
||||||
qLock.unlock();
|
qLock.unlock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
task();
|
task();
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) { onError(std::string(e.what())); }
|
||||||
onError(std::string(e.what()));
|
qLock.lock();
|
||||||
}
|
|
||||||
|
|
||||||
{
|
running--;
|
||||||
std::lock_guard qLock(queueLock);
|
if (queue.empty() && running == 0) { finished.notify_all(); }
|
||||||
running--;
|
|
||||||
if (queue.empty() && running == 0) { finished.notify_all(); }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
# GoogleTest requires at least C++14
|
# GoogleTest requires at least C++14
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
BuzhashTest
|
BuzhashTest
|
||||||
@@ -8,5 +8,5 @@ target_link_libraries(
|
|||||||
BuzhashTest PRIVATE
|
BuzhashTest PRIVATE
|
||||||
GTest::gtest_main chunkers
|
GTest::gtest_main chunkers
|
||||||
)
|
)
|
||||||
gtest_discover_tests(BuzhashTest)
|
gtest_discover_tests(BuzhashTest DISCOVERY_TIMEOUT 600)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,12 @@
|
|||||||
|
|
||||||
// Demonstrate some basic assertions.
|
// Demonstrate some basic assertions.
|
||||||
TEST(BuzhashTest, SimpleTest) {
|
TEST(BuzhashTest, SimpleTest) {
|
||||||
std::string loremipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
std::string loremipsum =
|
||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et "
|
||||||
|
"dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip "
|
||||||
|
"ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu "
|
||||||
|
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
|
||||||
|
"mollit anim id est laborum.";
|
||||||
|
|
||||||
for (int i = 15; i < 49; i++) {
|
for (int i = 15; i < 49; i++) {
|
||||||
Buzhash b(i);
|
Buzhash b(i);
|
||||||
@@ -21,11 +26,11 @@ TEST(BuzhashTest, SimpleTest) {
|
|||||||
for (int i = 0; i < loremipsum.length(); i++) {
|
for (int i = 0; i < loremipsum.length(); i++) {
|
||||||
b2.feed((uint8_t) loremipsum[i]);
|
b2.feed((uint8_t) loremipsum[i]);
|
||||||
if (b2.get() == h1) {
|
if (b2.get() == h1) {
|
||||||
EXPECT_EQ(i, loremipsum.find("e eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non p"));
|
ASSERT_EQ(i, loremipsum.find("e eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non p"));
|
||||||
h1found = true;
|
h1found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPECT_EQ(h1found, true);
|
ASSERT_EQ(h1found, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
@@ -11,5 +11,5 @@ target_link_libraries(
|
|||||||
CLITests PRIVATE
|
CLITests PRIVATE
|
||||||
GTest::gtest_main testUtils
|
GTest::gtest_main testUtils
|
||||||
)
|
)
|
||||||
gtest_discover_tests(CLITests)
|
gtest_discover_tests(CLITests DISCOVERY_TIMEOUT 600)
|
||||||
|
|
||||||
|
|||||||
@@ -61,13 +61,13 @@ echo "testtestasdf9uuu" > "$TESTDATADIR"/testdata/4/filexd
|
|||||||
|
|
||||||
echo "Data created"
|
echo "Data created"
|
||||||
|
|
||||||
if ! $CMD init --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --compression zlib --compression-level 4 --encryption aes --password asdff --salt e; then
|
if ! $CMD init --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --compression zlib --compression-level 4 --encryption aes --password asdff --salt e; then
|
||||||
echo "Error creating repo!"
|
echo "Error creating repo!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "Repo created"
|
echo "Repo created"
|
||||||
|
|
||||||
OUT=$($CMD run --from "$TESTDATADIR"/testdata/1 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress simple --verbose 1)
|
OUT=$($CMD run --from "$TESTDATADIR"/testdata/1 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress simple --verbose 1)
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ( echo "$OUT" | grep -q 'Copied: notempty/testfile' )\
|
if ! ( ( echo "$OUT" | grep -q 'Copied: notempty/testfile' )\
|
||||||
&& ( echo "$OUT" | grep -q 'Copied: notempty' )\
|
&& ( echo "$OUT" | grep -q 'Copied: notempty' )\
|
||||||
@@ -87,7 +87,7 @@ if ! ( ( echo "$OUT" | grep -q 'Copied: notempty/testfile' )\
|
|||||||
fi
|
fi
|
||||||
echo "Backup 1 ok"
|
echo "Backup 1 ok"
|
||||||
|
|
||||||
OUT=$($CMD run --from "$TESTDATADIR"/testdata/2 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress simple --verbose 1)
|
OUT=$($CMD run --from "$TESTDATADIR"/testdata/2 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress simple --verbose 1)
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4 ' )\
|
if ! ( ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4 ' )\
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5 ' )\
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5 ' )\
|
||||||
@@ -100,7 +100,7 @@ if ! ( ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4 ' )\
|
|||||||
fi
|
fi
|
||||||
echo "Backup 2 ok"
|
echo "Backup 2 ok"
|
||||||
|
|
||||||
OUT=$($CMD run --from "$TESTDATADIR"/testdata/3 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress simple --verbose 1)
|
OUT=$($CMD run --from "$TESTDATADIR"/testdata/3 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress simple --verbose 1)
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4 ' )\
|
if ! ( ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4 ' )\
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5 ' )\
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5 ' )\
|
||||||
@@ -116,7 +116,7 @@ echo "Backup 3 ok"
|
|||||||
|
|
||||||
i=$((0))
|
i=$((0))
|
||||||
AIDS=()
|
AIDS=()
|
||||||
OUT=$($CMD list --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff)
|
OUT=$($CMD list --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff)
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
while IFS= read -r l; do
|
while IFS= read -r l; do
|
||||||
((i++))
|
((i++))
|
||||||
@@ -124,7 +124,7 @@ while IFS= read -r l; do
|
|||||||
AIDS+=("$aid")
|
AIDS+=("$aid")
|
||||||
done <<< "$OUT"
|
done <<< "$OUT"
|
||||||
|
|
||||||
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[0]} --aid2 ${AIDS[1]})
|
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[0]} --aid2 ${AIDS[1]})
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
||||||
@@ -132,7 +132,7 @@ if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && (
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "OK comparing archive 1 and 2"
|
echo "OK comparing archive 1 and 2"
|
||||||
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[1]} --aid2 ${AIDS[2]})
|
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[1]} --aid2 ${AIDS[2]})
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
||||||
@@ -141,7 +141,7 @@ if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && (
|
|||||||
fi
|
fi
|
||||||
echo "OK comparing archive 2 and 3"
|
echo "OK comparing archive 2 and 3"
|
||||||
|
|
||||||
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[2]})
|
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[2]})
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
||||||
@@ -150,7 +150,7 @@ if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && (
|
|||||||
fi
|
fi
|
||||||
echo "OK comparing archive 3 and current"
|
echo "OK comparing archive 3 and current"
|
||||||
|
|
||||||
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress none --verbose 1 )
|
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress none --verbose 1 )
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
||||||
@@ -159,7 +159,7 @@ if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && (
|
|||||||
fi
|
fi
|
||||||
echo "OK comparing archive last (3) and current"
|
echo "OK comparing archive last (3) and current"
|
||||||
|
|
||||||
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[2]} --prefix notempty2/notemptyi2 )
|
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress none --verbose 1 --aid ${AIDS[2]} --prefix notempty2/notemptyi2 )
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ! ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ! ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
||||||
@@ -168,7 +168,7 @@ if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && !
|
|||||||
fi
|
fi
|
||||||
echo "OK comparing archive 3 and current with prefix "
|
echo "OK comparing archive 3 and current with prefix "
|
||||||
|
|
||||||
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$"$TESTDATADIR"/testdataDIR"/testdir/to1 --password asdff --progress none --verbose 1 --prefix notempty2/notemptyi2 )
|
OUT=$($CMD diff --from "$TESTDATADIR"/testdata/4 --repo "$TESTDATADIR"/testdataDIR/testdir/to1 --password asdff --progress none --verbose 1 --prefix notempty2/notemptyi2 )
|
||||||
echo "$OUT"
|
echo "$OUT"
|
||||||
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ! ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
if ! ( ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test6 is different' ) && ! ( echo "$OUT" | grep -q 'filexd is different' ) && ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test7 is different' ) \
|
||||||
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
&& ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test4' ) && ! ( echo "$OUT" | grep -q 'notempty2/notemptyi2/test5' ) && ! ( echo "$OUT" | grep -q 'notemptyi2/ignoredir/testa' ) ); then
|
||||||
@@ -178,6 +178,6 @@ fi
|
|||||||
echo "OK comparing archive last (3) and current with prefix "
|
echo "OK comparing archive last (3) and current with prefix "
|
||||||
|
|
||||||
|
|
||||||
rm -rf "$"$TESTDATADIR"/testdataDIR"
|
rm -rf "$TESTDATADIR"/testdataDIR
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
@@ -85,6 +85,6 @@ if ! ( ( echo "$OUT" | grep -q ' notempty/testfile' )\
|
|||||||
fi
|
fi
|
||||||
echo "List 1 ok"
|
echo "List 1 ok"
|
||||||
|
|
||||||
rm -rf "$"$TESTDATADIR"/testdataDIR"
|
rm -rf "$TESTDATADIR"/testdataDIR
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
@@ -9,15 +9,15 @@
|
|||||||
|
|
||||||
TEST(CLITest, Backup) {
|
TEST(CLITest, Backup) {
|
||||||
int ret = system("../../../tests/clitests/scripts/backup.sh");
|
int ret = system("../../../tests/clitests/scripts/backup.sh");
|
||||||
EXPECT_EQ(WEXITSTATUS(ret), 0);
|
ASSERT_EQ(WEXITSTATUS(ret), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CLITest, Ignore) {
|
TEST(CLITest, Ignore) {
|
||||||
int ret = system("../../../tests/clitests/scripts/ignore.sh");
|
int ret = system("../../../tests/clitests/scripts/ignore.sh");
|
||||||
EXPECT_EQ(WEXITSTATUS(ret), 0);
|
ASSERT_EQ(WEXITSTATUS(ret), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CLITest, Diff) {
|
TEST(CLITest, Diff) {
|
||||||
int ret = system("../../../tests/clitests/scripts/diff.sh");
|
int ret = system("../../../tests/clitests/scripts/diff.sh");
|
||||||
EXPECT_EQ(WEXITSTATUS(ret), 0);
|
ASSERT_EQ(WEXITSTATUS(ret), 0);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
CryptoTests
|
CryptoTests
|
||||||
@@ -10,5 +10,5 @@ target_link_libraries(
|
|||||||
GTest::gtest_main crypto
|
GTest::gtest_main crypto
|
||||||
)
|
)
|
||||||
|
|
||||||
gtest_discover_tests(CryptoTests)
|
gtest_discover_tests(CryptoTests DISCOVERY_TIMEOUT 600)
|
||||||
|
|
||||||
|
|||||||
@@ -11,42 +11,42 @@ TEST(CryptoTests, AES) {
|
|||||||
std::string in = "hello1";
|
std::string in = "hello1";
|
||||||
auto enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
auto enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
||||||
auto dec = AES::decrypt(enc, "p1", "e");
|
auto dec = AES::decrypt(enc, "p1", "e");
|
||||||
EXPECT_EQ(in, std::string(dec.begin(), dec.end()));
|
ASSERT_EQ(in, std::string(dec.begin(), dec.end()));
|
||||||
|
|
||||||
in = "";
|
in = "";
|
||||||
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
||||||
dec = AES::decrypt(enc, "p1", "e");
|
dec = AES::decrypt(enc, "p1", "e");
|
||||||
EXPECT_EQ(in, std::string(dec.begin(), dec.end()));
|
ASSERT_EQ(in, std::string(dec.begin(), dec.end()));
|
||||||
|
|
||||||
in = "1234567890asdfg";
|
in = "1234567890asdfg";
|
||||||
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
||||||
dec = AES::decrypt(enc, "p1", "e");
|
dec = AES::decrypt(enc, "p1", "e");
|
||||||
EXPECT_EQ(in, std::string(dec.begin(), dec.end()));
|
ASSERT_EQ(in, std::string(dec.begin(), dec.end()));
|
||||||
|
|
||||||
in = "1234567890asdfgh";
|
in = "1234567890asdfgh";
|
||||||
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
||||||
dec = AES::decrypt(enc, "p1", "e");
|
dec = AES::decrypt(enc, "p1", "e");
|
||||||
EXPECT_EQ(in, std::string(dec.begin(), dec.end()));
|
ASSERT_EQ(in, std::string(dec.begin(), dec.end()));
|
||||||
|
|
||||||
in = "1234567890asdfghe";
|
in = "1234567890asdfghe";
|
||||||
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
||||||
dec = AES::decrypt(enc, "p1", "e");
|
dec = AES::decrypt(enc, "p1", "e");
|
||||||
EXPECT_EQ(in, std::string(dec.begin(), dec.end()));
|
ASSERT_EQ(in, std::string(dec.begin(), dec.end()));
|
||||||
|
|
||||||
in = "1234567890asdfgheq";
|
in = "1234567890asdfgheq";
|
||||||
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
enc = AES::encrypt(std::vector<char>(in.begin(), in.end()), "p1", "e");
|
||||||
dec = AES::decrypt(enc, "p1", "e");
|
dec = AES::decrypt(enc, "p1", "e");
|
||||||
EXPECT_EQ(in, std::string(dec.begin(), dec.end()));
|
ASSERT_EQ(in, std::string(dec.begin(), dec.end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CryptoTests, SHA) {
|
TEST(CryptoTests, SHA) {
|
||||||
std::vector<char> data{'h', 'e', 'l', 'l', 'o'};
|
std::vector<char> data{'h', 'e', 'l', 'l', 'o'};
|
||||||
std::array<unsigned char, 32> excepted{0x2c, 0xf2, 0x4d, 0xba, 0x5f, 0xb0, 0xa3, 0x0e, 0x26, 0xe8, 0x3b, 0x2a, 0xc5, 0xb9, 0xe2, 0x9e, 0x1b, 0x16, 0x1e, 0x5c, 0x1f, 0xa7, 0x42, 0x5e, 0x73, 0x04, 0x33, 0x62, 0x93, 0x8b, 0x98, 0x24};
|
std::array<unsigned char, 32> excepted{0x2c, 0xf2, 0x4d, 0xba, 0x5f, 0xb0, 0xa3, 0x0e, 0x26, 0xe8, 0x3b,
|
||||||
|
0x2a, 0xc5, 0xb9, 0xe2, 0x9e, 0x1b, 0x16, 0x1e, 0x5c, 0x1f, 0xa7,
|
||||||
|
0x42, 0x5e, 0x73, 0x04, 0x33, 0x62, 0x93, 0x8b, 0x98, 0x24};
|
||||||
|
|
||||||
auto out = SHA::calculate(data);
|
auto out = SHA::calculate(data);
|
||||||
|
|
||||||
EXPECT_EQ(out.size(), 32);
|
ASSERT_EQ(out.size(), 32);
|
||||||
for (int i = 0; i < out.size(); i++) {
|
for (int i = 0; i < out.size(); i++) { ASSERT_EQ(static_cast<uint8_t>(out[i]), excepted[i]); }
|
||||||
EXPECT_EQ(static_cast<uint8_t>(out[i]), excepted[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
FullTest
|
FullTest
|
||||||
@@ -10,5 +10,5 @@ target_link_libraries(
|
|||||||
GTest::gtest_main commands utils testUtils repo
|
GTest::gtest_main commands utils testUtils repo
|
||||||
)
|
)
|
||||||
|
|
||||||
gtest_discover_tests(FullTest)
|
gtest_discover_tests(FullTest DISCOVERY_TIMEOUT 600)
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ TEST(FullTest, Simple) {
|
|||||||
{
|
{
|
||||||
std::filesystem::create_directories("Simple/testfrom");
|
std::filesystem::create_directories("Simple/testfrom");
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ofstream o(std::filesystem::path("Simple/testfrom") / ("f" + std::to_string(i)), std::ios::binary | std::ios::out | std::ios::trunc);
|
std::ofstream o(std::filesystem::path("Simple/testfrom") / ("f" + std::to_string(i)),
|
||||||
|
std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
||||||
}
|
}
|
||||||
std::filesystem::create_directories("Simple/testfrom/testdir");
|
std::filesystem::create_directories("Simple/testfrom/testdir");
|
||||||
@@ -60,20 +61,21 @@ TEST(FullTest, Simple) {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
EXPECT_EQ(std::filesystem::is_directory("Simple/testtores/testdir"), true);
|
ASSERT_EQ(std::filesystem::is_directory("Simple/testtores/testdir"), true);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::cerr << "Empty directory doesn't exist!" << std::endl;
|
std::cerr << "Empty directory doesn't exist!" << std::endl;
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ifstream o(std::filesystem::path("Simple/testtores") / ("f" + std::to_string(i)), std::ios::binary | std::ios::in);
|
std::ifstream o(std::filesystem::path("Simple/testtores") / ("f" + std::to_string(i)),
|
||||||
|
std::ios::binary | std::ios::in);
|
||||||
try {
|
try {
|
||||||
EXPECT_EQ(o.is_open(), true);
|
ASSERT_EQ(o.is_open(), true);
|
||||||
for (int j = 0; j < i; j++) {
|
for (int j = 0; j < i; j++) {
|
||||||
char c;
|
char c;
|
||||||
EXPECT_EQ(o.get(c).operator bool(), true);
|
ASSERT_EQ(o.get(c).operator bool(), true);
|
||||||
EXPECT_EQ(static_cast<char>(j % 256), c);
|
ASSERT_EQ(static_cast<char>(j % 256), c);
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::cerr << "Error comparing file " << i << std::endl;
|
std::cerr << "Error comparing file " << i << std::endl;
|
||||||
@@ -89,7 +91,8 @@ TEST(FullTest, SimpleWithIgnore) {
|
|||||||
{
|
{
|
||||||
std::filesystem::create_directories("SimpleWithIgnore/testfrom");
|
std::filesystem::create_directories("SimpleWithIgnore/testfrom");
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ofstream o(std::filesystem::path("SimpleWithIgnore/testfrom") / ("f" + std::to_string(i)), std::ios::binary | std::ios::out | std::ios::trunc);
|
std::ofstream o(std::filesystem::path("SimpleWithIgnore/testfrom") / ("f" + std::to_string(i)),
|
||||||
|
std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
||||||
}
|
}
|
||||||
std::filesystem::create_directories("SimpleWithIgnore/testfrom/testdir");
|
std::filesystem::create_directories("SimpleWithIgnore/testfrom/testdir");
|
||||||
@@ -134,7 +137,9 @@ TEST(FullTest, SimpleWithIgnore) {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
conf.add("repo", "SimpleWithIgnore/testto").add("aid", std::to_string(aid)).add("to", "SimpleWithIgnore/testtores");
|
conf.add("repo", "SimpleWithIgnore/testto")
|
||||||
|
.add("aid", std::to_string(aid))
|
||||||
|
.add("to", "SimpleWithIgnore/testtores");
|
||||||
|
|
||||||
auto repo = std::make_unique<FileRepository>(conf);
|
auto repo = std::make_unique<FileRepository>(conf);
|
||||||
repo->open();
|
repo->open();
|
||||||
@@ -145,15 +150,16 @@ TEST(FullTest, SimpleWithIgnore) {
|
|||||||
cmd.run(Context{&logger, repo.get()});
|
cmd.run(Context{&logger, repo.get()});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
EXPECT_EQ(std::filesystem::is_directory("SimpleWithIgnore/testtores/testdir"), true);
|
ASSERT_EQ(std::filesystem::is_directory("SimpleWithIgnore/testtores/testdir"), true);
|
||||||
|
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ifstream o(std::filesystem::path("SimpleWithIgnore/testtores") / ("f" + std::to_string(i)), std::ios::binary | std::ios::in);
|
std::ifstream o(std::filesystem::path("SimpleWithIgnore/testtores") / ("f" + std::to_string(i)),
|
||||||
EXPECT_EQ(o.is_open(), true);
|
std::ios::binary | std::ios::in);
|
||||||
|
ASSERT_EQ(o.is_open(), true);
|
||||||
for (int j = 0; j < i; j++) {
|
for (int j = 0; j < i; j++) {
|
||||||
char c;
|
char c;
|
||||||
EXPECT_EQ(o.get(c).operator bool(), true);
|
ASSERT_EQ(o.get(c).operator bool(), true);
|
||||||
EXPECT_EQ(static_cast<char>(j % 256), c);
|
ASSERT_EQ(static_cast<char>(j % 256), c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,31 +167,31 @@ TEST(FullTest, SimpleWithIgnore) {
|
|||||||
std::ifstream file("SimpleWithIgnore/testtores/testdir2/.ignore");
|
std::ifstream file("SimpleWithIgnore/testtores/testdir2/.ignore");
|
||||||
std::string s;
|
std::string s;
|
||||||
file >> s;
|
file >> s;
|
||||||
EXPECT_EQ(s, "hello.txt");
|
ASSERT_EQ(s, "hello.txt");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir3/.ignore");
|
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir3/.ignore");
|
||||||
std::string s;
|
std::string s;
|
||||||
file >> s;
|
file >> s;
|
||||||
EXPECT_EQ(s, ".*\\.txt");
|
ASSERT_EQ(s, ".*\\.txt");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::ifstream file("SimpleWithIgnore/testtores/testdir2/hello.txt");
|
std::ifstream file("SimpleWithIgnore/testtores/testdir2/hello.txt");
|
||||||
EXPECT_EQ(!file, true);
|
ASSERT_EQ(!file, true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir3/hello.txt");
|
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir3/hello.txt");
|
||||||
EXPECT_EQ(!file, true);
|
ASSERT_EQ(!file, true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir3/asdf.txt");
|
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir3/asdf.txt");
|
||||||
EXPECT_EQ(!file, true);
|
ASSERT_EQ(!file, true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir4/asdf.txt");
|
std::ifstream file("SimpleWithIgnore/testtores/testdir2/testdir4/asdf.txt");
|
||||||
std::string s;
|
std::string s;
|
||||||
file >> s;
|
file >> s;
|
||||||
EXPECT_EQ(s, "asdf2");
|
ASSERT_EQ(s, "asdf2");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,13 +202,16 @@ TEST(FullTest, SimpleWithCompress) {
|
|||||||
{
|
{
|
||||||
std::filesystem::create_directories("SimpleWithCompress/testfrom");
|
std::filesystem::create_directories("SimpleWithCompress/testfrom");
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ofstream o(std::filesystem::path("SimpleWithCompress/testfrom") / ("f" + std::to_string(i)), std::ios::binary | std::ios::out | std::ios::trunc);
|
std::ofstream o(std::filesystem::path("SimpleWithCompress/testfrom") / ("f" + std::to_string(i)),
|
||||||
|
std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
||||||
}
|
}
|
||||||
std::filesystem::create_directories("SimpleWithCompress/testfrom/testdir");
|
std::filesystem::create_directories("SimpleWithCompress/testfrom/testdir");
|
||||||
|
|
||||||
Config conf;
|
Config conf;
|
||||||
conf.add("repo", "SimpleWithCompress/testto").add("compression", "zlib").add("from", "SimpleWithCompress/testfrom");
|
conf.add("repo", "SimpleWithCompress/testto")
|
||||||
|
.add("compression", "zlib")
|
||||||
|
.add("from", "SimpleWithCompress/testfrom");
|
||||||
|
|
||||||
auto repo = std::make_unique<FileRepository>(conf);
|
auto repo = std::make_unique<FileRepository>(conf);
|
||||||
repo->init();
|
repo->init();
|
||||||
@@ -215,7 +224,9 @@ TEST(FullTest, SimpleWithCompress) {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
conf.add("repo", "SimpleWithCompress/testto").add("aid", std::to_string(aid)).add("to", "SimpleWithCompress/testtores");
|
conf.add("repo", "SimpleWithCompress/testto")
|
||||||
|
.add("aid", std::to_string(aid))
|
||||||
|
.add("to", "SimpleWithCompress/testtores");
|
||||||
|
|
||||||
auto repo = std::make_unique<FileRepository>(conf);
|
auto repo = std::make_unique<FileRepository>(conf);
|
||||||
repo->open();
|
repo->open();
|
||||||
@@ -226,15 +237,16 @@ TEST(FullTest, SimpleWithCompress) {
|
|||||||
cmd.run(Context{&logger, repo.get()});
|
cmd.run(Context{&logger, repo.get()});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
EXPECT_EQ(std::filesystem::is_directory("SimpleWithCompress/testtores/testdir"), true);
|
ASSERT_EQ(std::filesystem::is_directory("SimpleWithCompress/testtores/testdir"), true);
|
||||||
|
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ifstream o(std::filesystem::path("SimpleWithCompress/testtores") / ("f" + std::to_string(i)), std::ios::binary | std::ios::in);
|
std::ifstream o(std::filesystem::path("SimpleWithCompress/testtores") / ("f" + std::to_string(i)),
|
||||||
EXPECT_EQ(o.is_open(), true);
|
std::ios::binary | std::ios::in);
|
||||||
|
ASSERT_EQ(o.is_open(), true);
|
||||||
for (int j = 0; j < i; j++) {
|
for (int j = 0; j < i; j++) {
|
||||||
char c;
|
char c;
|
||||||
EXPECT_EQ(o.get(c).operator bool(), true);
|
ASSERT_EQ(o.get(c).operator bool(), true);
|
||||||
EXPECT_EQ(static_cast<char>(j % 256), c);
|
ASSERT_EQ(static_cast<char>(j % 256), c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,13 +258,19 @@ TEST(FullTest, SimpleWithCompEnd) {
|
|||||||
{
|
{
|
||||||
std::filesystem::create_directories("SimpleWithCompEnd/testfrom");
|
std::filesystem::create_directories("SimpleWithCompEnd/testfrom");
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ofstream o(std::filesystem::path("SimpleWithCompEnd/testfrom") / ("f" + std::to_string(i)), std::ios::binary | std::ios::out | std::ios::trunc);
|
std::ofstream o(std::filesystem::path("SimpleWithCompEnd/testfrom") / ("f" + std::to_string(i)),
|
||||||
|
std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
for (int j = 0; j < i; j++) o.put(static_cast<char>(j % 256));
|
||||||
}
|
}
|
||||||
std::filesystem::create_directories("SimpleWithCompEnd/testfrom/testdir");
|
std::filesystem::create_directories("SimpleWithCompEnd/testfrom/testdir");
|
||||||
|
|
||||||
Config conf;
|
Config conf;
|
||||||
conf.add("repo", "SimpleWithCompEnd/testto").add("compression", "zlib").add("from", "SimpleWithCompEnd/testfrom").add("encryption", "aes").add("password", "testp").add("salt", "tests");
|
conf.add("repo", "SimpleWithCompEnd/testto")
|
||||||
|
.add("compression", "zlib")
|
||||||
|
.add("from", "SimpleWithCompEnd/testfrom")
|
||||||
|
.add("encryption", "aes")
|
||||||
|
.add("password", "testp")
|
||||||
|
.add("salt", "tests");
|
||||||
|
|
||||||
auto repo = std::make_unique<FileRepository>(conf);
|
auto repo = std::make_unique<FileRepository>(conf);
|
||||||
repo->init();
|
repo->init();
|
||||||
@@ -272,14 +290,17 @@ TEST(FullTest, SimpleWithCompEnd) {
|
|||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
try {
|
try {
|
||||||
EXPECT_EQ(repo->open(), false);
|
ASSERT_EQ(repo->open(), false);
|
||||||
ok = false;
|
ok = false;
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
EXPECT_EQ(ok, true);
|
ASSERT_EQ(ok, true);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
conf.add("repo", "SimpleWithCompEnd/testto").add("password", "testp").add("aid", std::to_string(aid)).add("to", "SimpleWithCompEnd/testtores");
|
conf.add("repo", "SimpleWithCompEnd/testto")
|
||||||
|
.add("password", "testp")
|
||||||
|
.add("aid", std::to_string(aid))
|
||||||
|
.add("to", "SimpleWithCompEnd/testtores");
|
||||||
|
|
||||||
auto repo = std::make_unique<FileRepository>(conf);
|
auto repo = std::make_unique<FileRepository>(conf);
|
||||||
repo->open();
|
repo->open();
|
||||||
@@ -291,15 +312,16 @@ TEST(FullTest, SimpleWithCompEnd) {
|
|||||||
cmd.run(Context{&logger, repo.get()});
|
cmd.run(Context{&logger, repo.get()});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
EXPECT_EQ(std::filesystem::is_directory("SimpleWithCompEnd/testtores/testdir"), true);
|
ASSERT_EQ(std::filesystem::is_directory("SimpleWithCompEnd/testtores/testdir"), true);
|
||||||
|
|
||||||
for (int i = 0; i < 257; i++) {
|
for (int i = 0; i < 257; i++) {
|
||||||
std::ifstream o(std::filesystem::path("SimpleWithCompEnd/testtores") / ("f" + std::to_string(i)), std::ios::binary | std::ios::in);
|
std::ifstream o(std::filesystem::path("SimpleWithCompEnd/testtores") / ("f" + std::to_string(i)),
|
||||||
EXPECT_EQ(o.is_open(), true);
|
std::ios::binary | std::ios::in);
|
||||||
|
ASSERT_EQ(o.is_open(), true);
|
||||||
for (int j = 0; j < i; j++) {
|
for (int j = 0; j < i; j++) {
|
||||||
char c;
|
char c;
|
||||||
EXPECT_EQ(o.get(c).operator bool(), true);
|
ASSERT_EQ(o.get(c).operator bool(), true);
|
||||||
EXPECT_EQ(static_cast<char>(j % 256), c);
|
ASSERT_EQ(static_cast<char>(j % 256), c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,16 +332,40 @@ TEST(FullTest, Fuzz) {
|
|||||||
srand(time(nullptr));
|
srand(time(nullptr));
|
||||||
std::vector<Config> confs;
|
std::vector<Config> confs;
|
||||||
Config conf;
|
Config conf;
|
||||||
conf.add("repo", "Fuzz/testto").add("compression", "none").add("from", "Fuzz/testfrom").add("encryption", "none").add("password", "testp").add("salt", "tests").add("progress", "none");
|
conf.add("repo", "Fuzz/testto")
|
||||||
|
.add("compression", "none")
|
||||||
|
.add("from", "Fuzz/testfrom")
|
||||||
|
.add("encryption", "none")
|
||||||
|
.add("password", "testp")
|
||||||
|
.add("salt", "tests")
|
||||||
|
.add("progress", "none");
|
||||||
confs.emplace_back(conf);
|
confs.emplace_back(conf);
|
||||||
conf = Config();
|
conf = Config();
|
||||||
conf.add("repo", "Fuzz/testto").add("compression", "zlib").add("from", "Fuzz/testfrom").add("encryption", "none").add("password", "testp").add("salt", "tests").add("progress", "none");
|
conf.add("repo", "Fuzz/testto")
|
||||||
|
.add("compression", "zlib")
|
||||||
|
.add("from", "Fuzz/testfrom")
|
||||||
|
.add("encryption", "none")
|
||||||
|
.add("password", "testp")
|
||||||
|
.add("salt", "tests")
|
||||||
|
.add("progress", "none");
|
||||||
confs.emplace_back(conf);
|
confs.emplace_back(conf);
|
||||||
conf = Config();
|
conf = Config();
|
||||||
conf.add("repo", "Fuzz/testto").add("compression", "none").add("from", "Fuzz/testfrom").add("encryption", "zlib").add("password", "testp").add("salt", "tests").add("progress", "none");
|
conf.add("repo", "Fuzz/testto")
|
||||||
|
.add("compression", "none")
|
||||||
|
.add("from", "Fuzz/testfrom")
|
||||||
|
.add("encryption", "zlib")
|
||||||
|
.add("password", "testp")
|
||||||
|
.add("salt", "tests")
|
||||||
|
.add("progress", "none");
|
||||||
confs.emplace_back(conf);
|
confs.emplace_back(conf);
|
||||||
conf = Config();
|
conf = Config();
|
||||||
conf.add("repo", "Fuzz/testto").add("compression", "zlib").add("from", "Fuzz/testfrom").add("encryption", "aes").add("password", "testp").add("salt", "tests").add("progress", "none");
|
conf.add("repo", "Fuzz/testto")
|
||||||
|
.add("compression", "zlib")
|
||||||
|
.add("from", "Fuzz/testfrom")
|
||||||
|
.add("encryption", "aes")
|
||||||
|
.add("password", "testp")
|
||||||
|
.add("salt", "tests")
|
||||||
|
.add("progress", "none");
|
||||||
confs.emplace_back(conf);
|
confs.emplace_back(conf);
|
||||||
|
|
||||||
for (auto const &conf: confs) {
|
for (auto const &conf: confs) {
|
||||||
@@ -332,7 +378,8 @@ TEST(FullTest, Fuzz) {
|
|||||||
{
|
{
|
||||||
std::filesystem::create_directories("Fuzz/testfrom");
|
std::filesystem::create_directories("Fuzz/testfrom");
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
std::ofstream o(std::filesystem::path("Fuzz/testfrom") / ("f" + std::to_string(i)), std::ios::binary | std::ios::out | std::ios::trunc);
|
std::ofstream o(std::filesystem::path("Fuzz/testfrom") / ("f" + std::to_string(i)),
|
||||||
|
std::ios::binary | std::ios::out | std::ios::trunc);
|
||||||
for (int j = 0; j < i; j++) o.put(j % 2);
|
for (int j = 0; j < i; j++) o.put(j % 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,28 +397,28 @@ TEST(FullTest, Fuzz) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
if (filetobreak & 0b00000001) {
|
if (filetobreak & 0b00000001) {
|
||||||
for (int i = 0; i < cutoff; i++)
|
for (int i = 0; i < cutoff; i++) randomChange("Fuzz/testto/1");
|
||||||
randomChange("Fuzz/testto/1");
|
|
||||||
if (cutoff > 5)
|
if (cutoff > 5)
|
||||||
std::filesystem::resize_file("Fuzz/testto/1", std::filesystem::file_size("Fuzz/testto/1") - cutoff);
|
std::filesystem::resize_file("Fuzz/testto/1",
|
||||||
|
std::filesystem::file_size("Fuzz/testto/1") - cutoff);
|
||||||
}
|
}
|
||||||
if (filetobreak & 0b00000010) {
|
if (filetobreak & 0b00000010) {
|
||||||
for (int i = 0; i < cutoff; i++)
|
for (int i = 0; i < cutoff; i++) randomChange("Fuzz/testto/index");
|
||||||
randomChange("Fuzz/testto/index");
|
|
||||||
if (cutoff > 5)
|
if (cutoff > 5)
|
||||||
std::filesystem::resize_file("Fuzz/testto/index", std::filesystem::file_size("Fuzz/testto/index") - cutoff);
|
std::filesystem::resize_file("Fuzz/testto/index",
|
||||||
|
std::filesystem::file_size("Fuzz/testto/index") - cutoff);
|
||||||
}
|
}
|
||||||
if (filetobreak & 0b00000100) {
|
if (filetobreak & 0b00000100) {
|
||||||
for (int i = 0; i < cutoff; i++)
|
for (int i = 0; i < cutoff; i++) randomChange("Fuzz/testto/offsets");
|
||||||
randomChange("Fuzz/testto/offsets");
|
|
||||||
if (cutoff > 5)
|
if (cutoff > 5)
|
||||||
std::filesystem::resize_file("Fuzz/testto/offsets", std::filesystem::file_size("Fuzz/testto/offsets") - cutoff);
|
std::filesystem::resize_file("Fuzz/testto/offsets",
|
||||||
|
std::filesystem::file_size("Fuzz/testto/offsets") - cutoff);
|
||||||
}
|
}
|
||||||
if (filetobreak & 0b00001000) {
|
if (filetobreak & 0b00001000) {
|
||||||
for (int i = 0; i < cutoff; i++)
|
for (int i = 0; i < cutoff; i++) randomChange("Fuzz/testto/info");
|
||||||
randomChange("Fuzz/testto/info");
|
|
||||||
if (cutoff > 5)
|
if (cutoff > 5)
|
||||||
std::filesystem::resize_file("Fuzz/testto/info", std::filesystem::file_size("Fuzz/testto/info") - cutoff);
|
std::filesystem::resize_file("Fuzz/testto/info",
|
||||||
|
std::filesystem::file_size("Fuzz/testto/info") - cutoff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,10 +438,9 @@ TEST(FullTest, Fuzz) {
|
|||||||
CommandRestore cmd;
|
CommandRestore cmd;
|
||||||
cmd.run(Context{&logger, repo.get()});
|
cmd.run(Context{&logger, repo.get()});
|
||||||
auto outstr = runnerout.str();
|
auto outstr = runnerout.str();
|
||||||
if (outstr.find("Error") == std::string::npos)
|
if (outstr.find("Error") == std::string::npos) ok = false;
|
||||||
ok = false;
|
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
EXPECT_EQ(ok, true);
|
ASSERT_EQ(ok, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
FileRepositoryTest
|
FileRepositoryTest
|
||||||
@@ -20,5 +20,5 @@ target_link_libraries(
|
|||||||
GTest::gtest_main commands utils testUtils repo
|
GTest::gtest_main commands utils testUtils repo
|
||||||
)
|
)
|
||||||
|
|
||||||
gtest_discover_tests(ChunkTest)
|
gtest_discover_tests(ChunkTest DISCOVERY_TIMEOUT 600)
|
||||||
gtest_discover_tests(FileRepositoryTest)
|
gtest_discover_tests(FileRepositoryTest DISCOVERY_TIMEOUT 600)
|
||||||
|
|||||||
@@ -29,26 +29,18 @@ TEST(Chunk, Deserialize) {
|
|||||||
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
||||||
Chunk o2e(777, {1}, data2);
|
Chunk o2e(777, {1}, data2);
|
||||||
|
|
||||||
EXPECT_EQ(o1.id, o1e.id);
|
ASSERT_EQ(o1.id, o1e.id);
|
||||||
EXPECT_EQ(o2.id, o2e.id);
|
ASSERT_EQ(o2.id, o2e.id);
|
||||||
EXPECT_EQ((int) o1.type, (int) o1e.type);
|
ASSERT_EQ((int) o1.type, (int) o1e.type);
|
||||||
EXPECT_EQ((int) o2.type, (int) o2e.type);
|
ASSERT_EQ((int) o2.type, (int) o2e.type);
|
||||||
|
|
||||||
EXPECT_EQ(o1.data.size(), o1e.data.size());
|
ASSERT_EQ(o1.data.size(), o1e.data.size());
|
||||||
EXPECT_EQ(o2.data.size(), o2e.data.size());
|
ASSERT_EQ(o2.data.size(), o2e.data.size());
|
||||||
for (int i = 0; i < o1.data.size(); i++) {
|
for (int i = 0; i < o1.data.size(); i++) { ASSERT_EQ(o1.data[i], o1e.data[i]); }
|
||||||
EXPECT_EQ(o1.data[i], o1e.data[i]);
|
for (int i = 0; i < o2.data.size(); i++) { ASSERT_EQ(o2.data[i], o2e.data[i]); }
|
||||||
}
|
|
||||||
for (int i = 0; i < o2.data.size(); i++) {
|
|
||||||
EXPECT_EQ(o2.data[i], o2e.data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < o1.SHA.size(); i++) {
|
for (int i = 0; i < o1.SHA.size(); i++) { ASSERT_EQ(o1.SHA[i], o1e.SHA[i]); }
|
||||||
EXPECT_EQ(o1.SHA[i], o1e.SHA[i]);
|
for (int i = 0; i < o2.SHA.size(); i++) { ASSERT_EQ(o2.SHA[i], o2e.SHA[i]); }
|
||||||
}
|
|
||||||
for (int i = 0; i < o2.SHA.size(); i++) {
|
|
||||||
EXPECT_EQ(o2.SHA[i], o2e.SHA[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +49,7 @@ TEST(Chunk, Garbage) {
|
|||||||
auto eb = e.cbegin();
|
auto eb = e.cbegin();
|
||||||
try {
|
try {
|
||||||
Chunk o1(eb, e.cend());
|
Chunk o1(eb, e.cend());
|
||||||
} catch (...) {
|
} catch (...) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
FAIL() << "Object constructed with garbage data!";
|
FAIL() << "Object constructed with garbage data!";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,9 +58,7 @@ TEST(Chunk, Garbage2) {
|
|||||||
auto eb = e.cbegin();
|
auto eb = e.cbegin();
|
||||||
try {
|
try {
|
||||||
Chunk o1(eb, e.cend());
|
Chunk o1(eb, e.cend());
|
||||||
} catch (...) {
|
} catch (...) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
FAIL() << "Object constructed with garbage data!";
|
FAIL() << "Object constructed with garbage data!";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,16 +81,13 @@ TEST(Chunk, Garbage3) {
|
|||||||
try {
|
try {
|
||||||
Chunk o1 = Serialize::deserialize<Chunk>(s1);
|
Chunk o1 = Serialize::deserialize<Chunk>(s1);
|
||||||
fail = true;
|
fail = true;
|
||||||
} catch (...) {
|
} catch (...) {}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Chunk o2 = Serialize::deserialize<Chunk>(s2);
|
Chunk o2 = Serialize::deserialize<Chunk>(s2);
|
||||||
fail = true;
|
fail = true;
|
||||||
} catch (...) {
|
} catch (...) {}
|
||||||
}
|
|
||||||
|
|
||||||
if (fail)
|
if (fail) FAIL() << "Object constructed with garbage data!";
|
||||||
FAIL() << "Object constructed with garbage data!";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,7 @@ TEST(FileRepository, Deserialize) {
|
|||||||
conf.add("repo", "Deserizlize/testrepo");
|
conf.add("repo", "Deserizlize/testrepo");
|
||||||
FileRepository repo(conf);
|
FileRepository repo(conf);
|
||||||
repo.init();
|
repo.init();
|
||||||
std::vector<char>
|
std::vector<char> data1{'a', 'b', 'c', 'e'};
|
||||||
data1{'a', 'b', 'c', 'e'};
|
|
||||||
|
|
||||||
std::string o1k(16, '\0');
|
std::string o1k(16, '\0');
|
||||||
std::string o2k(16, '\0');
|
std::string o2k(16, '\0');
|
||||||
@@ -33,8 +32,8 @@ TEST(FileRepository, Deserialize) {
|
|||||||
repo.putObject(o2);
|
repo.putObject(o2);
|
||||||
|
|
||||||
|
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
@@ -46,11 +45,11 @@ TEST(FileRepository, Deserialize) {
|
|||||||
std::string o2k(16, '\0');
|
std::string o2k(16, '\0');
|
||||||
o2k[0] = 1;
|
o2k[0] = 1;
|
||||||
o2k[1] = 2;
|
o2k[1] = 2;
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
||||||
|
|
||||||
auto o1o = repo.getObject(666);
|
auto o1o = repo.getObjectRaw(666);
|
||||||
auto o2o = repo.getObject(777);
|
auto o2o = repo.getObjectRaw(777);
|
||||||
|
|
||||||
auto o1ob = o1o.cbegin();
|
auto o1ob = o1o.cbegin();
|
||||||
auto o2ob = o2o.cbegin();
|
auto o2ob = o2o.cbegin();
|
||||||
@@ -62,22 +61,18 @@ TEST(FileRepository, Deserialize) {
|
|||||||
std::vector<char> data2{'q', 'w', 'e', 'r', static_cast<char>(255)};
|
std::vector<char> data2{'q', 'w', 'e', 'r', static_cast<char>(255)};
|
||||||
Chunk o2e(777, o2k, data2);
|
Chunk o2e(777, o2k, data2);
|
||||||
|
|
||||||
EXPECT_EQ(o1.id, o1e.id);
|
ASSERT_EQ(o1.id, o1e.id);
|
||||||
EXPECT_EQ(o2.id, o2e.id);
|
ASSERT_EQ(o2.id, o2e.id);
|
||||||
EXPECT_EQ((int) o1.type, (int) o1e.type);
|
ASSERT_EQ((int) o1.type, (int) o1e.type);
|
||||||
EXPECT_EQ((int) o2.type, (int) o2e.type);
|
ASSERT_EQ((int) o2.type, (int) o2e.type);
|
||||||
auto o1d = o1.data;
|
auto o1d = o1.data;
|
||||||
auto o1ed = o1e.data;
|
auto o1ed = o1e.data;
|
||||||
auto o2d = o2.data;
|
auto o2d = o2.data;
|
||||||
auto o2ed = o2e.data;
|
auto o2ed = o2e.data;
|
||||||
EXPECT_EQ(o1.data.size(), o1e.data.size());
|
ASSERT_EQ(o1.data.size(), o1e.data.size());
|
||||||
EXPECT_EQ(o2.data.size(), o2e.data.size());
|
ASSERT_EQ(o2.data.size(), o2e.data.size());
|
||||||
for (int i = 0; i < o1.data.size(); i++) {
|
for (int i = 0; i < o1.data.size(); i++) { ASSERT_EQ(o1.data[i], o1e.data[i]); }
|
||||||
EXPECT_EQ(o1.data[i], o1e.data[i]);
|
for (int i = 0; i < o2.data.size(); i++) { ASSERT_EQ(o2.data[i], o2e.data[i]); }
|
||||||
}
|
|
||||||
for (int i = 0; i < o2.data.size(); i++) {
|
|
||||||
EXPECT_EQ(o2.data[i], o2e.data[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,8 +94,7 @@ TEST(FileRepository, Filters) {
|
|||||||
std::string o2k(16, '\0');
|
std::string o2k(16, '\0');
|
||||||
o2k[0] = 1;
|
o2k[0] = 1;
|
||||||
o2k[1] = 2;
|
o2k[1] = 2;
|
||||||
std::vector<char>
|
std::vector<char> data1{'a', 'b', 'c', 'e'};
|
||||||
data1{'a', 'b', 'c', 'e'};
|
|
||||||
Chunk o1(666, o1k, data1);
|
Chunk o1(666, o1k, data1);
|
||||||
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
||||||
Chunk o2(777, o2k, data2);
|
Chunk o2(777, o2k, data2);
|
||||||
@@ -109,8 +103,8 @@ TEST(FileRepository, Filters) {
|
|||||||
repo.putObject(o2);
|
repo.putObject(o2);
|
||||||
|
|
||||||
|
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
@@ -132,7 +126,7 @@ TEST(FileRepository, Filters) {
|
|||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto o1o = repo.getObject(666);
|
auto o1o = repo.getObjectRaw(666);
|
||||||
auto o1ob = o1o.cbegin();
|
auto o1ob = o1o.cbegin();
|
||||||
|
|
||||||
Chunk o1(o1ob, o1o.cend());
|
Chunk o1(o1ob, o1o.cend());
|
||||||
@@ -140,15 +134,14 @@ TEST(FileRepository, Filters) {
|
|||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto o2o = repo.getObject(777);
|
auto o2o = repo.getObjectRaw(777);
|
||||||
auto o2ob = o2o.cbegin();
|
auto o2ob = o2o.cbegin();
|
||||||
|
|
||||||
Chunk o2(o2ob, o2o.cend());
|
Chunk o2(o2ob, o2o.cend());
|
||||||
err = true;
|
err = true;
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
|
|
||||||
if (err)
|
if (err) throw Exception("Object constructed with garbage data!");
|
||||||
throw Exception("Object constructed with garbage data!");
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
@@ -167,11 +160,11 @@ TEST(FileRepository, Filters) {
|
|||||||
std::string o2k(16, '\0');
|
std::string o2k(16, '\0');
|
||||||
o2k[0] = 1;
|
o2k[0] = 1;
|
||||||
o2k[1] = 2;
|
o2k[1] = 2;
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 666);
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 777);
|
||||||
|
|
||||||
auto o1o = repo.getObject(666);
|
auto o1o = repo.getObjectRaw(666);
|
||||||
auto o2o = repo.getObject(777);
|
auto o2o = repo.getObjectRaw(777);
|
||||||
|
|
||||||
auto o1ob = o1o.cbegin();
|
auto o1ob = o1o.cbegin();
|
||||||
auto o2ob = o2o.cbegin();
|
auto o2ob = o2o.cbegin();
|
||||||
@@ -183,27 +176,22 @@ TEST(FileRepository, Filters) {
|
|||||||
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
||||||
Chunk o2e(777, o2k, data2);
|
Chunk o2e(777, o2k, data2);
|
||||||
|
|
||||||
EXPECT_EQ(o1.id, o1e.id);
|
ASSERT_EQ(o1.id, o1e.id);
|
||||||
EXPECT_EQ(o2.id, o2e.id);
|
ASSERT_EQ(o2.id, o2e.id);
|
||||||
EXPECT_EQ((int) o1.type, (int) o1e.type);
|
ASSERT_EQ((int) o1.type, (int) o1e.type);
|
||||||
EXPECT_EQ((int) o2.type, (int) o2e.type);
|
ASSERT_EQ((int) o2.type, (int) o2e.type);
|
||||||
auto o1d = o1.data;
|
auto o1d = o1.data;
|
||||||
auto o1ed = o1e.data;
|
auto o1ed = o1e.data;
|
||||||
auto o2d = o2.data;
|
auto o2d = o2.data;
|
||||||
auto o2ed = o2e.data;
|
auto o2ed = o2e.data;
|
||||||
EXPECT_EQ(o1.data.size(), o1e.data.size());
|
ASSERT_EQ(o1.data.size(), o1e.data.size());
|
||||||
EXPECT_EQ(o2.data.size(), o2e.data.size());
|
ASSERT_EQ(o2.data.size(), o2e.data.size());
|
||||||
for (int i = 0; i < o1.data.size(); i++) {
|
for (int i = 0; i < o1.data.size(); i++) { ASSERT_EQ(o1.data[i], o1e.data[i]); }
|
||||||
EXPECT_EQ(o1.data[i], o1e.data[i]);
|
for (int i = 0; i < o2.data.size(); i++) { ASSERT_EQ(o2.data[i], o2e.data[i]); }
|
||||||
}
|
|
||||||
for (int i = 0; i < o2.data.size(); i++) {
|
|
||||||
EXPECT_EQ(o2.data[i], o2e.data[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileRepository, IDsDisabled) {
|
TEST(FileRepository, IDsDisabled) {
|
||||||
GTEST_SKIP();
|
|
||||||
Cleaner c({"IDS/testrepo"});
|
Cleaner c({"IDS/testrepo"});
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
@@ -215,29 +203,28 @@ TEST(FileRepository, IDsDisabled) {
|
|||||||
std::string o2k(16, '\0');
|
std::string o2k(16, '\0');
|
||||||
o2k[0] = 1;
|
o2k[0] = 1;
|
||||||
|
|
||||||
std::vector<char>
|
std::vector<char> data1{'a', 'b', 'c', 'e'};
|
||||||
data1{'a', 'b', 'c', 'e'};
|
|
||||||
Chunk o1(repo.getId(), o1k, data1);
|
Chunk o1(repo.getId(), o1k, data1);
|
||||||
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
||||||
Chunk o2(repo.getId(), o2k, data2);
|
Chunk o2(repo.getId(), o2k, data2);
|
||||||
|
|
||||||
EXPECT_EQ(o1.id, 1);
|
ASSERT_EQ(o1.id, 1);
|
||||||
EXPECT_EQ(o2.id, 2);
|
ASSERT_EQ(o2.id, 2);
|
||||||
|
|
||||||
repo.putObject(o1);
|
repo.putObject(o1);
|
||||||
repo.putObject(o2);
|
repo.putObject(o2);
|
||||||
|
|
||||||
|
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 1);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 1);
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 2);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
conf.add("repo", "IDS/testrepo");
|
conf.add("repo", "IDS/testrepo");
|
||||||
FileRepository repo(conf);
|
FileRepository repo(conf);
|
||||||
repo.open();
|
repo.open();
|
||||||
auto o1o = repo.getObject(1);
|
auto o1o = repo.getObjectRaw(1);
|
||||||
auto o2o = repo.getObject(2);
|
auto o2o = repo.getObjectRaw(2);
|
||||||
|
|
||||||
auto o1ob = o1o.cbegin();
|
auto o1ob = o1o.cbegin();
|
||||||
auto o2ob = o2o.cbegin();
|
auto o2ob = o2o.cbegin();
|
||||||
@@ -251,29 +238,25 @@ TEST(FileRepository, IDsDisabled) {
|
|||||||
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
std::vector<char> data2{'q', 'w', 'e', 'r', 'b'};
|
||||||
Chunk o2e(2, o2k, data2);
|
Chunk o2e(2, o2k, data2);
|
||||||
|
|
||||||
EXPECT_EQ(o1.id, o1e.id);
|
ASSERT_EQ(o1.id, o1e.id);
|
||||||
EXPECT_EQ(o2.id, o2e.id);
|
ASSERT_EQ(o2.id, o2e.id);
|
||||||
EXPECT_EQ((int) o1.type, (int) o1e.type);
|
ASSERT_EQ((int) o1.type, (int) o1e.type);
|
||||||
EXPECT_EQ((int) o2.type, (int) o2e.type);
|
ASSERT_EQ((int) o2.type, (int) o2e.type);
|
||||||
auto o1d = o1.data;
|
auto o1d = o1.data;
|
||||||
auto o1ed = o1e.data;
|
auto o1ed = o1e.data;
|
||||||
auto o2d = o2.data;
|
auto o2d = o2.data;
|
||||||
auto o2ed = o2e.data;
|
auto o2ed = o2e.data;
|
||||||
EXPECT_EQ(o1.data.size(), o1e.data.size());
|
ASSERT_EQ(o1.data.size(), o1e.data.size());
|
||||||
EXPECT_EQ(o2.data.size(), o2e.data.size());
|
ASSERT_EQ(o2.data.size(), o2e.data.size());
|
||||||
for (int i = 0; i < o1.data.size(); i++) {
|
for (int i = 0; i < o1.data.size(); i++) { ASSERT_EQ(o1.data[i], o1e.data[i]); }
|
||||||
EXPECT_EQ(o1.data[i], o1e.data[i]);
|
for (int i = 0; i < o2.data.size(); i++) { ASSERT_EQ(o2.data[i], o2e.data[i]); }
|
||||||
}
|
|
||||||
for (int i = 0; i < o2.data.size(); i++) {
|
|
||||||
EXPECT_EQ(o2.data[i], o2e.data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 1);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o1k), 1);
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 2);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 2);
|
||||||
|
|
||||||
|
|
||||||
repo.deleteObject(o1);
|
repo.deleteObjects({o1.id});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Config conf;
|
Config conf;
|
||||||
@@ -283,13 +266,12 @@ TEST(FileRepository, IDsDisabled) {
|
|||||||
|
|
||||||
std::string o2k(16, '\0');
|
std::string o2k(16, '\0');
|
||||||
o2k[0] = 1;
|
o2k[0] = 1;
|
||||||
EXPECT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 2);
|
ASSERT_EQ(repo.getObjectId(Object::ObjectType::Chunk, o2k), 2);
|
||||||
|
|
||||||
auto id = repo.getId();
|
auto id = repo.getId();
|
||||||
EXPECT_EQ(id, 1);
|
ASSERT_EQ(id, 1);
|
||||||
std::vector<char>
|
std::vector<char> data1{'a', 'b', 'c', 'e'};
|
||||||
data1{'a', 'b', 'c', 'e'};
|
|
||||||
Chunk o1(id, o2k, data1);
|
Chunk o1(id, o2k, data1);
|
||||||
EXPECT_EQ(repo.getId(), 3);
|
ASSERT_EQ(repo.getId(), 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.22)
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
add_library(testUtils srcs/Cleaner.cpp)
|
add_library(testUtils srcs/Cleaner.cpp)
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,9 @@
|
|||||||
#include "Cleaner.h"
|
#include "Cleaner.h"
|
||||||
|
|
||||||
Cleaner::Cleaner(std::vector<std::filesystem::path> toClean) : toClean(std::move(toClean)) {
|
Cleaner::Cleaner(std::vector<std::filesystem::path> toClean) : toClean(std::move(toClean)) {
|
||||||
for (const auto &p: this->toClean) {
|
for (const auto &p: this->toClean) { std::filesystem::remove_all(p); }
|
||||||
std::filesystem::remove_all(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cleaner::~Cleaner() {
|
Cleaner::~Cleaner() {
|
||||||
for (const auto &p: toClean) {
|
for (const auto &p: toClean) { std::filesystem::remove_all(p); }
|
||||||
std::filesystem::remove_all(p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user