Merge branch 'develop' into feature/either

This commit is contained in:
Sebastian Messmer 2018-12-24 09:13:31 +01:00
commit 0444b809a2
116 changed files with 1294 additions and 651 deletions

View File

@ -90,12 +90,12 @@ references:
restore_cache:
keys:
# Find the most recent cache from any branch
- v2_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
- v3_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
upgrade_boost_post: &upgrade_boost_post
save_cache:
key: v2_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
key: v3_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
paths:
- /tmp/boost_1_56_0
- /tmp/boost_1_57_0
upgrade_boost: &upgrade_boost
run:
name: Upgrade Boost
@ -104,10 +104,10 @@ references:
export NUMCORES=`nproc`
echo Using $NUMCORES cores
# Download and prepare boost (only if not already present from cache)
if [ ! -d "/tmp/boost_1_56_0" ]; then
if [ ! -d "/tmp/boost_1_57_0" ]; then
echo "Didn't find boost in cache. Downloading and building."
wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.56.0/boost_1_56_0.tar.bz2/download
if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "1ce9871c3a2519682538a5f1331938b948123432d99aea0ce026958cbebd25d84019a3a28c452724b9693df98a8d1264bb2d93d2fee508453f8b42836e67481e" ]; then
wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.bz2/download
if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "61881440fd89644c43c6e3bc6292e9fed75a6d3a76f98654b189d0ed4e1087d77b585884e882270c08bf9f7132b173bfc1fde05848e06aa78ba7f1008d10714d" ]; then
echo Correct sha512sum
else
echo Wrong sha512sum
@ -117,14 +117,14 @@ references:
echo Extracting...
tar -xf /tmp/boost.tar.bz2 -C /tmp
rm -rf boost.tar.bz2
cd /tmp/boost_1_56_0
cd /tmp/boost_1_57_0
./bootstrap.sh --with-toolset=${BUILD_TOOLSET} --with-libraries=filesystem,thread,chrono,program_options
cd ..
else
echo Found boost in cache. Use cache and build.
fi
# Compile and install boost (if cached, this should be fast)
cd /tmp/boost_1_56_0
cd /tmp/boost_1_57_0
sudo ./b2 toolset=${BUILD_TOOLSET} link=static cxxflags=-fPIC -d0 -j$NUMCORES install
build_pre: &build_pre
restore_cache:

16
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,16 @@
## Expected Behavior
## Actual Behavior
## Steps to Reproduce the Problem
1.
2.
3.
## Specifications
- CryFS Version:
- Operating System (incl. Version):

View File

@ -37,9 +37,9 @@ if (MSVC)
option(DOKAN_PATH "Location of the Dokan library, e.g. C:\\Program Files\\Dokan\\DokanLibrary-1.1.0" "")
endif()
# Default value is to build in release mode
# Default value is to build in release mode but with debug symbols
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE INTERNAL "CMAKE_BUILD_TYPE")
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE INTERNAL "CMAKE_BUILD_TYPE")
endif(NOT CMAKE_BUILD_TYPE)
# The MSVC version on AppVeyor CI needs this

View File

@ -26,7 +26,7 @@ Requirements
- GCC version >= 5.0 or Clang >= 4.0
- CMake version >= 3.0
- libcurl4 (including development headers)
- Boost libraries version >= 1.56 (including development headers)
- Boost libraries version >= 1.57 (including development headers)
- filesystem
- system
- chrono

View File

@ -2,3 +2,5 @@ Change homebrew recipe to "brew install libomp" but not "brew install cryptopp"
Change AppVeyor Badge to master branch
Add coverity and PVR Studio?
- Max time for items in cache to reduce conflicts?

View File

@ -29,13 +29,13 @@ init:
- echo %generator%
install:
- choco install -y dokany --installargs ADDLOCAL=DokanDevFeature,DokanLibBFeature,DokanPDBFeature
- choco install -y dokany --version 1.1.0.2000 --installargs INSTALLDEVFILES=1
- cmake --version
build_script:
- cmd: mkdir build
- cmd: cd build
- cmd: cmake .. -G "%generator%" -DBUILD_TESTING=on -DBOOST_ROOT=C:\Libraries\boost_1_65_1 -DDOKAN_PATH="C:\Program Files\Dokan\DokanLibrary-1.1.0"
- cmd: cmake .. -G "%generator%" -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_65_1" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.1.0"
# TODO Make build parallel
- cmd: cmake --build . --config %CONFIGURATION%
- cmd: .\test\gitversion\%CONFIGURATION%\gitversion-test.exe
@ -46,7 +46,8 @@ build_script:
- cmd: .\test\blobstore\%CONFIGURATION%\blobstore-test.exe
- cmd: .\test\cryfs\%CONFIGURATION%\cryfs-test.exe
#- cmd: .\test\cryfs-cli\%CONFIGURATION%\cryfs-cli-test.exe
#- cmd: cpack --verbose -G WIX
- cmd: cpack -C %CONFIGURATION% --verbose -G WIX
on_failure:
- cmd: type C:\projects\cryfs\build\_CPack_Packages\win64\WIX\wix.log

View File

@ -108,7 +108,7 @@ endfunction(target_enable_style_warnings)
function(target_add_boost TARGET)
# Load boost libraries
if(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
# Many supported systems don't have boost >= 1.56. Better link it statically.
# Many supported systems don't have boost >= 1.57. Better link it statically.
message(STATUS "Boost will be statically linked")
set(Boost_USE_STATIC_LIBS ON)
else(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
@ -116,7 +116,7 @@ function(target_add_boost TARGET)
set(Boost_USE_STATIC_LIBS OFF)
endif(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
set(BOOST_THREAD_VERSION 4)
find_package(Boost 1.56.0
find_package(Boost 1.57.0
REQUIRED
COMPONENTS ${ARGN})
target_include_directories(${TARGET} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})

View File

@ -18,15 +18,11 @@ function(append_build_number VERSION_NUMBER OUTPUT)
set(${OUTPUT} "${STRIPPED_VERSION_NUMBER}" PARENT_SCOPE)
endfunction()
# We only allow generating distribution packages if:
# - it is a release build (to ensure all generated debian packages are fine to be distributed)
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILDTYPE)
if(BUILDTYPE MATCHES RELEASE OR BUILDTYPE MATCHES RELWITHDEBINFO)
if("${CMAKE_VERSION}" VERSION_LESS "3.3")
if("${CMAKE_VERSION}" VERSION_LESS "3.3")
# Earlier cmake versions generate .deb packages for which the package manager says they're bad quality
# and asks the user whether they really want to install it. Cmake 3.3 fixes this.
message(WARNING "Distribution package generation is only supported for CMake version >= 3.3. You're using ${CMAKE_VERSION}. You will be able to build and install CryFS, but you won't be able to generate .deb packages.")
else("${CMAKE_VERSION}" VERSION_LESS "3.3")
else()
# Fix debfiles permissions. Unfortunately, git doesn't store file permissions.
# When installing the .deb package and these files have the wrong permissions, the package manager complains.
execute_process(COMMAND /bin/bash -c "chmod 0755 ${CMAKE_CURRENT_SOURCE_DIR}/debfiles/*")
@ -73,5 +69,4 @@ if(BUILDTYPE MATCHES RELEASE OR BUILDTYPE MATCHES RELWITHDEBINFO)
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postinst;${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postrm")
include(CPack)
endif()
endif()

View File

@ -18,7 +18,7 @@ DataInnerNode::DataInnerNode(DataNodeView view)
: DataNode(std::move(view)) {
ASSERT(depth() > 0, "Inner node can't have depth 0. Is this a leaf maybe?");
if (node().FormatVersion() != FORMAT_VERSION_HEADER) {
throw std::runtime_error("This node format is not supported. Was it created with a newer version of CryFS?");
throw std::runtime_error("This node format (" + std::to_string(node().FormatVersion()) + ") is not supported. Was it created with a newer version of CryFS?");
}
}

View File

@ -137,6 +137,10 @@ DataNodeLayout DataNodeStore::layout() const {
return _layout;
}
void DataNodeStore::forEachNode(std::function<void (const BlockId& nodeId)> callback) const {
_blockstore->forEachBlock(std::move(callback));
}
}
}
}

View File

@ -51,6 +51,8 @@ public:
uint64_t estimateSpaceForNumNodesLeft() const;
//TODO Test overwriteNodeWith(), createNodeAsCopyFrom(), removeSubtree()
void forEachNode(std::function<void (const blockstore::BlockId& nodeId)> callback) const;
private:
cpputils::unique_ref<blockstore::BlockStore> _blockstore;

View File

@ -27,7 +27,6 @@ set(SOURCES
implementations/integrity/IntegrityBlockStore2.cpp
implementations/integrity/KnownBlockVersions.cpp
implementations/integrity/ClientIdAndBlockId.cpp
implementations/integrity/IntegrityViolationError.cpp
implementations/mock/MockBlockStore.cpp
implementations/mock/MockBlock.cpp
)

View File

@ -16,6 +16,8 @@ using std::mutex;
namespace blockstore {
namespace caching {
constexpr double CachingBlockStore2::MAX_LIFETIME_SEC;
CachingBlockStore2::CachedBlock::CachedBlock(const CachingBlockStore2* blockStore, const BlockId &blockId, cpputils::Data data, bool isDirty)
: _blockStore(blockStore), _blockId(blockId), _data(std::move(data)), _dirty(isDirty) {
}

View File

@ -54,6 +54,11 @@ private:
mutable std::unordered_set<BlockId> _cachedBlocksNotInBaseStore;
mutable Cache<BlockId, cpputils::unique_ref<CachedBlock>, 1000> _cache;
public:
static constexpr double MAX_LIFETIME_SEC = decltype(_cache)::MAX_LIFETIME_SEC;
private:
DISALLOW_COPY_AND_ASSIGN(CachingBlockStore2);
};

View File

@ -36,10 +36,9 @@ Data IntegrityBlockStore2::_prependHeaderToData(const BlockId& blockId, uint32_t
return result;
}
void IntegrityBlockStore2::_checkHeader(const BlockId &blockId, const Data &data) const {
bool IntegrityBlockStore2::_checkHeader(const BlockId &blockId, const Data &data) const {
_checkFormatHeader(data);
_checkIdHeader(blockId, data);
_checkVersionHeader(blockId, data);
return _checkIdHeader(blockId, data) && _checkVersionHeader(blockId, data);
}
void IntegrityBlockStore2::_checkFormatHeader(const Data &data) const {
@ -48,20 +47,26 @@ void IntegrityBlockStore2::_checkFormatHeader(const Data &data) const {
}
}
void IntegrityBlockStore2::_checkVersionHeader(const BlockId &blockId, const Data &data) const {
bool IntegrityBlockStore2::_checkVersionHeader(const BlockId &blockId, const Data &data) const {
uint32_t clientId = _readClientId(data);
uint64_t version = _readVersion(data);
if(!_knownBlockVersions.checkAndUpdateVersion(clientId, blockId, version)) {
integrityViolationDetected("The block version number is too low. Did an attacker try to roll back the block or to re-introduce a deleted block?");
return false;
}
return true;
}
void IntegrityBlockStore2::_checkIdHeader(const BlockId &expectedBlockId, const Data &data) const {
bool IntegrityBlockStore2::_checkIdHeader(const BlockId &expectedBlockId, const Data &data) const {
BlockId actualBlockId = _readBlockId(data);
if (expectedBlockId != actualBlockId) {
integrityViolationDetected("The block id is wrong. Did an attacker try to rename some blocks?");
return false;
}
return true;
}
uint16_t IntegrityBlockStore2::_readFormatHeader(const Data &data) {
@ -84,44 +89,34 @@ Data IntegrityBlockStore2::_removeHeader(const Data &data) {
return data.copyAndRemovePrefix(HEADER_LENGTH);
}
void IntegrityBlockStore2::_checkNoPastIntegrityViolations() const {
if (_integrityViolationDetected) {
throw std::runtime_error(string() +
"There was an integrity violation detected. Preventing any further access to the file system. " +
"If you want to reset the integrity data (i.e. accept changes made by a potential attacker), " +
"please unmount the file system and delete the following file before re-mounting it: " +
_knownBlockVersions.path().string());
}
}
void IntegrityBlockStore2::integrityViolationDetected(const string &reason) const {
if (_allowIntegrityViolations) {
LOG(WARN, "Integrity violation (but integrity checks are disabled): {}", reason);
return;
}
_integrityViolationDetected = true;
throw IntegrityViolationError(reason);
_knownBlockVersions.setIntegrityViolationOnPreviousRun(true);
_onIntegrityViolation();
}
IntegrityBlockStore2::IntegrityBlockStore2(unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath, myClientId), _allowIntegrityViolations(allowIntegrityViolations), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _integrityViolationDetected(false) {
IntegrityBlockStore2::IntegrityBlockStore2(unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void ()> onIntegrityViolation)
: _baseBlockStore(std::move(baseBlockStore)), _knownBlockVersions(integrityFilePath, myClientId), _allowIntegrityViolations(allowIntegrityViolations), _missingBlockIsIntegrityViolation(missingBlockIsIntegrityViolation), _onIntegrityViolation(std::move(onIntegrityViolation)) {
if (_knownBlockVersions.integrityViolationOnPreviousRun()) {
throw IntegrityViolationOnPreviousRun(_knownBlockVersions.path());
}
}
bool IntegrityBlockStore2::tryCreate(const BlockId &blockId, const Data &data) {
_checkNoPastIntegrityViolations();
uint64_t version = _knownBlockVersions.incrementVersion(blockId);
Data dataWithHeader = _prependHeaderToData(blockId, _knownBlockVersions.myClientId(), version, data);
return _baseBlockStore->tryCreate(blockId, dataWithHeader);
}
bool IntegrityBlockStore2::remove(const BlockId &blockId) {
_checkNoPastIntegrityViolations();
_knownBlockVersions.markBlockAsDeleted(blockId);
return _baseBlockStore->remove(blockId);
}
optional<Data> IntegrityBlockStore2::load(const BlockId &blockId) const {
_checkNoPastIntegrityViolations();
auto loaded = _baseBlockStore->load(blockId);
if (none == loaded) {
if (_missingBlockIsIntegrityViolation && _knownBlockVersions.blockShouldExist(blockId)) {
@ -132,13 +127,17 @@ optional<Data> IntegrityBlockStore2::load(const BlockId &blockId) const {
#ifndef CRYFS_NO_COMPATIBILITY
if (FORMAT_VERSION_HEADER_OLD == _readFormatHeader(*loaded)) {
Data migrated = _migrateBlock(blockId, *loaded);
_checkHeader(blockId, migrated);
if (!_checkHeader(blockId, migrated) && !_allowIntegrityViolations) {
return optional<Data>(none);
}
Data content = _removeHeader(migrated);
const_cast<IntegrityBlockStore2*>(this)->store(blockId, content);
return optional<Data>(_removeHeader(migrated));
}
#endif
_checkHeader(blockId, *loaded);
if (!_checkHeader(blockId, *loaded) && !_allowIntegrityViolations) {
return optional<Data>(none);
}
return optional<Data>(_removeHeader(*loaded));
}
@ -154,7 +153,6 @@ Data IntegrityBlockStore2::_migrateBlock(const BlockId &blockId, const Data &dat
#endif
void IntegrityBlockStore2::store(const BlockId &blockId, const Data &data) {
_checkNoPastIntegrityViolations();
uint64_t version = _knownBlockVersions.incrementVersion(blockId);
Data dataWithHeader = _prependHeaderToData(blockId, _knownBlockVersions.myClientId(), version, data);
return _baseBlockStore->store(blockId, dataWithHeader);

View File

@ -5,18 +5,34 @@
#include "../../interface/BlockStore2.h"
#include <cpp-utils/macros.h>
#include "KnownBlockVersions.h"
#include "IntegrityViolationError.h"
namespace blockstore {
namespace integrity {
// This exception is thrown if the filesystem can't be loaded because an integrity violation happened
// in one of its earlier runs.
// TODO Use block store factory with expected<> result instead of exception throwing.
class IntegrityViolationOnPreviousRun final : public std::exception {
public:
IntegrityViolationOnPreviousRun(boost::filesystem::path stateFile)
: _stateFile(std::move(stateFile)) {}
const boost::filesystem::path& stateFile() const {
return _stateFile;
}
private:
// The state file/directory that has to be deleted so the file system works again
boost::filesystem::path _stateFile;
};
//TODO Format version headers
// This blockstore implements integrity measures.
// It depends on being used on top of an encrypted block store that protects integrity of the block contents (i.e. uses an authenticated cipher).
class IntegrityBlockStore2 final: public BlockStore2 {
public:
IntegrityBlockStore2(cpputils::unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation);
IntegrityBlockStore2(cpputils::unique_ref<BlockStore2> baseBlockStore, const boost::filesystem::path &integrityFilePath, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void ()> onIntegrityViolation);
bool tryCreate(const BlockId &blockId, const cpputils::Data &data) override;
bool remove(const BlockId &blockId) override;
@ -49,10 +65,10 @@ public:
private:
static cpputils::Data _prependHeaderToData(const BlockId &blockId, uint32_t myClientId, uint64_t version, const cpputils::Data &data);
void _checkHeader(const BlockId &blockId, const cpputils::Data &data) const;
WARN_UNUSED_RESULT bool _checkHeader(const BlockId &blockId, const cpputils::Data &data) const;
void _checkFormatHeader(const cpputils::Data &data) const;
void _checkIdHeader(const BlockId &expectedBlockId, const cpputils::Data &data) const;
void _checkVersionHeader(const BlockId &blockId, const cpputils::Data &data) const;
WARN_UNUSED_RESULT bool _checkIdHeader(const BlockId &expectedBlockId, const cpputils::Data &data) const;
WARN_UNUSED_RESULT bool _checkVersionHeader(const BlockId &blockId, const cpputils::Data &data) const;
static uint16_t _readFormatHeader(const cpputils::Data &data);
static uint32_t _readClientId(const cpputils::Data &data);
static BlockId _readBlockId(const cpputils::Data &data);
@ -61,14 +77,13 @@ private:
static cpputils::Data _migrateBlock(const BlockId &blockId, const cpputils::Data &data);
#endif
static cpputils::Data _removeHeader(const cpputils::Data &data);
void _checkNoPastIntegrityViolations() const;
void integrityViolationDetected(const std::string &reason) const;
cpputils::unique_ref<BlockStore2> _baseBlockStore;
mutable KnownBlockVersions _knownBlockVersions;
const bool _allowIntegrityViolations;
const bool _missingBlockIsIntegrityViolation;
mutable bool _integrityViolationDetected;
std::function<void ()> _onIntegrityViolation;
DISALLOW_COPY_AND_ASSIGN(IntegrityBlockStore2);
};

View File

@ -1 +0,0 @@
#include "IntegrityViolationError.h"

View File

@ -1,27 +0,0 @@
#pragma once
#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INTEGRITY_INTEGRITYVIOLATIONERROR_H_
#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_INTEGRITY_INTEGRITYVIOLATIONERROR_H_
#include <cpp-utils/macros.h>
#include <string>
#include <stdexcept>
namespace blockstore {
namespace integrity {
class IntegrityViolationError final : public std::runtime_error {
private:
// Constructor is private to make sure that only IntegrityBlockStore can throw this exception.
// This is because IntegrityBlockStore wants to know about integrity violations and
// block all further file system access if it happens.
IntegrityViolationError(const std::string &reason)
: std::runtime_error("Integrity violation: " + reason) {
}
friend class IntegrityBlockStore2;
};
}
}
#endif

View File

@ -17,20 +17,22 @@ using cpputils::Deserializer;
namespace blockstore {
namespace integrity {
const string KnownBlockVersions::HEADER = "cryfs.integritydata.knownblockversions;0";
const string KnownBlockVersions::OLD_HEADER = "cryfs.integritydata.knownblockversions;0";
const string KnownBlockVersions::HEADER = "cryfs.integritydata.knownblockversions;1";
constexpr uint32_t KnownBlockVersions::CLIENT_ID_FOR_DELETED_BLOCK;
KnownBlockVersions::KnownBlockVersions(const bf::path &stateFilePath, uint32_t myClientId)
:_knownVersions(), _lastUpdateClientId(), _stateFilePath(stateFilePath), _myClientId(myClientId), _mutex(), _valid(true) {
:_integrityViolationOnPreviousRun(false), _knownVersions(), _lastUpdateClientId(), _stateFilePath(stateFilePath), _myClientId(myClientId), _mutex(), _valid(true) {
unique_lock<mutex> lock(_mutex);
ASSERT(_myClientId != CLIENT_ID_FOR_DELETED_BLOCK, "This is not a valid client id");
_loadStateFile();
}
KnownBlockVersions::KnownBlockVersions(KnownBlockVersions &&rhs) // NOLINT (intentionally not noexcept)
: _knownVersions(), _lastUpdateClientId(), _stateFilePath(), _myClientId(0), _mutex(), _valid(true) {
: _integrityViolationOnPreviousRun(false), _knownVersions(), _lastUpdateClientId(), _stateFilePath(), _myClientId(0), _mutex(), _valid(true) {
unique_lock<mutex> rhsLock(rhs._mutex);
unique_lock<mutex> lock(_mutex);
_integrityViolationOnPreviousRun = rhs._integrityViolationOnPreviousRun;
_knownVersions = std::move(rhs._knownVersions);
_lastUpdateClientId = std::move(rhs._lastUpdateClientId);
_stateFilePath = std::move(rhs._stateFilePath);
@ -45,6 +47,14 @@ KnownBlockVersions::~KnownBlockVersions() {
}
}
void KnownBlockVersions::setIntegrityViolationOnPreviousRun(bool value) {
_integrityViolationOnPreviousRun = value;
}
bool KnownBlockVersions::integrityViolationOnPreviousRun() const {
return _integrityViolationOnPreviousRun;
}
bool KnownBlockVersions::checkAndUpdateVersion(uint32_t clientId, const BlockId &blockId, uint64_t version) {
unique_lock<mutex> lock(_mutex);
ASSERT(clientId != CLIENT_ID_FOR_DELETED_BLOCK, "This is not a valid client id");
@ -89,13 +99,25 @@ void KnownBlockVersions::_loadStateFile() {
// File doesn't exist means we loaded empty state.
return;
}
Deserializer deserializer(&*file);
if (HEADER != deserializer.readString()) {
const string loaded_header = deserializer.readString();
#ifndef CRYFS_NO_COMPATIBILITY
if (OLD_HEADER == loaded_header) {
_knownVersions = _deserializeKnownVersions(&deserializer);
_lastUpdateClientId = _deserializeLastUpdateClientIds(&deserializer);
deserializer.finished();
_saveStateFile();
return;
}
#endif
if (HEADER != loaded_header) {
throw std::runtime_error("Invalid local state: Invalid integrity file header.");
}
_deserializeKnownVersions(&deserializer);
_deserializeLastUpdateClientIds(&deserializer);
_integrityViolationOnPreviousRun = deserializer.readBool();
_knownVersions = _deserializeKnownVersions(&deserializer);
_lastUpdateClientId = _deserializeLastUpdateClientIds(&deserializer);
deserializer.finished();
};
@ -104,30 +126,34 @@ void KnownBlockVersions::_loadStateFile() {
void KnownBlockVersions::_saveStateFile() const {
Serializer serializer(
Serializer::StringSize(HEADER) +
Serializer::BoolSize() +
sizeof(uint64_t) + _knownVersions.size() * (sizeof(uint32_t) + BlockId::BINARY_LENGTH + sizeof(uint64_t)) +
sizeof(uint64_t) + _lastUpdateClientId.size() * (BlockId::BINARY_LENGTH + sizeof(uint32_t)));
serializer.writeString(HEADER);
_serializeKnownVersions(&serializer);
_serializeLastUpdateClientIds(&serializer);
serializer.writeBool(_integrityViolationOnPreviousRun);
_serializeKnownVersions(&serializer, _knownVersions);
_serializeLastUpdateClientIds(&serializer, _lastUpdateClientId);
serializer.finished().StoreToFile(_stateFilePath);
}
void KnownBlockVersions::_deserializeKnownVersions(Deserializer *deserializer) {
std::unordered_map<ClientIdAndBlockId, uint64_t> KnownBlockVersions::_deserializeKnownVersions(Deserializer *deserializer) {
uint64_t numEntries = deserializer->readUint64();
_knownVersions.clear();
_knownVersions.reserve(static_cast<uint64_t>(1.2 * numEntries)); // Reserve for factor 1.2 more, so the file system doesn't immediately have to resize it on the first new block.
std::unordered_map<ClientIdAndBlockId, uint64_t> result;
result.reserve(static_cast<uint64_t>(1.2 * numEntries)); // Reserve for factor 1.2 more, so the file system doesn't immediately have to resize it on the first new block.
for (uint64_t i = 0 ; i < numEntries; ++i) {
auto entry = _deserializeKnownVersionsEntry(deserializer);
_knownVersions.insert(entry);
result.insert(entry);
}
return result;
}
void KnownBlockVersions::_serializeKnownVersions(Serializer *serializer) const {
uint64_t numEntries = _knownVersions.size();
void KnownBlockVersions::_serializeKnownVersions(Serializer *serializer, const std::unordered_map<ClientIdAndBlockId, uint64_t>& knownVersions) {
uint64_t numEntries = knownVersions.size();
serializer->writeUint64(numEntries);
for (const auto &entry : _knownVersions) {
for (const auto &entry : knownVersions) {
_serializeKnownVersionsEntry(serializer, entry);
}
}
@ -146,21 +172,22 @@ void KnownBlockVersions::_serializeKnownVersionsEntry(Serializer *serializer, co
serializer->writeUint64(entry.second);
}
void KnownBlockVersions::_deserializeLastUpdateClientIds(Deserializer *deserializer) {
std::unordered_map<BlockId, uint32_t> KnownBlockVersions::_deserializeLastUpdateClientIds(Deserializer *deserializer) {
uint64_t numEntries = deserializer->readUint64();
_lastUpdateClientId.clear();
_lastUpdateClientId.reserve(static_cast<uint64_t>(1.2 * numEntries)); // Reserve for factor 1.2 more, so the file system doesn't immediately have to resize it on the first new block.
std::unordered_map<BlockId, uint32_t> result;
result.reserve(static_cast<uint64_t>(1.2 * numEntries)); // Reserve for factor 1.2 more, so the file system doesn't immediately have to resize it on the first new block.
for (uint64_t i = 0 ; i < numEntries; ++i) {
auto entry = _deserializeLastUpdateClientIdEntry(deserializer);
_lastUpdateClientId.insert(entry);
result.insert(entry);
}
return result;
}
void KnownBlockVersions::_serializeLastUpdateClientIds(Serializer *serializer) const {
uint64_t numEntries = _lastUpdateClientId.size();
void KnownBlockVersions::_serializeLastUpdateClientIds(Serializer *serializer, const std::unordered_map<BlockId, uint32_t>& lastUpdateClientId) {
uint64_t numEntries = lastUpdateClientId.size();
serializer->writeUint64(numEntries);
for (const auto &entry : _lastUpdateClientId) {
for (const auto &entry : lastUpdateClientId) {
_serializeLastUpdateClientIdEntry(serializer, entry);
}
}

View File

@ -36,9 +36,13 @@ namespace blockstore {
uint32_t myClientId() const;
const boost::filesystem::path &path() const;
bool integrityViolationOnPreviousRun() const;
void setIntegrityViolationOnPreviousRun(bool value);
static constexpr uint32_t CLIENT_ID_FOR_DELETED_BLOCK = 0;
private:
bool _integrityViolationOnPreviousRun;
std::unordered_map<ClientIdAndBlockId, uint64_t> _knownVersions;
std::unordered_map<BlockId, uint32_t> _lastUpdateClientId; // The client who last updated the block
@ -47,19 +51,20 @@ namespace blockstore {
mutable std::mutex _mutex;
bool _valid;
static const std::string OLD_HEADER;
static const std::string HEADER;
void _loadStateFile();
void _saveStateFile() const;
void _deserializeKnownVersions(cpputils::Deserializer *deserializer);
void _serializeKnownVersions(cpputils::Serializer *serializer) const;
static std::unordered_map<ClientIdAndBlockId, uint64_t> _deserializeKnownVersions(cpputils::Deserializer *deserializer);
static void _serializeKnownVersions(cpputils::Serializer *serializer, const std::unordered_map<ClientIdAndBlockId, uint64_t>& knownVersions);
static std::pair<ClientIdAndBlockId, uint64_t> _deserializeKnownVersionsEntry(cpputils::Deserializer *deserializer);
static void _serializeKnownVersionsEntry(cpputils::Serializer *serializer, const std::pair<ClientIdAndBlockId, uint64_t> &entry);
void _deserializeLastUpdateClientIds(cpputils::Deserializer *deserializer);
void _serializeLastUpdateClientIds(cpputils::Serializer *serializer) const;
static std::unordered_map<BlockId, uint32_t> _deserializeLastUpdateClientIds(cpputils::Deserializer *deserializer);
static void _serializeLastUpdateClientIds(cpputils::Serializer *serializer, const std::unordered_map<BlockId, uint32_t>& lastUpdateClientIds);
static std::pair<BlockId, uint32_t> _deserializeLastUpdateClientIdEntry(cpputils::Deserializer *deserializer);
static void _serializeLastUpdateClientIdEntry(cpputils::Serializer *serializer, const std::pair<BlockId, uint32_t> &entry);

View File

@ -13,6 +13,7 @@ namespace cpputils {
public:
Deserializer(const Data *source);
bool readBool();
uint8_t readUint8();
int8_t readInt8();
uint16_t readUint16();
@ -42,6 +43,17 @@ namespace cpputils {
inline Deserializer::Deserializer(const Data *source): _pos(0), _source(source) {
}
inline bool Deserializer::readBool() {
uint8_t read = readUint8();
if (read == 1) {
return true;
} else if (read == 0) {
return false;
} else {
throw std::runtime_error("Read invalid bool value");
}
}
inline uint8_t Deserializer::readUint8() {
return _read<uint8_t>();
}

View File

@ -16,6 +16,7 @@ namespace cpputils {
public:
Serializer(size_t size);
void writeBool(bool value);
void writeUint8(uint8_t value);
void writeInt8(int8_t value);
void writeUint16(uint16_t value);
@ -32,6 +33,7 @@ namespace cpputils {
// It does not store a data size but limits the size by the size of the serialization result
void writeTailData(const Data &value);
static size_t BoolSize();
static size_t DataSize(const Data &value);
static size_t StringSize(const std::string &value);
@ -50,6 +52,14 @@ namespace cpputils {
inline Serializer::Serializer(size_t size): _pos(0), _result(size) {
}
inline size_t Serializer::BoolSize() {
return sizeof(uint8_t);
}
inline void Serializer::writeBool(bool value) {
writeUint8(value ? 1 : 0);
}
inline void Serializer::writeUint8(uint8_t value) {
_write<uint8_t>(value);
}
@ -118,6 +128,7 @@ namespace cpputils {
_pos += count;
}
inline void Serializer::writeString(const std::string &value) {
_writeData(value.c_str(), value.size() + 1); // +1 for the nullbyte
}

View File

@ -8,6 +8,7 @@
#include <codecvt>
#include <Windows.h>
#include <Winhttp.h>
#include <VersionHelpers.h>
using boost::none;
using boost::optional;
@ -212,7 +213,8 @@ namespace cpputils {
namespace {
cpputils::unique_ref<WinHttpSession> create_session() {
HttpHandleRAII session_handle = WinHttpOpen(L"cpputils::HttpClient", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
const DWORD dwAccessType = IsWindows8Point1OrGreater() ? WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
HttpHandleRAII session_handle = WinHttpOpen(L"cpputils::HttpClient", dwAccessType, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if(nullptr == session_handle.handle) {
throw std::runtime_error("Error calling WinHttpOpen. Error code: " + std::to_string(GetLastError()));
}

View File

@ -27,6 +27,6 @@ target_enable_style_warnings(${PROJECT_NAME}_bin)
target_activate_cpp14(${PROJECT_NAME}_bin)
install(TARGETS ${PROJECT_NAME}_bin
CONFIGURATIONS Debug Release RelWithDebInfo
DESTINATION ${CMAKE_INSTALL_BINDIR}
CONFIGURATIONS Release RelWithDebInfo
)

View File

@ -51,8 +51,9 @@ using std::string;
using std::endl;
using std::shared_ptr;
using std::make_shared;
using std::unique_ptr;
using std::make_unique;
using std::function;
using std::make_shared;
using boost::optional;
using boost::none;
using boost::chrono::minutes;
@ -67,8 +68,8 @@ using gitversion::VersionCompare;
namespace cryfs {
Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings& scryptSettings, shared_ptr<Console> console):
_keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(), _noninteractive(false) {
Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, shared_ptr<Console> console):
_keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(), _noninteractive(false), _idleUnmounter(none), _device(none) {
_noninteractive = Environment::isNoninteractive();
if (_noninteractive) {
_console = make_shared<NoninteractiveConsole>(console);
@ -219,33 +220,59 @@ namespace cryfs {
cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath), allowFilesystemUpgrade, allowReplacedFilesystem);
}
void Cli::_runFilesystem(const ProgramOptions &options) {
void Cli::_runFilesystem(const ProgramOptions &options, std::function<void()> onMounted) {
try {
LocalStateDir localStateDir(Environment::localStateDir());
auto blockStore = make_unique_ref<OnDiskBlockStore2>(options.baseDir());
auto config = _loadOrCreateConfig(options, localStateDir);
CryDevice device(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId,
options.allowIntegrityViolations(), config.configFile.config()->missingBlockIsIntegrityViolation());
_sanityCheckFilesystem(&device);
fspp::FilesystemImpl fsimpl(&device);
fspp::fuse::Fuse fuse(&fsimpl, "cryfs", "cryfs@" + options.baseDir().string());
unique_ptr<fspp::fuse::Fuse> fuse = nullptr;
bool stoppedBecauseOfIntegrityViolation = false;
_initLogfile(options);
auto onIntegrityViolation = [&fuse, &stoppedBecauseOfIntegrityViolation] () {
if (fuse.get() != nullptr) {
LOG(ERR, "Integrity violation detected. Unmounting.");
stoppedBecauseOfIntegrityViolation = true;
fuse->stop();
} else {
// Usually on an integrity violation, the file system is unmounted.
// Here, the file system isn't initialized yet, i.e. we failed in the initial steps when
// setting up _device before running initFilesystem.
// We can't unmount a not-mounted file system, but we can make sure it doesn't get mounted.
throw CryfsException("Integrity violation detected. Unmounting.", ErrorCode::IntegrityViolation);
}
};
const bool missingBlockIsIntegrityViolation = config.configFile.config()->missingBlockIsIntegrityViolation();
_device = optional<unique_ref<CryDevice>>(make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId,
options.allowIntegrityViolations(), missingBlockIsIntegrityViolation, std::move(onIntegrityViolation)));
_sanityCheckFilesystem(_device->get());
auto initFilesystem = [&] (fspp::fuse::Fuse *fs){
ASSERT(_device != none, "File system not ready to be initialized. Was it already initialized before?");
//TODO Test auto unmounting after idle timeout
//TODO This can fail due to a race condition if the filesystem isn't started yet (e.g. passing --unmount-idle 0").
auto idleUnmounter = _createIdleCallback(options.unmountAfterIdleMinutes(), [&fuse] { fuse.stop(); });
if (idleUnmounter != none) {
device.onFsAction(std::bind(&CallAfterTimeout::resetTimer, idleUnmounter->get()));
_idleUnmounter = _createIdleCallback(options.unmountAfterIdleMinutes(), [fs] {fs->stop();});
if (_idleUnmounter != none) {
(*_device)->onFsAction(std::bind(&CallAfterTimeout::resetTimer, _idleUnmounter->get()));
}
return make_shared<fspp::FilesystemImpl>(std::move(*_device));
};
fuse = make_unique<fspp::fuse::Fuse>(initFilesystem, std::move(onMounted), "cryfs", "cryfs@" + options.baseDir().string());
_initLogfile(options);
#ifdef __APPLE__
std::cout << "\nMounting filesystem. To unmount, call:\n$ umount " << options.mountDir() << "\n" << std::endl;
#else
std::cout << "\nMounting filesystem. To unmount, call:\n$ fusermount -u " << options.mountDir() << "\n"
<< std::endl;
#endif
fuse.run(options.mountDir(), options.fuseOptions());
fuse->run(options.mountDir(), options.fuseOptions());
if (stoppedBecauseOfIntegrityViolation) {
throw CryfsException("Integrity violation detected. Unmounting.", ErrorCode::IntegrityViolation);
}
} catch (const CryfsException &e) {
throw; // CryfsException is only thrown if setup goes wrong. Throw it through so that we get the correct process exit code.
} catch (const std::exception &e) {
@ -363,17 +390,17 @@ namespace cryfs {
return false;
}
int Cli::main(int argc, const char *argv[], unique_ref<HttpClient> httpClient) {
int Cli::main(int argc, const char *argv[], unique_ref<HttpClient> httpClient, std::function<void()> onMounted) {
cpputils::showBacktraceOnCrash();
try {
_showVersion(std::move(httpClient));
ProgramOptions options = program_options::Parser(argc, argv).parse(CryCiphers::supportedCipherNames());
_sanityChecks(options);
_runFilesystem(options);
_runFilesystem(options, std::move(onMounted));
} catch (const CryfsException &e) {
if (e.errorCode() != ErrorCode::Success) {
std::cerr << "Error: " << e.what() << std::endl;
if (e.what() != string()) {
std::cerr << "Error " << static_cast<int>(e.errorCode()) << ": " << e.what() << std::endl;
}
return exitCode(e.errorCode());
} catch (const std::runtime_error &e) {

View File

@ -18,11 +18,11 @@ namespace cryfs {
class Cli final {
public:
Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings& scryptSettings, std::shared_ptr<cpputils::Console> console);
int main(int argc, const char *argv[], cpputils::unique_ref<cpputils::HttpClient> httpClient);
int main(int argc, const char *argv[], cpputils::unique_ref<cpputils::HttpClient> httpClient, std::function<void()> onMounted);
private:
void _checkForUpdates(cpputils::unique_ref<cpputils::HttpClient> httpClient);
void _runFilesystem(const program_options::ProgramOptions &options);
void _runFilesystem(const program_options::ProgramOptions &options, std::function<void()> onMounted);
CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options, const LocalStateDir& localStateDir);
void _checkConfigIntegrity(const boost::filesystem::path& basedir, const LocalStateDir& localStateDir, const CryConfigFile& config, bool allowReplacedFilesystem);
boost::optional<CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(boost::filesystem::path configFilePath, LocalStateDir localStateDir, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const boost::optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem);
@ -48,6 +48,8 @@ namespace cryfs {
cpputils::SCryptSettings _scryptSettings;
std::shared_ptr<cpputils::Console> _console;
bool _noninteractive;
boost::optional<cpputils::unique_ref<CallAfterTimeout>> _idleUnmounter;
boost::optional<cpputils::unique_ref<CryDevice>> _device;
DISALLOW_COPY_AND_ASSIGN(Cli);
};

View File

@ -5,6 +5,7 @@
#if defined(_MSC_VER)
#include <cpp-utils/network/WinHttpClient.h>
#include <VersionHelpers.h>
#else
#include <cpp-utils/network/CurlHttpClient.h>
#endif
@ -18,6 +19,13 @@ using std::make_shared;
using std::cerr;
int main(int argc, const char *argv[]) {
#if defined(_MSC_VER)
if (!IsWindows7SP1OrGreater()) {
std::cerr << "CryFS is currently only supported on Windows 7 SP1 (or later)." << std::endl;
exit(1);
}
#endif
try {
auto &keyGenerator = Random::OSRandom();
#if defined(_MSC_VER)
@ -26,9 +34,9 @@ int main(int argc, const char *argv[]) {
auto httpClient = make_unique_ref<cpputils::CurlHttpClient>();
#endif
return Cli(keyGenerator, SCrypt::DefaultSettings, make_shared<IOStreamConsole>())
.main(argc, argv, std::move(httpClient));
.main(argc, argv, std::move(httpClient), []{});
} catch (const CryfsException &e) {
if (e.errorCode() != ErrorCode::Success) {
if (e.what() != string()) {
std::cerr << "Error: " << e.what() << std::endl;
}
return exitCode(e.errorCode());

View File

@ -18,6 +18,7 @@ set(LIB_SOURCES
config/CryConfigCreator.cpp
config/CryKeyProvider.cpp
config/CryPasswordBasedKeyProvider.cpp
config/CryPresetPasswordBasedKeyProvider.cpp
filesystem/CryOpenFile.cpp
filesystem/fsblobstore/utils/DirEntry.cpp
filesystem/fsblobstore/utils/DirEntryList.cpp

View File

@ -51,6 +51,12 @@ enum class ErrorCode : int {
// File system is in single-client mode and can only be used from the client that created it.
SingleClientFileSystem = 23,
// A previous run of the file system detected an integrity violation. Preventing access to make sure the user notices. The file system will be accessible again after the user deletes the integrity state file.
IntegrityViolationOnPreviousRun = 24,
// An integrity violation was detected and the file system unmounted to make sure the user notices.
IntegrityViolation = 25
};
inline int exitCode(ErrorCode code) {

View File

@ -102,6 +102,10 @@ void CryConfigLoader::_checkMissingBlocksAreIntegrityViolations(CryConfigFile *c
}
}
optional<CryConfigLoader::ConfigLoadResult> CryConfigLoader::load(bf::path filename, bool allowFilesystemUpgrade, bool allowReplacedFilesystem) {
return _loadConfig(std::move(filename), allowFilesystemUpgrade, allowReplacedFilesystem);
}
optional<CryConfigLoader::ConfigLoadResult> CryConfigLoader::loadOrCreate(bf::path filename, bool allowFilesystemUpgrade, bool allowReplacedFilesystem) {
if (bf::exists(filename)) {
return _loadConfig(std::move(filename), allowFilesystemUpgrade, allowReplacedFilesystem);

View File

@ -23,6 +23,7 @@ public:
};
boost::optional<ConfigLoadResult> loadOrCreate(boost::filesystem::path filename, bool allowFilesystemUpgrade, bool allowReplacedFilesystem);
boost::optional<ConfigLoadResult> load(boost::filesystem::path filename, bool allowFilesystemUpgrade, bool allowReplacedFilesystem);
private:
boost::optional<ConfigLoadResult> _loadConfig(boost::filesystem::path filename, bool allowFilesystemUpgrade, bool allowReplacedFilesystem);

View File

@ -4,14 +4,14 @@
#include "CryKeyProvider.h"
#include <functional>
#include <cpp-utils/crypto/kdf/Scrypt.h>
#include <cpp-utils/crypto/kdf/PasswordBasedKDF.h>
#include <cpp-utils/io/Console.h>
namespace cryfs {
// TODO Remove duplication with CryPresetPasswordBasedKeyProvider
class CryPasswordBasedKeyProvider final : public CryKeyProvider {
public:
// TODO Pass in KDF as dependency (needs changes in the KDF interface because of the static functions ::forNewKey and ::forExistingKey)
explicit CryPasswordBasedKeyProvider(std::shared_ptr<cpputils::Console> console, std::function<std::string()> askPasswordForExistingFilesystem, std::function<std::string()> askPasswordForNewFilesystem, cpputils::unique_ref<cpputils::PasswordBasedKDF> kdf);
cpputils::EncryptionKey requestKeyForExistingFilesystem(size_t keySize, const cpputils::Data& kdfParameters) override;

View File

@ -0,0 +1,23 @@
#include "CryPresetPasswordBasedKeyProvider.h"
using cpputils::unique_ref;
using cpputils::EncryptionKey;
using cpputils::unique_ref;
using cpputils::PasswordBasedKDF;
using cpputils::Data;
namespace cryfs {
CryPresetPasswordBasedKeyProvider::CryPresetPasswordBasedKeyProvider(std::string password, unique_ref<PasswordBasedKDF> kdf)
: _password(std::move(password)), _kdf(std::move(kdf)) {}
EncryptionKey CryPresetPasswordBasedKeyProvider::requestKeyForExistingFilesystem(size_t keySize, const Data& kdfParameters) {
return _kdf->deriveExistingKey(keySize, _password, kdfParameters);
}
CryPresetPasswordBasedKeyProvider::KeyResult CryPresetPasswordBasedKeyProvider::requestKeyForNewFilesystem(size_t keySize) {
auto keyResult = _kdf->deriveNewKey(keySize, _password);
return {std::move(keyResult.key), std::move(keyResult.kdfParameters)};
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#ifndef CRYFS_CRYPRESETPASSWORDFROMCONSOLEKEYPROVIDER_H
#define CRYFS_CRYPRESETPASSWORDFROMCONSOLEKEYPROVIDER_H
#include "CryKeyProvider.h"
#include <functional>
#include <cpp-utils/crypto/kdf/PasswordBasedKDF.h>
namespace cryfs {
class CryPresetPasswordBasedKeyProvider final : public CryKeyProvider {
public:
explicit CryPresetPasswordBasedKeyProvider(std::string password, cpputils::unique_ref<cpputils::PasswordBasedKDF> kdf);
cpputils::EncryptionKey requestKeyForExistingFilesystem(size_t keySize, const cpputils::Data& kdfParameters) override;
KeyResult requestKeyForNewFilesystem(size_t keySize) override;
private:
std::string _password;
cpputils::unique_ref<cpputils::PasswordBasedKDF> _kdf;
DISALLOW_COPY_AND_ASSIGN(CryPresetPasswordBasedKeyProvider);
};
}
#endif

View File

@ -20,6 +20,7 @@
#include <gitversion/VersionCompare.h>
#include <blockstore/interface/BlockStore2.h>
#include "cryfs/localstate/LocalStateDir.h"
#include <cryfs/CryfsException.h>
using std::string;
@ -51,14 +52,14 @@ namespace bf = boost::filesystem;
namespace cryfs {
CryDevice::CryDevice(CryConfigFile configFile, unique_ref<BlockStore2> blockStore, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation)
: _fsBlobStore(CreateFsBlobStore(std::move(blockStore), &configFile, localStateDir, myClientId, allowIntegrityViolations, missingBlockIsIntegrityViolation)),
CryDevice::CryDevice(CryConfigFile configFile, unique_ref<BlockStore2> blockStore, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void()> onIntegrityViolation)
: _fsBlobStore(CreateFsBlobStore(std::move(blockStore), &configFile, localStateDir, myClientId, allowIntegrityViolations, missingBlockIsIntegrityViolation, std::move(onIntegrityViolation))),
_rootBlobId(GetOrCreateRootBlobId(&configFile)),
_onFsAction() {
}
unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CryDevice::CreateFsBlobStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation) {
auto blobStore = CreateBlobStore(std::move(blockStore), localStateDir, configFile, myClientId, allowIntegrityViolations, missingBlockIsIntegrityViolation);
unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CryDevice::CreateFsBlobStore(unique_ref<BlockStore2> blockStore, CryConfigFile *configFile, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void()> onIntegrityViolation) {
auto blobStore = CreateBlobStore(std::move(blockStore), localStateDir, configFile, myClientId, allowIntegrityViolations, missingBlockIsIntegrityViolation, std::move(onIntegrityViolation));
#ifndef CRYFS_NO_COMPATIBILITY
auto fsBlobStore = MigrateOrCreateFsBlobStore(std::move(blobStore), configFile);
@ -83,8 +84,8 @@ unique_ref<fsblobstore::FsBlobStore> CryDevice::MigrateOrCreateFsBlobStore(uniqu
}
#endif
unique_ref<blobstore::BlobStore> CryDevice::CreateBlobStore(unique_ref<BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation) {
auto integrityEncryptedBlockStore = CreateIntegrityEncryptedBlockStore(std::move(blockStore), localStateDir, configFile, myClientId, allowIntegrityViolations, missingBlockIsIntegrityViolation);
unique_ref<blobstore::BlobStore> CryDevice::CreateBlobStore(unique_ref<BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void()> onIntegrityViolation) {
auto integrityEncryptedBlockStore = CreateIntegrityEncryptedBlockStore(std::move(blockStore), localStateDir, configFile, myClientId, allowIntegrityViolations, missingBlockIsIntegrityViolation, std::move(onIntegrityViolation));
// Create integrityEncryptedBlockStore not in the same line as BlobStoreOnBlocks, because it can modify BlocksizeBytes
// in the configFile and therefore has to be run before the second parameter to the BlobStoreOnBlocks parameter is evaluated.
return make_unique_ref<BlobStoreOnBlocks>(
@ -96,7 +97,7 @@ unique_ref<blobstore::BlobStore> CryDevice::CreateBlobStore(unique_ref<BlockStor
configFile->config()->BlocksizeBytes());
}
unique_ref<BlockStore2> CryDevice::CreateIntegrityEncryptedBlockStore(unique_ref<BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation) {
unique_ref<BlockStore2> CryDevice::CreateIntegrityEncryptedBlockStore(unique_ref<BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void()> onIntegrityViolation) {
auto encryptedBlockStore = CreateEncryptedBlockStore(*configFile->config(), std::move(blockStore));
auto statePath = localStateDir.forFilesystemId(configFile->config()->FilesystemId());
auto integrityFilePath = statePath / "integritydata";
@ -110,7 +111,19 @@ unique_ref<BlockStore2> CryDevice::CreateIntegrityEncryptedBlockStore(unique_ref
}
#endif
return make_unique_ref<IntegrityBlockStore2>(std::move(encryptedBlockStore), integrityFilePath, myClientId, allowIntegrityViolations, missingBlockIsIntegrityViolation);
try {
return make_unique_ref<IntegrityBlockStore2>(std::move(encryptedBlockStore), integrityFilePath, myClientId,
allowIntegrityViolations, missingBlockIsIntegrityViolation,
std::move(onIntegrityViolation));
} catch (const blockstore::integrity::IntegrityViolationOnPreviousRun& e) {
throw CryfsException(string() +
"There was an integrity violation detected. Preventing any further access to the file system. " +
"This can either happen if an attacker changed your files or rolled back the file system to a previous state, " +
"but it can also happen if you rolled back the file system yourself, for example restored a backup. " +
"If you want to reset the integrity data (i.e. accept changes made by a potential attacker), " +
"please delete the following file before re-mounting it: " +
e.stateFile().string(), ErrorCode::IntegrityViolationOnPreviousRun);
}
}
BlockId CryDevice::CreateRootBlobAndReturnId() {

View File

@ -19,7 +19,7 @@ namespace cryfs {
class CryDevice final: public fspp::Device {
public:
CryDevice(CryConfigFile config, cpputils::unique_ref<blockstore::BlockStore2> blockStore, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation);
CryDevice(CryConfigFile config, cpputils::unique_ref<blockstore::BlockStore2> blockStore, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void ()> onIntegrityViolation);
statvfs statfs() override;
@ -54,12 +54,12 @@ private:
blockstore::BlockId GetOrCreateRootBlobId(CryConfigFile *config);
blockstore::BlockId CreateRootBlobAndReturnId();
static cpputils::unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CreateFsBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation);
static cpputils::unique_ref<parallelaccessfsblobstore::ParallelAccessFsBlobStore> CreateFsBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, CryConfigFile *configFile, const LocalStateDir& localStateDir, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void()> onIntegrityViolation);
#ifndef CRYFS_NO_COMPATIBILITY
static cpputils::unique_ref<fsblobstore::FsBlobStore> MigrateOrCreateFsBlobStore(cpputils::unique_ref<blobstore::BlobStore> blobStore, CryConfigFile *configFile);
#endif
static cpputils::unique_ref<blobstore::BlobStore> CreateBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation);
static cpputils::unique_ref<blockstore::BlockStore2> CreateIntegrityEncryptedBlockStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation);
static cpputils::unique_ref<blobstore::BlobStore> CreateBlobStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void()> onIntegrityViolation);
static cpputils::unique_ref<blockstore::BlockStore2> CreateIntegrityEncryptedBlockStore(cpputils::unique_ref<blockstore::BlockStore2> blockStore, const LocalStateDir& localStateDir, CryConfigFile *configFile, uint32_t myClientId, bool allowIntegrityViolations, bool missingBlockIsIntegrityViolation, std::function<void()> onIntegrityViolation);
static cpputils::unique_ref<blockstore::BlockStore2> CreateEncryptedBlockStore(const CryConfig &config, cpputils::unique_ref<blockstore::BlockStore2> baseBlockStore);
struct BlobWithParent {

View File

@ -15,6 +15,8 @@ using cryfs::fsblobstore::SymlinkBlob;
namespace cryfs {
namespace cachingfsblobstore {
constexpr double CachingFsBlobStore::MAX_LIFETIME_SEC;
optional<unique_ref<FsBlobRef>> CachingFsBlobStore::load(const BlockId &blockId) {
auto fromCache = _cache.pop(blockId);
if (fromCache != none) {

View File

@ -40,6 +40,11 @@ namespace cryfs {
//TODO Use other cache config (i.e. smaller max number of entries) here than in blockstore
blockstore::caching::Cache<blockstore::BlockId, cpputils::unique_ref<fsblobstore::FsBlob>, 50> _cache;
public:
static constexpr double MAX_LIFETIME_SEC = decltype(_cache)::MAX_LIFETIME_SEC;
private:
DISALLOW_COPY_AND_ASSIGN(CachingFsBlobStore);
};

View File

@ -21,18 +21,18 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
if ("${TARGET_ARCH}" STREQUAL "x86_64")
set(DOKAN_LIB_PATH "${DOKAN_PATH}")
elseif("${TARGET_ARCH}" STREQUAL "i386")
set(DOKAN_LIB_PATH "${DOKAN_PATH}\\x86")
set(DOKAN_LIB_PATH "${DOKAN_PATH}/x86")
else()
message(FATAL_ERROR "Unsupported architecture: ${TARGET_ARCH}")
endif()
target_include_directories(${PROJECT_NAME} PUBLIC "${DOKAN_PATH}\\include")
target_link_libraries(${PROJECT_NAME} PUBLIC "${DOKAN_LIB_PATH}\\lib\\dokan1.lib")
#target_link_libraries(${PROJECT_NAME} PUBLIC "${DOKAN_LIB_PATH}\\lib\\dokannp1.lib")
target_link_libraries(${PROJECT_NAME} PUBLIC "${DOKAN_LIB_PATH}\\lib\\dokanfuse1.lib")
target_include_directories(${PROJECT_NAME} PUBLIC "${DOKAN_PATH}/include")
target_link_libraries(${PROJECT_NAME} PUBLIC "${DOKAN_LIB_PATH}/lib/dokan1.lib")
#target_link_libraries(${PROJECT_NAME} PUBLIC "${DOKAN_LIB_PATH}/lib/dokannp1.lib")
target_link_libraries(${PROJECT_NAME} PUBLIC "${DOKAN_LIB_PATH}/lib/dokanfuse1.lib")
install(FILES ${DOKAN_LIB_PATH}\\dokan1.dll ${DOKAN_LIB_PATH}\\dokanfuse1.dll
DESTINATION ${CMAKE_INSTALL_BINDIR}
install(FILES "${DOKAN_LIB_PATH}/dokan1.dll" "${DOKAN_LIB_PATH}/dokanfuse1.dll"
DESTINATION "${CMAKE_INSTALL_BINDIR}"
)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")

View File

@ -8,12 +8,15 @@
#include <cpp-utils/assert/assert.h>
#include <cpp-utils/logging/logging.h>
#include <csignal>
#include "InvalidFilesystem.h"
using std::vector;
using std::string;
namespace bf = boost::filesystem;
using namespace cpputils::logging;
using std::make_shared;
using std::shared_ptr;
using namespace fspp::fuse;
namespace {
@ -226,8 +229,10 @@ Fuse::~Fuse() {
_argv.clear();
}
Fuse::Fuse(Filesystem *fs, std::string fstype, boost::optional<std::string> fsname)
:_fs(fs), _mountdir(), _running(false), _fstype(std::move(fstype)), _fsname(std::move(fsname)) {
Fuse::Fuse(std::function<shared_ptr<Filesystem> (Fuse *fuse)> init, std::function<void()> onMounted, std::string fstype, boost::optional<std::string> fsname)
:_init(std::move(init)), _onMounted(std::move(onMounted)), _fs(make_shared<InvalidFilesystem>()), _mountdir(), _running(false), _fstype(std::move(fstype)), _fsname(std::move(fsname)) {
ASSERT(static_cast<bool>(_init), "Invalid init given");
ASSERT(static_cast<bool>(_onMounted), "Invalid onMounted given");
}
void Fuse::_logException(const std::exception &e) {
@ -717,7 +722,6 @@ int Fuse::write(const bf::path &path, const char *buf, size_t size, int64_t offs
}
}
//TODO
int Fuse::statfs(const bf::path &path, struct ::statvfs *fsstat) {
#ifdef FSPP_LOG
LOG(DEBUG, "statfs({}, _)", path);
@ -859,9 +863,12 @@ int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo)
void Fuse::init(fuse_conn_info *conn) {
UNUSED(conn);
_fs = _init(this);
LOG(INFO, "Filesystem started.");
_running = true;
_onMounted();
#ifdef FSPP_LOG
cpputils::logging::setLevel(DEBUG);
@ -869,8 +876,10 @@ void Fuse::init(fuse_conn_info *conn) {
}
void Fuse::destroy() {
_fs = make_shared<InvalidFilesystem>();
LOG(INFO, "Filesystem stopped.");
_running = false;
cpputils::logging::logger()->flush();
}
int Fuse::access(const bf::path &path, int mask) {

View File

@ -21,7 +21,7 @@ class Filesystem;
class Fuse final {
public:
explicit Fuse(Filesystem *fs, std::string fstype, boost::optional<std::string> fsname);
explicit Fuse(std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> init, std::function<void()> onMounted, std::string fstype, boost::optional<std::string> fsname);
~Fuse();
void run(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
@ -68,7 +68,9 @@ private:
std::vector<char *> _build_argv(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void _add_fuse_option_if_not_exists(std::vector<char *> *argv, const std::string &key, const std::string &value);
Filesystem *_fs;
std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> _init;
std::function<void()> _onMounted;
std::shared_ptr<Filesystem> _fs;
boost::filesystem::path _mountdir;
std::vector<char*> _argv;
std::atomic<bool> _running;

View File

@ -0,0 +1,110 @@
#pragma once
#ifndef MESSMER_FSPP_FUSE_INVALIDFILESYSTEM_H_
#define MESSMER_FSPP_FUSE_INVALIDFILESYSTEM_H_
#include "Filesystem.h"
namespace fspp {
namespace fuse {
class InvalidFilesystem final : public Filesystem {
int createAndOpenFile(const boost::filesystem::path &, ::mode_t , ::uid_t , ::gid_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
int openFile(const boost::filesystem::path &, int ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void flush(int ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void closeFile(int ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void lstat(const boost::filesystem::path &, fspp::fuse::STAT *) override {
throw std::logic_error("Filesystem not initialized yet");
}
void fstat(int , fspp::fuse::STAT *) override {
throw std::logic_error("Filesystem not initialized yet");
}
void chmod(const boost::filesystem::path &, ::mode_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void chown(const boost::filesystem::path &, ::uid_t , ::gid_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void truncate(const boost::filesystem::path &, fspp::num_bytes_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void ftruncate(int , fspp::num_bytes_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
fspp::num_bytes_t read(int , void *, fspp::num_bytes_t , fspp::num_bytes_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void write(int , const void *, fspp::num_bytes_t , fspp::num_bytes_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void fsync(int ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void fdatasync(int ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void access(const boost::filesystem::path &, int ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void mkdir(const boost::filesystem::path &, ::mode_t , ::uid_t , ::gid_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void rmdir(const boost::filesystem::path &) override {
throw std::logic_error("Filesystem not initialized yet");
}
void unlink(const boost::filesystem::path &) override {
throw std::logic_error("Filesystem not initialized yet");
}
void rename(const boost::filesystem::path &, const boost::filesystem::path &) override {
throw std::logic_error("Filesystem not initialized yet");
}
void utimens(const boost::filesystem::path &, timespec , timespec ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void statfs(struct ::statvfs *) override {
throw std::logic_error("Filesystem not initialized yet");
}
cpputils::unique_ref<std::vector<Dir::Entry>> readDir(const boost::filesystem::path &) override {
throw std::logic_error("Filesystem not initialized yet");
}
void createSymlink(const boost::filesystem::path &, const boost::filesystem::path &, ::uid_t , ::gid_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
void readSymlink(const boost::filesystem::path &, char *, fspp::num_bytes_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}
};
}
}
#endif

View File

@ -32,7 +32,7 @@ using namespace cpputils::logging;
#define PROFILE(name)
#endif
FilesystemImpl::FilesystemImpl(Device *device)
FilesystemImpl::FilesystemImpl(cpputils::unique_ref<Device> device)
:
#ifdef FSPP_PROFILE
_loadFileNanosec(0), _loadDirNanosec(0), _loadSymlinkNanosec(0), _openFileNanosec(0), _flushNanosec(0),
@ -44,7 +44,7 @@ FilesystemImpl::FilesystemImpl(Device *device)
_utimensNanosec(0), _statfsNanosec(0), _createSymlinkNanosec(0), _createSymlinkNanosec_withoutLoading(0),
_readSymlinkNanosec(0), _readSymlinkNanosec_withoutLoading(0),
#endif
_device(device), _open_files()
_device(std::move(device)), _open_files()
{
}

View File

@ -21,7 +21,7 @@ class OpenFile;
class FilesystemImpl final: public fuse::Filesystem {
public:
explicit FilesystemImpl(Device *device);
explicit FilesystemImpl(cpputils::unique_ref<Device> device);
virtual ~FilesystemImpl();
int openFile(const boost::filesystem::path &path, int flags) override;
@ -93,7 +93,7 @@ private:
std::atomic<uint64_t> _readSymlinkNanosec_withoutLoading;
#endif
Device *_device;
cpputils::unique_ref<Device> _device;
FuseOpenFileList _open_files;
DISALLOW_COPY_AND_ASSIGN(FilesystemImpl);

View File

@ -1,8 +1,9 @@
#include <iostream>
#include <boost/filesystem.hpp>
#include <cryfs/config/CryConfigFile.h>
#include <cryfs/config/CryConfigLoader.h>
#include <cryfs/config/CryPasswordBasedKeyProvider.h>
#include <blockstore/implementations/ondisk/OnDiskBlockStore2.h>
#include <blockstore/implementations/integrity/IntegrityBlockStore2.h>
#include <blockstore/implementations/low2highlevel/LowToHighLevelBlockStore.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNode.h>
@ -13,6 +14,7 @@
#include <cryfs/filesystem/fsblobstore/DirBlob.h>
#include <cryfs/filesystem/CryDevice.h>
#include <cpp-utils/io/IOStreamConsole.h>
#include <cpp-utils/system/homedir.h>
#include <set>
@ -23,13 +25,14 @@ using namespace cryfs;
using namespace cpputils;
using namespace blockstore;
using namespace blockstore::ondisk;
using namespace blockstore::integrity;
using namespace blockstore::lowtohighlevel;
using namespace blobstore::onblocks;
using namespace blobstore::onblocks::datanodestore;
using namespace cryfs::fsblobstore;
void printNode(unique_ref<DataNode> node) {
std::cout << "BlockId: " << node->blockId().ToString() << ", Depth: " << node->depth() << " ";
std::cout << "BlockId: " << node->blockId().ToString() << ", Depth: " << static_cast<int>(node->depth()) << " ";
auto innerNode = dynamic_pointer_move<DataInnerNode>(node);
if (innerNode != none) {
std::cout << "Type: inner\n";
@ -42,111 +45,146 @@ void printNode(unique_ref<DataNode> node) {
}
}
set<BlockId> _getBlockstoreUnaccountedBlocks(const CryConfig &config) {
auto onDiskBlockStore = make_unique_ref<OnDiskBlockStore2>("/home/heinzi/basedir");
auto encryptedBlockStore = CryCiphers::find(config.Cipher()).createEncryptedBlockstore(std::move(onDiskBlockStore), config.EncryptionKey());
auto highLevelBlockStore = make_unique_ref<LowToHighLevelBlockStore>(std::move(encryptedBlockStore));
auto nodeStore = make_unique_ref<DataNodeStore>(std::move(highLevelBlockStore), config.BlocksizeBytes());
std::set<BlockId> unaccountedBlocks;
uint32_t numBlocks = nodeStore->numNodes();
uint32_t i = 0;
cout << "There are " << nodeStore->numNodes() << " blocks." << std::endl;
// Add all blocks to unaccountedBlocks
for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) {
cout << "\r" << (++i) << "/" << numBlocks << flush;
if (file->path().filename() != "cryfs.config") {
auto blockId = BlockId::FromString(file->path().filename().string().c_str());
unaccountedBlocks.insert(blockId);
void _forEachBlob(FsBlobStore* blobStore, const BlockId& rootId, std::function<void (const BlockId& blobId)> callback) {
callback(rootId);
auto rootBlob = blobStore->load(rootId);
ASSERT(rootBlob != boost::none, "Blob not found but referenced from directory entry");
auto rootDir = dynamic_pointer_move<DirBlob>(*rootBlob);
if (rootDir != boost::none) {
std::vector<fspp::Dir::Entry> children;
children.reserve((*rootDir)->NumChildren());
(*rootDir)->AppendChildrenTo(&children);
for (const auto& child : children) {
auto childEntry = (*rootDir)->GetChild(child.name);
ASSERT(childEntry != boost::none, "We just got this from the entry list, it must exist.");
auto childId = childEntry->blockId();
_forEachBlob(blobStore, childId, callback);
}
}
i = 0;
cout << "\nRemove blocks that have a parent" << endl;
//Remove root block from unaccountedBlocks
unaccountedBlocks.erase(BlockId::FromString(config.RootBlob()));
//Remove all blocks that have a parent node from unaccountedBlocks
for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) {
cout << "\r" << (++i) << "/" << numBlocks << flush;
if (file->path().filename() != "cryfs.config") {
auto blockId = BlockId::FromString(file->path().filename().string().c_str());
auto node = nodeStore->load(blockId);
}
void _forEachBlockInBlob(DataNodeStore* nodeStore, const BlockId& rootId, std::function<void (const BlockId& blockId)> callback) {
callback(rootId);
auto node = nodeStore->load(rootId);
auto innerNode = dynamic_pointer_move<DataInnerNode>(*node);
if (innerNode != none) {
if (innerNode != boost::none) {
for (uint32_t childIndex = 0; childIndex < (*innerNode)->numChildren(); ++childIndex) {
auto child = (*innerNode)->readChild(childIndex).blockId();
unaccountedBlocks.erase(child);
auto childId = (*innerNode)->readChild(childIndex).blockId();
_forEachBlockInBlob(nodeStore, childId, callback);
}
}
}
}
return unaccountedBlocks;
}
set<BlockId> _getBlocksReferencedByDirEntries(const CryConfig &config) {
auto onDiskBlockStore = make_unique_ref<OnDiskBlockStore2>("/home/heinzi/basedir");
auto encryptedBlockStore = CryCiphers::find(config.Cipher()).createEncryptedBlockstore(std::move(onDiskBlockStore), config.EncryptionKey());
auto highLevelBlockStore = make_unique_ref<LowToHighLevelBlockStore>(std::move(encryptedBlockStore));
auto fsBlobStore = make_unique_ref<FsBlobStore>(make_unique_ref<BlobStoreOnBlocks>(std::move(highLevelBlockStore), config.BlocksizeBytes()));
set<BlockId> blocksReferencedByDirEntries;
uint32_t numBlocks = fsBlobStore->numBlocks();
unique_ref<BlockStore> makeBlockStore(const path& basedir, const CryConfigLoader::ConfigLoadResult& config, LocalStateDir& localStateDir) {
auto onDiskBlockStore = make_unique_ref<OnDiskBlockStore2>(basedir);
auto encryptedBlockStore = CryCiphers::find(config.configFile.config()->Cipher()).createEncryptedBlockstore(std::move(onDiskBlockStore), config.configFile.config()->EncryptionKey());
auto statePath = localStateDir.forFilesystemId(config.configFile.config()->FilesystemId());
auto integrityFilePath = statePath / "integritydata";
auto onIntegrityViolation = [] () {
std::cerr << "Warning: Integrity violation encountered" << std::endl;
};
auto integrityBlockStore = make_unique_ref<IntegrityBlockStore2>(std::move(encryptedBlockStore), integrityFilePath, config.myClientId, false, true, onIntegrityViolation);
return make_unique_ref<LowToHighLevelBlockStore>(std::move(integrityBlockStore));
}
std::vector<BlockId> _getKnownBlobIds(const path& basedir, const CryConfigLoader::ConfigLoadResult& config, LocalStateDir& localStateDir) {
auto blockStore = makeBlockStore(basedir, config, localStateDir);
auto fsBlobStore = make_unique_ref<FsBlobStore>(make_unique_ref<BlobStoreOnBlocks>(std::move(blockStore), config.configFile.config()->BlocksizeBytes()));
std::vector<BlockId> result;
cout << "Listing all file system entities (i.e. blobs)..." << flush;
auto rootId = BlockId::FromString(config.configFile.config()->RootBlob());
_forEachBlob(fsBlobStore.get(), rootId, [&result] (const BlockId& blockId) {
result.push_back(blockId);
});
cout << "done" << endl;
return result;
}
std::vector<BlockId> _getKnownBlockIds(const path& basedir, const CryConfigLoader::ConfigLoadResult& config, LocalStateDir& localStateDir) {
auto knownBlobIds = _getKnownBlobIds(basedir, config, localStateDir);
auto blockStore = makeBlockStore(basedir, config, localStateDir);
auto nodeStore = make_unique_ref<DataNodeStore>(std::move(blockStore), config.configFile.config()->BlocksizeBytes());
std::vector<BlockId> result;
const uint32_t numNodes = nodeStore->numNodes();
result.reserve(numNodes);
uint32_t i = 0;
cout << "\nRemove blocks referenced by dir entries" << endl;
for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) {
cout << "\r" << (++i) << "/" << numBlocks << flush;
if (file->path().filename() != "cryfs.config") {
auto blockId = BlockId::FromString(file->path().filename().string().c_str());
try {
auto blob = fsBlobStore->load(blockId);
if (blob != none) {
auto dir = dynamic_pointer_move<DirBlob>(*blob);
if (dir != none) {
vector<fspp::Dir::Entry> children;
(*dir)->AppendChildrenTo(&children);
for (const auto &child : children) {
blocksReferencedByDirEntries.insert((*dir)->GetChild(child.name)->blockId());
cout << "Listing all blocks used by these file system entities..." << endl;
for (const auto& blobId : knownBlobIds) {
_forEachBlockInBlob(nodeStore.get(), blobId, [&result, &i, numNodes] (const BlockId& blockId) {
cout << "\r" << (++i) << "/" << numNodes << flush;
result.push_back(blockId);
});
}
}
}
} catch (...) {}
}
}
return blocksReferencedByDirEntries;
std::cout << "...done" << endl;
return result;
}
set<BlockId> _getAllBlockIds(const path& basedir, const CryConfigLoader::ConfigLoadResult& config, LocalStateDir& localStateDir) {
auto blockStore= makeBlockStore(basedir, config, localStateDir);
set<BlockId> result;
blockStore->forEachBlock([&result] (const BlockId& blockId) {
result.insert(blockId);
});
return result;
}
int main() {
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: cryfs-stats [basedir]" << std::endl;
exit(1);
}
path basedir = argv[1];
std::cout << "Calculating stats for filesystem at " << basedir << std::endl;
auto console = std::make_shared<cpputils::IOStreamConsole>();
console->print("Loading config\n");
auto askPassword = [console] () {
return console->askPassword("Password: ");
};
auto keyProvider = make_unique_ref<CryPasswordBasedKeyProvider>(
unique_ref<CryKeyProvider> keyProvider = make_unique_ref<CryPasswordBasedKeyProvider>(
console,
askPassword,
askPassword,
make_unique_ref<SCrypt>(SCrypt::DefaultSettings)
);
auto config = CryConfigFile::load("/home/heinzi/basedir/cryfs.config", keyProvider.get());
set<BlockId> unaccountedBlocks = _getBlockstoreUnaccountedBlocks(*config->config());
//Remove all blocks that are referenced by a directory entry from unaccountedBlocks
set<BlockId> blocksReferencedByDirEntries = _getBlocksReferencedByDirEntries(*config->config());
for (const auto &blockId : blocksReferencedByDirEntries) {
unaccountedBlocks.erase(blockId);
auto config_path = basedir / "cryfs.config";
LocalStateDir localStateDir(cpputils::system::HomeDirectory::getXDGDataDir() / "cryfs");
CryConfigLoader config_loader(console, Random::OSRandom(), std::move(keyProvider), localStateDir, boost::none, boost::none, boost::none);
auto config = config_loader.load(config_path, false, true);
if (config == boost::none) {
std::cerr << "Error loading config file" << std::endl;
exit(1);
}
cout << "Listing all blocks..." << flush;
set<BlockId> unaccountedBlocks = _getAllBlockIds(basedir, *config, localStateDir);
cout << "done" << endl;
vector<BlockId> accountedBlocks = _getKnownBlockIds(basedir, *config, localStateDir);
for (const BlockId& blockId : accountedBlocks) {
auto num_erased = unaccountedBlocks.erase(blockId);
ASSERT(1 == num_erased, "Blob id referenced by directory entry but didn't found it on disk? This can't happen.");
}
console->print("Calculate statistics\n");
auto onDiskBlockStore = make_unique_ref<OnDiskBlockStore2>("/home/heinzi/basedir");
auto encryptedBlockStore = CryCiphers::find(config->config()->Cipher()).createEncryptedBlockstore(std::move(onDiskBlockStore), config->config()->EncryptionKey());
auto highLevelBlockStore = make_unique_ref<LowToHighLevelBlockStore>(std::move(encryptedBlockStore));
auto nodeStore = make_unique_ref<DataNodeStore>(std::move(highLevelBlockStore), config->config()->BlocksizeBytes());
auto blockStore = makeBlockStore(basedir, *config, localStateDir);
auto nodeStore = make_unique_ref<DataNodeStore>(std::move(blockStore), config->configFile.config()->BlocksizeBytes());
uint32_t numUnaccountedBlocks = unaccountedBlocks.size();
uint32_t numLeaves = 0;
uint32_t numInner = 0;
console->print("Unaccounted blocks: " + std::to_string(unaccountedBlocks.size()) + "\n");
for (const auto &blockId : unaccountedBlocks) {
console->print("\r" + std::to_string(numLeaves+numInner) + "/" + std::to_string(numUnaccountedBlocks));
console->print("\r" + std::to_string(numLeaves+numInner) + "/" + std::to_string(numUnaccountedBlocks) + ": ");
auto node = nodeStore->load(blockId);
auto innerNode = dynamic_pointer_move<DataInnerNode>(*node);
if (innerNode != none) {

View File

@ -25,7 +25,7 @@ public:
TempFile stateFile;
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<LowToHighLevelBlockStore>(
make_unique_ref<IntegrityBlockStore2>(make_unique_ref<InMemoryBlockStore2>(), stateFile.path(), 0x12345678, AllowIntegrityViolations, MissingBlockIsIntegrityViolation)
make_unique_ref<IntegrityBlockStore2>(make_unique_ref<InMemoryBlockStore2>(), stateFile.path(), 0x12345678, AllowIntegrityViolations, MissingBlockIsIntegrityViolation, [] {})
);
}
};
@ -47,7 +47,7 @@ public:
TempFile stateFile;
unique_ref<BlockStore2> createBlockStore() override {
return make_unique_ref<IntegrityBlockStore2>(make_unique_ref<InMemoryBlockStore2>(), stateFile.path(), 0x12345678, AllowIntegrityViolations, MissingBlockIsIntegrityViolation);
return make_unique_ref<IntegrityBlockStore2>(make_unique_ref<InMemoryBlockStore2>(), stateFile.path(), 0x12345678, AllowIntegrityViolations, MissingBlockIsIntegrityViolation, [] {});
}
};

View File

@ -16,40 +16,50 @@ using cpputils::TempFile;
using cpputils::serialize;
using cpputils::deserialize;
using boost::none;
using std::make_unique;
using std::unique_ptr;
using blockstore::inmemory::InMemoryBlockStore2;
using namespace blockstore::integrity;
namespace {
class FakeCallback final {
public:
FakeCallback(): wasCalled_(false) {}
bool wasCalled() const {
return wasCalled_;
}
std::function<void ()> callback() {
return [this] () {
wasCalled_ = true;
};
}
private:
bool wasCalled_;
};
}
template<bool AllowIntegrityViolations, bool MissingBlockIsIntegrityViolation>
class IntegrityBlockStoreTest: public Test {
public:
static constexpr unsigned int BLOCKSIZE = 1024;
IntegrityBlockStoreTest():
stateFile(false),
onIntegrityViolation(),
baseBlockStore(new InMemoryBlockStore2),
blockStore(make_unique_ref<IntegrityBlockStore2>(std::move(cpputils::nullcheck(std::unique_ptr<InMemoryBlockStore2>(baseBlockStore)).value()), stateFile.path(), myClientId, false, false)),
blockStore(make_unique_ref<IntegrityBlockStore2>(std::move(cpputils::nullcheck(std::unique_ptr<InMemoryBlockStore2>(baseBlockStore)).value()), stateFile.path(), myClientId, AllowIntegrityViolations, MissingBlockIsIntegrityViolation, onIntegrityViolation.callback())),
data(DataFixture::generate(BLOCKSIZE)) {
}
static constexpr uint32_t myClientId = 0x12345678;
TempFile stateFile;
FakeCallback onIntegrityViolation;
InMemoryBlockStore2 *baseBlockStore;
unique_ref<IntegrityBlockStore2> blockStore;
Data data;
std::pair<InMemoryBlockStore2 *, unique_ptr<IntegrityBlockStore2>> makeBlockStoreWithDeletionPrevention() {
InMemoryBlockStore2 *baseBlockStore = new InMemoryBlockStore2;
auto blockStore = make_unique<IntegrityBlockStore2>(std::move(cpputils::nullcheck(std::unique_ptr<InMemoryBlockStore2>(baseBlockStore)).value()), stateFile.path(), myClientId, false, true);
return std::make_pair(baseBlockStore, std::move(blockStore));
}
std::pair<InMemoryBlockStore2 *, unique_ptr<IntegrityBlockStore2>> makeBlockStoreWithoutDeletionPrevention() {
InMemoryBlockStore2 *baseBlockStore = new InMemoryBlockStore2;
auto blockStore = make_unique<IntegrityBlockStore2>(std::move(cpputils::nullcheck(std::unique_ptr<InMemoryBlockStore2>(baseBlockStore)).value()), stateFile.path(), myClientId, false, false);
return std::make_pair(baseBlockStore, std::move(blockStore));
}
blockstore::BlockId CreateBlockReturnKey() {
return CreateBlockReturnKey(data);
}
@ -114,34 +124,58 @@ private:
DISALLOW_COPY_AND_ASSIGN(IntegrityBlockStoreTest);
};
constexpr uint32_t IntegrityBlockStoreTest::myClientId;
using IntegrityBlockStoreTest_Default = IntegrityBlockStoreTest<false, false>;
using IntegrityBlockStoreTest_MissingBlockIsIntegrityViolation = IntegrityBlockStoreTest<false, true>;
using IntegrityBlockStoreTest_AllowIntegrityViolations = IntegrityBlockStoreTest<true, false>;
using IntegrityBlockStoreTest_AllowIntegrityViolations_MissingBlockIsIntegrityViolation = IntegrityBlockStoreTest<true, true>;
template<bool AllowIntegrityViolations, bool MissingBlockIsIntegrityViolation>
constexpr uint32_t IntegrityBlockStoreTest<AllowIntegrityViolations, MissingBlockIsIntegrityViolation>::myClientId;
// Test that a decreasing version number is not allowed
TEST_F(IntegrityBlockStoreTest, RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_1) {
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_1) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
modifyBlock(blockId);
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_THROW(
blockStore->load(blockId),
IntegrityViolationError
);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest, RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_2) {
// Test that a decreasing version number is allowed if allowIntegrityViolations is set.
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsDecreasingVersionNumberForSameClient_1) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
modifyBlock(blockId);
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowDecreasingVersionNumberForSameClient_2) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
// Decrease the version number again
decreaseVersionNumber(blockId);
EXPECT_THROW(
blockStore->load(blockId),
IntegrityViolationError
);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsDecreasingVersionNumberForSameClient_2) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
// Decrease the version number again
decreaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Test that a different client doesn't need to have a higher version number (i.e. version numbers are per client).
TEST_F(IntegrityBlockStoreTest, RollbackPrevention_DoesAllowDecreasingVersionNumberForDifferentClient) {
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesAllowDecreasingVersionNumberForDifferentClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
@ -149,10 +183,22 @@ TEST_F(IntegrityBlockStoreTest, RollbackPrevention_DoesAllowDecreasingVersionNum
changeClientId(blockId);
decreaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_DoesAllowDecreasingVersionNumberForDifferentClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
// Fake a modification by a different client with lower version numbers
changeClientId(blockId);
decreaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Test that it doesn't allow a rollback to the "newest" block of a client, when this block was superseded by a version of a different client
TEST_F(IntegrityBlockStoreTest, RollbackPrevention_DoesntAllowSameVersionNumberForOldClient) {
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowSameVersionNumberForOldClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
@ -162,62 +208,99 @@ TEST_F(IntegrityBlockStoreTest, RollbackPrevention_DoesntAllowSameVersionNumberF
loadBlock(blockId); // make the block store know about this other client's modification
// Rollback to old client
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_THROW(
blockStore->load(blockId),
IntegrityViolationError
);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Test that it does allow a rollback to the "newest" block of a client, when this block was superseded by a version of a different client, but integrity violations are allowed
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsSameVersionNumberForOldClient) {
auto blockId = CreateBlockReturnKey();
// Increase the version number
modifyBlock(blockId);
Data oldBaseBlock = loadBaseBlock(blockId);
// Fake a modification by a different client with lower version numbers
changeClientId(blockId);
loadBlock(blockId); // make the block store know about this other client's modification
// Rollback to old client
rollbackBaseBlock(blockId, oldBaseBlock);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Test that deleted blocks cannot be re-introduced
TEST_F(IntegrityBlockStoreTest, RollbackPrevention_DoesntAllowReintroducingDeletedBlocks) {
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_DoesntAllowReintroducingDeletedBlocks) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
EXPECT_THROW(
blockStore->load(blockId),
IntegrityViolationError
);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Test that deleted blocks can be re-introduced if integrity violations are allowed
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsReintroducingDeletedBlocks) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// This can happen if a client synchronization is delayed. Another client might have won the conflict and pushed a new version for the deleted block.
TEST_F(IntegrityBlockStoreTest, RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber) {
TEST_F(IntegrityBlockStoreTest_Default, RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
increaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, RollbackPrevention_AllowsReintroducingDeletedBlocksWithNewVersionNumber) {
auto blockId = CreateBlockReturnKey();
Data oldBaseBlock = loadBaseBlock(blockId);
deleteBlock(blockId);
insertBaseBlock(blockId, std::move(oldBaseBlock));
increaseVersionNumber(blockId);
EXPECT_NE(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a multi-client scenario, missing blocks are not integrity errors, because another client might have deleted them.
TEST_F(IntegrityBlockStoreTest, DeletionPrevention_AllowsDeletingBlocksWhenDeactivated) {
InMemoryBlockStore2 *baseBlockStore;
unique_ptr<IntegrityBlockStore2> blockStore;
std::tie(baseBlockStore, blockStore) = makeBlockStoreWithoutDeletionPrevention();
TEST_F(IntegrityBlockStoreTest_Default, DeletionPrevention_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, DeletionPrevention_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a single-client scenario, missing blocks are integrity errors.
TEST_F(IntegrityBlockStoreTest, DeletionPrevention_DoesntAllowDeletingBlocksWhenActivated) {
InMemoryBlockStore2 *baseBlockStore;
unique_ptr<IntegrityBlockStore2> blockStore;
std::tie(baseBlockStore, blockStore) = makeBlockStoreWithDeletionPrevention();
TEST_F(IntegrityBlockStoreTest_MissingBlockIsIntegrityViolation, DeletionPrevention_DoesntAllowDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_THROW(
blockStore->load(blockId),
IntegrityViolationError
);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
// Check that in a single-client scenario, missing blocks don't throw if integrity violations are allowed.
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations_MissingBlockIsIntegrityViolation, DeletionPrevention_AllowsDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_EQ(boost::none, blockStore->load(blockId));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a multi-client scenario, missing blocks are not integrity errors, because another client might have deleted them.
TEST_F(IntegrityBlockStoreTest, DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenDeactivated) {
InMemoryBlockStore2 *baseBlockStore;
unique_ptr<IntegrityBlockStore2> blockStore;
std::tie(baseBlockStore, blockStore) = makeBlockStoreWithoutDeletionPrevention();
TEST_F(IntegrityBlockStoreTest_Default, DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
int count = 0;
@ -225,29 +308,50 @@ TEST_F(IntegrityBlockStoreTest, DeletionPrevention_InForEachBlock_AllowsDeleting
++count;
});
EXPECT_EQ(0, count);
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenDeactivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
int count = 0;
blockStore->forEachBlock([&count] (const blockstore::BlockId &) {
++count;
});
EXPECT_EQ(0, count);
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// Check that in a single-client scenario, missing blocks are integrity errors.
TEST_F(IntegrityBlockStoreTest, DeletionPrevention_InForEachBlock_DoesntAllowDeletingBlocksWhenActivated) {
InMemoryBlockStore2 *baseBlockStore;
unique_ptr<IntegrityBlockStore2> blockStore;
std::tie(baseBlockStore, blockStore) = makeBlockStoreWithDeletionPrevention();
TEST_F(IntegrityBlockStoreTest_MissingBlockIsIntegrityViolation, DeletionPrevention_InForEachBlock_DoesntAllowDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
EXPECT_THROW(
blockStore->forEachBlock([] (const blockstore::BlockId &) {}),
IntegrityViolationError
);
blockStore->forEachBlock([] (const blockstore::BlockId &) {});
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest, LoadingWithDifferentBlockIdFails) {
// Check that in a single-client scenario, missing blocks don't throw if integrity violations are allowed.
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations_MissingBlockIsIntegrityViolation, DeletionPrevention_InForEachBlock_AllowsDeletingBlocksWhenActivated) {
auto blockId = blockStore->create(Data(0));
baseBlockStore->remove(blockId);
blockStore->forEachBlock([] (const blockstore::BlockId &) {});
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_Default, LoadingWithDifferentBlockIdFails) {
auto blockId = CreateBlockReturnKey();
blockstore::BlockId key2 = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
baseBlockStore->store(key2, baseBlockStore->load(blockId).value());
EXPECT_THROW(
blockStore->load(key2),
IntegrityViolationError
);
EXPECT_EQ(boost::none, blockStore->load(key2));
EXPECT_TRUE(onIntegrityViolation.wasCalled());
}
TEST_F(IntegrityBlockStoreTest_AllowIntegrityViolations, LoadingWithDifferentBlockIdDoesntFail) {
auto blockId = CreateBlockReturnKey();
blockstore::BlockId key2 = blockstore::BlockId::FromString("1491BB4932A389EE14BC7090AC772972");
baseBlockStore->store(key2, baseBlockStore->load(blockId).value());
EXPECT_NE(boost::none, blockStore->load(key2));
EXPECT_FALSE(onIntegrityViolation.wasCalled());
}
// TODO Test more integrity cases:
@ -256,17 +360,17 @@ TEST_F(IntegrityBlockStoreTest, LoadingWithDifferentBlockIdFails) {
// - Think about more...
// TODO Test that disabling integrity checks allows all these cases
TEST_F(IntegrityBlockStoreTest, PhysicalBlockSize_zerophysical) {
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_zerophysical) {
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(0));
}
TEST_F(IntegrityBlockStoreTest, PhysicalBlockSize_zerovirtual) {
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_zerovirtual) {
auto blockId = CreateBlockReturnKey(Data(0));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(0u, blockStore->blockSizeFromPhysicalBlockSize(base.size()));
}
TEST_F(IntegrityBlockStoreTest, PhysicalBlockSize_negativeboundaries) {
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_negativeboundaries) {
// This tests that a potential if/else in blockSizeFromPhysicalBlockSize that catches negative values has the
// correct boundary set. We test the highest value that is negative and the smallest value that is positive.
auto physicalSizeForVirtualSizeZero = baseBlockStore->load(CreateBlockReturnKey(Data(0))).value().size();
@ -277,7 +381,7 @@ TEST_F(IntegrityBlockStoreTest, PhysicalBlockSize_negativeboundaries) {
EXPECT_EQ(1u, blockStore->blockSizeFromPhysicalBlockSize(physicalSizeForVirtualSizeZero + 1));
}
TEST_F(IntegrityBlockStoreTest, PhysicalBlockSize_positive) {
TEST_F(IntegrityBlockStoreTest_Default, PhysicalBlockSize_positive) {
auto blockId = CreateBlockReturnKey(Data(10*1024));
auto base = baseBlockStore->load(blockId).value();
EXPECT_EQ(10*1024u, blockStore->blockSizeFromPhysicalBlockSize(base.size()));

View File

@ -4,6 +4,10 @@
#include <unordered_set>
#include <set>
// TODO Test with MovableOnly underlying type
// TODO Test that move constructing/assigning actually moves the underlying
// TODO Test that noexcept flags are set correctly
using cpputils::value_type::IdValueType;
using cpputils::value_type::OrderedIdValueType;
using cpputils::value_type::QuantityValueType;

View File

@ -3,6 +3,9 @@
#include <cryfs/ErrorCodes.h>
#include <cpp-utils/crypto/kdf/Scrypt.h>
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/tempfile/TempDir.h>
#include <blockstore/implementations/caching/CachingBlockStore2.h>
#include <cryfs/filesystem/cachingfsblobstore/CachingFsBlobStore.h>
using std::vector;
using std::string;
@ -13,9 +16,43 @@ using cryfs::CryKeyProvider;
using cpputils::Data;
using cpputils::EncryptionKey;
using cpputils::SCrypt;
using cpputils::TempDir;
namespace bf = boost::filesystem;
namespace {
void writeFile(const bf::path& filename, const string& content) {
std::ofstream file(filename.c_str(), std::ios::trunc);
file << content;
ASSERT(file.good(), "Failed writing file to file system");
}
bool readingFileIsSuccessful(const bf::path& filename) {
std::ifstream file(filename.c_str());
std::string content;
file >> content; // just read a little bit so we have a file access
return file.good();
}
void recursive_copy(const bf::path &src, const bf::path &dst) {
if (bf::exists(dst)) {
throw std::runtime_error(dst.generic_string() + " already exists");
}
if (bf::is_directory(src)) {
bf::create_directories(dst);
for (auto& item : bf::directory_iterator(src)) {
recursive_copy(item.path(), dst / item.path().filename());
}
} else if (bf::is_regular_file(src)) {
bf::copy_file(src, dst);
} else {
throw std::runtime_error(dst.generic_string() + " neither dir nor file");
}
}
class FakeCryKeyProvider final : public CryKeyProvider {
EncryptionKey requestKeyForExistingFilesystem(size_t keySize, const Data& kdfParameters) override {
EncryptionKey requestKeyForExistingFilesystem(size_t keySize, const Data &kdfParameters) override {
return SCrypt(SCrypt::TestSettings).deriveExistingKey(keySize, "pass", kdfParameters);
}
@ -28,7 +65,7 @@ class FakeCryKeyProvider final : public CryKeyProvider {
}
};
class CliTest_IntegrityCheck: public CliTest {
class CliTest_IntegrityCheck : public CliTest {
public:
void modifyFilesystemId() {
FakeCryKeyProvider keyProvider;
@ -46,25 +83,93 @@ public:
};
TEST_F(CliTest_IntegrityCheck, givenIncorrectFilesystemId_thenFails) {
vector<string> args {basedir.string().c_str(), mountdir.string().c_str(), "--cipher", "aes-256-gcm", "-f"};
vector<string> args{basedir.string().c_str(), mountdir.string().c_str(), "--cipher", "aes-256-gcm", "-f"};
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS can handle that
EXPECT_RUN_SUCCESS(args, mountdir);
modifyFilesystemId();
EXPECT_RUN_ERROR(
args,
"Error: The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir.",
"Error 20: The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir.",
ErrorCode::FilesystemIdChanged
);
}
TEST_F(CliTest_IntegrityCheck, givenIncorrectFilesystemKey_thenFails) {
vector<string> args {basedir.string().c_str(), mountdir.string().c_str(), "--cipher", "aes-256-gcm", "-f"};
vector<string> args{basedir.string().c_str(), mountdir.string().c_str(), "--cipher", "aes-256-gcm", "-f"};
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS can handle that
EXPECT_RUN_SUCCESS(args, mountdir);
modifyFilesystemKey();
EXPECT_RUN_ERROR(
args,
"Error: The filesystem encryption key differs from the last time we loaded this filesystem. Did an attacker replace the file system?",
"Error 21: The filesystem encryption key differs from the last time we loaded this filesystem. Did an attacker replace the file system?",
ErrorCode::EncryptionKeyChanged
);
}
// TODO Also enable this
TEST_F(CliTest_IntegrityCheck, givenFilesystemWithRolledBackBasedir_whenMounting_thenFails) {
vector<string> args{basedir.string().c_str(), mountdir.string().c_str(), "--cipher", "aes-256-gcm", "-f"};
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS/EXPECT_RUN_ERROR can handle that
// create a filesystem with one file
EXPECT_RUN_SUCCESS(args, mountdir, [&] {
writeFile(mountdir / "myfile", "hello world");
});
// backup the base directory
TempDir backup;
recursive_copy(basedir, backup.path() / "basedir");
// modify the file system contents
EXPECT_RUN_SUCCESS(args, mountdir, [&] {
writeFile(mountdir / "myfile", "hello world 2");
});
// roll back base directory
bf::remove_all(basedir);
recursive_copy(backup.path() / "basedir", basedir);
// error code is success because it unmounts normally
EXPECT_RUN_ERROR(args, "Integrity violation detected. Unmounting.", ErrorCode::IntegrityViolation, [&] {
EXPECT_FALSE(readingFileIsSuccessful(mountdir / "myfile"));
});
// Test it doesn't mount anymore now because it's marked with an integrity violation
EXPECT_RUN_ERROR(args, "There was an integrity violation detected. Preventing any further access to the file system.", ErrorCode::IntegrityViolationOnPreviousRun);
}
TEST_F(CliTest_IntegrityCheck, whenRollingBackBasedirWhileMounted_thenUnmounts) {
vector<string> args{basedir.string().c_str(), mountdir.string().c_str(), "--cipher", "aes-256-gcm", "-f"};
//TODO Remove "-f" parameter, once EXPECT_RUN_SUCCESS/EXPECT_RUN_ERROR can handle that
// create a filesystem with one file
EXPECT_RUN_SUCCESS(args, mountdir, [&] {
writeFile(mountdir / "myfile", "hello world");
});
// backup the base directory
TempDir backup;
recursive_copy(basedir, backup.path() / "basedir");
EXPECT_RUN_ERROR(args, "Integrity violation detected. Unmounting.", ErrorCode::IntegrityViolation, [&] {
// modify the file system contents
writeFile(mountdir / "myfile", "hello world 2");
ASSERT(readingFileIsSuccessful(mountdir / "myfile"), ""); // just to make sure reading usually works
// wait for cache timeout (i.e. flush file system to disk)
constexpr auto cache_timeout = blockstore::caching::CachingBlockStore2::MAX_LIFETIME_SEC + cryfs::cachingfsblobstore::CachingFsBlobStore::MAX_LIFETIME_SEC;
boost::this_thread::sleep_for(boost::chrono::seconds(static_cast<int>(std::ceil(cache_timeout * 3))));
// roll back base directory
bf::remove_all(basedir);
recursive_copy(backup.path() / "basedir", basedir);
// expect reading now fails
EXPECT_FALSE(readingFileIsSuccessful(mountdir / "myfile"));
});
// Test it doesn't mount anymore now because it's marked with an integrity violation
EXPECT_RUN_ERROR(args, "There was an integrity violation detected. Preventing any further access to the file system.", ErrorCode::IntegrityViolationOnPreviousRun);
}
}

View File

@ -89,7 +89,7 @@ TEST_P(CliTest_WrongEnvironment, NoErrorCondition) {
TEST_P(CliTest_WrongEnvironment, MountDirIsBaseDir) {
mountdir = basedir;
Test_Run_Error("Error: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
Test_Run_Error("Error 18: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
}
bf::path make_relative(const bf::path &path) {
@ -104,26 +104,26 @@ bf::path make_relative(const bf::path &path) {
TEST_P(CliTest_WrongEnvironment, MountDirIsBaseDir_MountDirRelative) {
mountdir = make_relative(basedir);
Test_Run_Error("Error: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
Test_Run_Error("Error 18: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
}
TEST_P(CliTest_WrongEnvironment, MountDirIsBaseDir_BaseDirRelative) {
mountdir = basedir;
basedir = make_relative(basedir);
Test_Run_Error("Error: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
Test_Run_Error("Error 18: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
}
TEST_P(CliTest_WrongEnvironment, MountDirIsBaseDir_BothRelative) {
basedir = make_relative(basedir);
mountdir = basedir;
Test_Run_Error("Error: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
Test_Run_Error("Error 18: base directory can't be inside the mount directory", ErrorCode::BaseDirInsideMountDir);
}
TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist) {
_basedir.remove();
// ON_CALL and not EXPECT_CALL, because this is a death test (i.e. it is forked) and gmock EXPECT_CALL in fork children don't report to parents.
ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?", _)).WillByDefault(Return(false));
Test_Run_Error("Error: base directory not found", ErrorCode::InaccessibleBaseDir);
Test_Run_Error("Error 16: base directory not found", ErrorCode::InaccessibleBaseDir);
}
TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist_Noninteractive) {
@ -132,7 +132,7 @@ TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist_Noninteractive) {
// So we set a default answer that shouldn't crash and check it's not called by checking that it crashes.
ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?", _)).WillByDefault(Return(true));
cpputils::setenv("CRYFS_FRONTEND", "noninteractive");
Test_Run_Error("Error: base directory not found", ErrorCode::InaccessibleBaseDir);
Test_Run_Error("Error 16: base directory not found", ErrorCode::InaccessibleBaseDir);
cpputils::unsetenv("CRYFS_FRONTEND");
}
@ -147,7 +147,7 @@ TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist_Create) {
TEST_P(CliTest_WrongEnvironment, BaseDir_IsNotDirectory) {
TempFile basedirfile;
basedir = basedirfile.path();
Test_Run_Error("Error: base directory is not a directory", ErrorCode::InaccessibleBaseDir);
Test_Run_Error("Error 16: base directory is not a directory", ErrorCode::InaccessibleBaseDir);
}
TEST_P(CliTest_WrongEnvironment, BaseDir_AllPermissions) {
@ -161,22 +161,22 @@ TEST_P(CliTest_WrongEnvironment, BaseDir_AllPermissions) {
#if !defined(_MSC_VER)
TEST_P(CliTest_WrongEnvironment, BaseDir_NoReadPermission) {
SetNoReadPermission(basedir);
Test_Run_Error("Error: Could not read from base directory", ErrorCode::InaccessibleBaseDir);
Test_Run_Error("Error 16: Could not read from base directory", ErrorCode::InaccessibleBaseDir);
}
TEST_P(CliTest_WrongEnvironment, BaseDir_NoExePermission) {
SetNoExePermission(basedir);
Test_Run_Error("Error: Could not write to base directory", ErrorCode::InaccessibleBaseDir);
Test_Run_Error("Error 16: Could not write to base directory", ErrorCode::InaccessibleBaseDir);
}
TEST_P(CliTest_WrongEnvironment, BaseDir_NoWritePermission) {
SetNoWritePermission(basedir);
Test_Run_Error("Error: Could not write to base directory", ErrorCode::InaccessibleBaseDir);
Test_Run_Error("Error 16: Could not write to base directory", ErrorCode::InaccessibleBaseDir);
}
TEST_P(CliTest_WrongEnvironment, BaseDir_NoPermission) {
SetNoPermission(basedir);
Test_Run_Error("Error: Could not write to base directory", ErrorCode::InaccessibleBaseDir);
Test_Run_Error("Error 16: Could not write to base directory", ErrorCode::InaccessibleBaseDir);
}
#endif
@ -208,7 +208,7 @@ TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist_Create) {
TEST_P(CliTest_WrongEnvironment, MountDir_IsNotDirectory) {
TempFile mountdirfile;
mountdir = mountdirfile.path();
Test_Run_Error("Error: mount directory is not a directory", ErrorCode::InaccessibleMountDir);
Test_Run_Error("Error 17: mount directory is not a directory", ErrorCode::InaccessibleMountDir);
}
TEST_P(CliTest_WrongEnvironment, MountDir_AllPermissions) {
@ -222,21 +222,21 @@ TEST_P(CliTest_WrongEnvironment, MountDir_AllPermissions) {
#if !defined(_MSC_VER)
TEST_P(CliTest_WrongEnvironment, MountDir_NoReadPermission) {
SetNoReadPermission(mountdir);
Test_Run_Error("Error: Could not read from mount directory", ErrorCode::InaccessibleMountDir);
Test_Run_Error("Error 17: Could not read from mount directory", ErrorCode::InaccessibleMountDir);
}
TEST_P(CliTest_WrongEnvironment, MountDir_NoExePermission) {
SetNoExePermission(mountdir);
Test_Run_Error("Error: Could not write to mount directory", ErrorCode::InaccessibleMountDir);
Test_Run_Error("Error 17: Could not write to mount directory", ErrorCode::InaccessibleMountDir);
}
TEST_P(CliTest_WrongEnvironment, MountDir_NoWritePermission) {
SetNoWritePermission(mountdir);
Test_Run_Error("Error: Could not write to mount directory", ErrorCode::InaccessibleMountDir);
Test_Run_Error("Error 17: Could not write to mount directory", ErrorCode::InaccessibleMountDir);
}
TEST_P(CliTest_WrongEnvironment, MountDir_NoPermission) {
SetNoPermission(mountdir);
Test_Run_Error("Error: Could not write to mount directory", ErrorCode::InaccessibleMountDir);
Test_Run_Error("Error 17: Could not write to mount directory", ErrorCode::InaccessibleMountDir);
}
#endif

View File

@ -16,6 +16,7 @@
#include <cpp-utils/logging/logging.h>
#include <cpp-utils/process/subprocess.h>
#include <cpp-utils/network/FakeHttpClient.h>
#include <cpp-utils/lock/ConditionBarrier.h>
#include "../../cryfs/testutils/MockConsole.h"
#include "../../cryfs/testutils/TestWithFakeHomeDirectory.h"
#include <cryfs/ErrorCodes.h>
@ -41,7 +42,7 @@ public:
return std::move(httpClient);
}
int run(const std::vector<std::string>& args) {
int run(const std::vector<std::string>& args, std::function<void()> onMounted) {
std::vector<const char*> _args;
_args.reserve(args.size() + 1);
_args.emplace_back("cryfs");
@ -52,28 +53,34 @@ public:
ON_CALL(*console, askPassword(testing::StrEq("Password: "))).WillByDefault(testing::Return("pass"));
ON_CALL(*console, askPassword(testing::StrEq("Confirm Password: "))).WillByDefault(testing::Return("pass"));
// Run Cryfs
return cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data(), _httpClient());
return cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data(), _httpClient(), std::move(onMounted));
}
void EXPECT_EXIT_WITH_HELP_MESSAGE(const std::vector<std::string>& args, const std::string &message, cryfs::ErrorCode errorCode) {
EXPECT_RUN_ERROR(args, "Usage:[^\\x00]*"+message, errorCode);
}
void EXPECT_RUN_ERROR(const std::vector<std::string>& args, const std::string& message, cryfs::ErrorCode errorCode) {
FilesystemOutput filesystem_output = _run_filesystem(args, boost::none);
void EXPECT_RUN_ERROR(const std::vector<std::string>& args, const std::string& message, cryfs::ErrorCode errorCode, std::function<void ()> onMounted = [] {}) {
FilesystemOutput filesystem_output = run_filesystem(args, boost::none, std::move(onMounted));
EXPECT_EQ(exitCode(errorCode), filesystem_output.exit_code);
EXPECT_TRUE(std::regex_search(filesystem_output.stderr_, std::regex(message)));
if (!std::regex_search(filesystem_output.stderr_, std::regex(message))) {
std::cerr << filesystem_output.stderr_ << std::endl;
EXPECT_TRUE(false);
}
}
void EXPECT_RUN_SUCCESS(const std::vector<std::string>& args, const boost::filesystem::path &mountDir) {
void EXPECT_RUN_SUCCESS(const std::vector<std::string>& args, const boost::optional<boost::filesystem::path> &mountDir, std::function<void ()> onMounted = [] {}) {
//TODO Make this work when run in background
ASSERT(std::find(args.begin(), args.end(), string("-f")) != args.end(), "Currently only works if run in foreground");
FilesystemOutput filesystem_output = _run_filesystem(args, mountDir);
FilesystemOutput filesystem_output = run_filesystem(args, mountDir, std::move(onMounted));
EXPECT_EQ(0, filesystem_output.exit_code);
EXPECT_TRUE(std::regex_search(filesystem_output.stdout_, std::regex("Mounting filesystem")));
if (!std::regex_search(filesystem_output.stdout_, std::regex("Mounting filesystem"))) {
std::cerr << filesystem_output.stdout_ << std::endl;
EXPECT_TRUE(false);
}
}
struct FilesystemOutput final {
@ -82,57 +89,77 @@ public:
std::string stderr_;
};
FilesystemOutput _run_filesystem(const std::vector<std::string>& args, const boost::optional<boost::filesystem::path>& mountDirForUnmounting) {
testing::internal::CaptureStdout();
testing::internal::CaptureStderr();
std::future<int> exit_code = std::async(std::launch::async, [this, &args] {
return run(args);
});
if (mountDirForUnmounting.is_initialized()) {
boost::filesystem::path mountDir = *mountDirForUnmounting;
std::future<bool> unmount_success = std::async(std::launch::async, [&mountDir] {
static void _unmount(const boost::filesystem::path &mountDir) {
int returncode = -1;
while (returncode != 0) {
#if defined(__APPLE__)
returncode = cpputils::Subprocess::call(std::string("umount ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
#elif defined(_MSC_VER)
// Somehow this sleeping is needed to not deadlock. Race condition in mounting/unmounting?
std::this_thread::sleep_for(std::chrono::milliseconds(50));
std::wstring mountDir_ = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(mountDir.string());
BOOL success = DokanRemoveMountPoint(mountDir_.c_str());
returncode = success ? 0 : -1;
#else
returncode = cpputils::Subprocess::call(std::string("fusermount -u ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
returncode = cpputils::Subprocess::call(
std::string("fusermount -u ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
#endif
EXPECT_EQ(0, returncode);
}
FilesystemOutput run_filesystem(const std::vector<std::string>& args, boost::optional<boost::filesystem::path> mountDirForUnmounting, std::function<void()> onMounted) {
testing::internal::CaptureStdout();
testing::internal::CaptureStderr();
bool exited = false;
cpputils::ConditionBarrier isMountedOrFailedBarrier;
std::future<int> exit_code = std::async(std::launch::async, [&] {
int exit_code = run(args, [&] { isMountedOrFailedBarrier.release(); });
// just in case it fails, we also want to release the barrier.
// if it succeeds, this will release it a second time, which doesn't hurt.
exited = true;
isMountedOrFailedBarrier.release();
return exit_code;
});
std::future<bool> on_mounted_success = std::async(std::launch::async, [&] {
isMountedOrFailedBarrier.wait();
if (exited) {
// file system already exited on its own, this indicates an error. It should have stayed mounted.
// while the exit_code from run() will signal an error in this case, we didn't encounter another
// error in the onMounted future, so return true here.
return true;
}
// now we know the filesystem stayed online, so we can call the onMounted callback
onMounted();
// and unmount it afterwards
if (mountDirForUnmounting.is_initialized()) {
_unmount(*mountDirForUnmounting);
}
return true;
});
if(std::future_status::ready != unmount_success.wait_for(std::chrono::seconds(10))) {
if(std::future_status::ready != on_mounted_success.wait_for(std::chrono::seconds(1000))) {
testing::internal::GetCapturedStdout(); // stop capturing stdout
testing::internal::GetCapturedStderr(); // stop capturing stderr
std::cerr << "Unmount thread didn't finish";
std::cerr << "onMounted thread (e.g. used for unmount) didn't finish" << std::endl;
// The std::future destructor of a future created with std::async blocks until the future is ready.
// so, instead of causing a deadlock, rather abort
exit(EXIT_FAILURE);
}
EXPECT_TRUE(unmount_success.get()); // this also re-throws any potential exceptions
}
EXPECT_TRUE(on_mounted_success.get()); // this also re-throws any potential exceptions
if(std::future_status::ready != exit_code.wait_for(std::chrono::seconds(10))) {
if(std::future_status::ready != exit_code.wait_for(std::chrono::seconds(1000))) {
testing::internal::GetCapturedStdout(); // stop capturing stdout
testing::internal::GetCapturedStderr(); // stop capturing stderr
std::cerr << "Filesystem thread didn't finish";
std::cerr << "Filesystem thread didn't finish" << std::endl;
// The std::future destructor of a future created with std::async blocks until the future is ready.
// so, instead of causing a deadlock, rather abort
exit(EXIT_FAILURE);
}
return {
exit_code.get(),
exit_code.get(), // this also re-throws any potential exceptions
testing::internal::GetCapturedStdout(),
testing::internal::GetCapturedStderr()
};

View File

@ -15,6 +15,7 @@ set(SOURCES
config/CryConfigLoaderTest.cpp
config/CryConfigConsoleTest.cpp
config/CryPasswordBasedKeyProviderTest.cpp
config/CryPresetPasswordBasedKeyProviderTest.cpp
filesystem/CryFsTest.cpp
filesystem/CryNodeTest.cpp
filesystem/FileSystemTest.cpp

View File

@ -7,7 +7,7 @@
#include <cpp-utils/crypto/symmetric/ciphers.h>
#include <cpp-utils/tempfile/TempFile.h>
#include <cryfs/config/CryConfigFile.h>
#include <cryfs/config/CryPasswordBasedKeyProvider.h>
#include <cryfs/config/CryPresetPasswordBasedKeyProvider.h>
#include "../testutils/MockConsole.h"
using cpputils::Data;
@ -16,7 +16,6 @@ using cpputils::Serpent128_CFB;
using cpputils::TempFile;
using cpputils::make_unique_ref;
using cpputils::SCrypt;
using std::make_shared;
using namespace cryfs;
// Test that config files created with (old) versions of cryfs are still loadable.
@ -28,12 +27,7 @@ public:
CryConfigFile loadConfigFromHex(const string &configFileContentHex) {
storeHexToFile(configFileContentHex);
CryPasswordBasedKeyProvider keyProvider(
make_shared<MockConsole>(),
[] () {return "mypassword"; },
[] () {return "mypassword"; },
make_unique_ref<SCrypt>(SCrypt::DefaultSettings)
);
CryPresetPasswordBasedKeyProvider keyProvider("mypassword", make_unique_ref<SCrypt>(SCrypt::DefaultSettings));
return CryConfigFile::load(file.path(), &keyProvider).value();
}

View File

@ -1,6 +1,6 @@
#include <gtest/gtest.h>
#include <cryfs/config/CryConfigLoader.h>
#include <cryfs/config/CryPasswordBasedKeyProvider.h>
#include <cryfs/config/CryPresetPasswordBasedKeyProvider.h>
#include "../testutils/MockConsole.h"
#include "../testutils/TestWithFakeHomeDirectory.h"
#include <cpp-utils/tempfile/TempFile.h>
@ -20,7 +20,7 @@ using cpputils::NoninteractiveConsole;
using cpputils::make_unique_ref;
using cpputils::Console;
using cpputils::unique_ref;
using cryfs::CryPasswordBasedKeyProvider;
using cryfs::CryPresetPasswordBasedKeyProvider;
using boost::optional;
using boost::none;
using std::string;
@ -63,13 +63,7 @@ private:
class CryConfigLoaderTest: public ::testing::Test, public TestWithMockConsole, TestWithFakeHomeDirectory {
public:
unique_ref<CryKeyProvider> keyProvider(const string& password) {
auto askPassword = [password] { return password;};
return make_unique_ref<CryPasswordBasedKeyProvider>(
console,
askPassword,
askPassword,
make_unique_ref<SCrypt>(SCrypt::TestSettings)
);
return make_unique_ref<CryPresetPasswordBasedKeyProvider>(password, make_unique_ref<SCrypt>(SCrypt::TestSettings));
}
CryConfigLoaderTest(): file(false), tempLocalStateDir(), localStateDir(tempLocalStateDir.path()) {

View File

@ -0,0 +1,56 @@
#include <cryfs/config/CryPresetPasswordBasedKeyProvider.h>
#include <gmock/gmock.h>
#include "../testutils/MockConsole.h"
#include <cpp-utils/data/DataFixture.h>
using cpputils::make_unique_ref;
using cpputils::EncryptionKey;
using cpputils::PasswordBasedKDF;
using cpputils::Data;
using cpputils::DataFixture;
using std::string;
using cryfs::CryPresetPasswordBasedKeyProvider;
using testing::Invoke;
using testing::Eq;
using testing::StrEq;
using testing::_;
class MockKDF : public PasswordBasedKDF {
public:
MOCK_METHOD3(deriveExistingKey, EncryptionKey(size_t keySize, const string& password, const Data& kdfParameters));
MOCK_METHOD2(deriveNewKey, KeyResult(size_t keySize, const string& password));
};
TEST(CryPresetPasswordBasedKeyProviderTest, requestKeyForNewFilesystem) {
constexpr size_t keySize = 512;
constexpr const char* password = "mypassword";
const EncryptionKey key = EncryptionKey::FromString(DataFixture::generate(keySize).ToString());
auto kdf = make_unique_ref<MockKDF>();
const Data kdfParameters = DataFixture::generate(100);
EXPECT_CALL(*kdf, deriveNewKey(Eq(keySize), StrEq(password))).Times(1).WillOnce(Invoke([&] (auto, auto) {return PasswordBasedKDF::KeyResult{key, kdfParameters.copy()};}));
CryPresetPasswordBasedKeyProvider keyProvider(password, std::move(kdf));
auto returned_key = keyProvider.requestKeyForNewFilesystem(keySize);
EXPECT_EQ(key.ToString(), returned_key.key.ToString());
EXPECT_EQ(kdfParameters, returned_key.kdfParameters);
}
TEST(CryPresetPasswordBasedKeyProviderTest, requestKeyForExistingFilesystem) {
constexpr size_t keySize = 512;
constexpr const char* password = "mypassword";
const EncryptionKey key = EncryptionKey::FromString(DataFixture::generate(keySize).ToString());
auto kdf = make_unique_ref<MockKDF>();
const Data kdfParameters = DataFixture::generate(100);
EXPECT_CALL(*kdf, deriveExistingKey(Eq(keySize), StrEq(password), _)).Times(1).WillOnce(Invoke([&] (auto, auto, const auto& kdfParams) {
EXPECT_EQ(kdfParameters, kdfParams);
return key;
}));
CryPresetPasswordBasedKeyProvider keyProvider(password, std::move(kdf));
EncryptionKey returned_key = keyProvider.requestKeyForExistingFilesystem(keySize, kdfParameters);
EXPECT_EQ(key.ToString(), returned_key.ToString());
}

View File

@ -9,7 +9,7 @@
#include <cryfs/filesystem/CryOpenFile.h>
#include "../testutils/MockConsole.h"
#include <cryfs/config/CryConfigLoader.h>
#include <cryfs/config/CryPasswordBasedKeyProvider.h>
#include <cryfs/config/CryPresetPasswordBasedKeyProvider.h>
#include <cpp-utils/system/homedir.h>
#include "../testutils/TestWithFakeHomeDirectory.h"
#include <cpp-utils/io/NoninteractiveConsole.h>
@ -28,24 +28,20 @@ using cpputils::Data;
using cpputils::NoninteractiveConsole;
using blockstore::ondisk::OnDiskBlockStore2;
using boost::none;
using cryfs::CryPasswordBasedKeyProvider;
using cryfs::CryPresetPasswordBasedKeyProvider;
namespace bf = boost::filesystem;
using namespace cryfs;
namespace {
class CryFsTest: public Test, public TestWithMockConsole, public TestWithFakeHomeDirectory {
public:
CryFsTest(): tempLocalStateDir(), localStateDir(tempLocalStateDir.path()), rootdir(), config(false) {
}
CryConfigFile loadOrCreateConfig() {
auto askPassword = [] {return "mypassword";};
auto keyProvider = make_unique_ref<CryPasswordBasedKeyProvider>(
make_shared<MockConsole>(),
askPassword,
askPassword,
make_unique_ref<SCrypt>(SCrypt::TestSettings)
);
auto keyProvider = make_unique_ref<CryPresetPasswordBasedKeyProvider>("mypassword", make_unique_ref<SCrypt>(SCrypt::TestSettings));
return CryConfigLoader(make_shared<NoninteractiveConsole>(mockConsole()), Random::PseudoRandom(), std::move(keyProvider), localStateDir, none, none, none).loadOrCreate(config.path(), false, false).value().configFile;
}
@ -59,23 +55,31 @@ public:
TempFile config;
};
auto failOnIntegrityViolation() {
return [] {
EXPECT_TRUE(false);
};
}
TEST_F(CryFsTest, CreatedRootdirIsLoadableAfterClosing) {
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false);
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
}
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false);
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
auto rootDir = dev.LoadDir(bf::path("/"));
rootDir.value()->children();
}
TEST_F(CryFsTest, LoadingFilesystemDoesntModifyConfigFile) {
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false);
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
}
Data configAfterCreating = Data::LoadFromFile(config.path()).value();
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false);
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
}
Data configAfterLoading = Data::LoadFromFile(config.path()).value();
EXPECT_EQ(configAfterCreating, configAfterLoading);
}
}

View File

@ -4,7 +4,7 @@
#include <cpp-utils/io/NoninteractiveConsole.h>
#include <cryfs/filesystem/CryDevice.h>
#include <cryfs/config/CryConfigLoader.h>
#include <cryfs/config/CryPasswordBasedKeyProvider.h>
#include <cryfs/config/CryPresetPasswordBasedKeyProvider.h>
#include "../testutils/MockConsole.h"
#include "../testutils/TestWithFakeHomeDirectory.h"
@ -17,10 +17,18 @@ using fspp::Device;
using boost::none;
using std::make_shared;
using blockstore::inmemory::InMemoryBlockStore2;
using cryfs::CryPasswordBasedKeyProvider;
using cryfs::CryPresetPasswordBasedKeyProvider;
using namespace cryfs;
namespace {
auto failOnIntegrityViolation() {
return [] {
EXPECT_TRUE(false);
};
}
class CryFsTestFixture: public FileSystemTestFixture, public TestWithMockConsole, public TestWithFakeHomeDirectory {
public:
CryFsTestFixture()
@ -29,17 +37,11 @@ public:
unique_ref<Device> createDevice() override {
auto blockStore = cpputils::make_unique_ref<InMemoryBlockStore2>();
auto askPassword = [] {return "mypassword";};
auto _console = make_shared<NoninteractiveConsole>(mockConsole());
auto keyProvider = make_unique_ref<CryPasswordBasedKeyProvider>(
_console,
askPassword,
askPassword,
make_unique_ref<SCrypt>(SCrypt::TestSettings)
);
auto keyProvider = make_unique_ref<CryPresetPasswordBasedKeyProvider>("mypassword", make_unique_ref<SCrypt>(SCrypt::TestSettings));
auto config = CryConfigLoader(_console, Random::PseudoRandom(), std::move(keyProvider), localStateDir, none, none, none)
.loadOrCreate(configFile.path(), false, false).value();
return make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), localStateDir, config.myClientId, false, false);
return make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), localStateDir, config.myClientId, false, false, failOnIntegrityViolation());
}
cpputils::TempDir tempLocalStateDir;
@ -48,3 +50,4 @@ public:
};
FSPP_ADD_FILESYTEM_TESTS(CryFS, CryFsTestFixture);
}

View File

@ -2,18 +2,24 @@
#define MESSMER_CRYFS_TEST_CRYFS_FILESYSTEM_CRYTESTBASE_H
#include <cryfs/filesystem/CryDevice.h>
#include <cryfs/config/CryPasswordBasedKeyProvider.h>
#include <cryfs/config/CryPresetPasswordBasedKeyProvider.h>
#include <blockstore/implementations/inmemory/InMemoryBlockStore2.h>
#include <cpp-utils/tempfile/TempFile.h>
#include <cpp-utils/crypto/kdf/Scrypt.h>
#include "../../testutils/TestWithFakeHomeDirectory.h"
#include "../../testutils/MockConsole.h"
inline auto failOnIntegrityViolation() {
return [] {
EXPECT_TRUE(false);
};
}
class CryTestBase : public TestWithFakeHomeDirectory {
public:
CryTestBase(): _tempLocalStateDir(), _localStateDir(_tempLocalStateDir.path()), _configFile(false), _device(nullptr) {
auto fakeBlockStore = cpputils::make_unique_ref<blockstore::inmemory::InMemoryBlockStore2>();
_device = std::make_unique<cryfs::CryDevice>(configFile(), std::move(fakeBlockStore), _localStateDir, 0x12345678, false, false);
_device = std::make_unique<cryfs::CryDevice>(configFile(), std::move(fakeBlockStore), _localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
}
cryfs::CryConfigFile configFile() {
@ -21,12 +27,7 @@ public:
config.SetCipher("aes-256-gcm");
config.SetEncryptionKey(cpputils::AES256_GCM::EncryptionKey::CreateKey(cpputils::Random::PseudoRandom(), cpputils::AES256_GCM::KEYSIZE).ToString());
config.SetBlocksizeBytes(10240);
cryfs::CryPasswordBasedKeyProvider keyProvider(
std::make_shared<MockConsole>(),
[] () {return "mypassword";},
[] () {return "mypassword";},
cpputils::make_unique_ref<cpputils::SCrypt>(cpputils::SCrypt::TestSettings)
);
cryfs::CryPresetPasswordBasedKeyProvider keyProvider("mypassword", cpputils::make_unique_ref<cpputils::SCrypt>(cpputils::SCrypt::TestSettings));
return cryfs::CryConfigFile::create(_configFile.path(), std::move(config), &keyProvider);
}

View File

@ -17,7 +17,7 @@ INSTANTIATE_TEST_CASE_P(FuseAccessErrorTest, FuseAccessErrorTest, Values(EACCES,
TEST_P(FuseAccessErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, access(StrEq(FILENAME), _))
EXPECT_CALL(*fsimpl, access(StrEq(FILENAME), _))
.Times(AtLeast(1)).WillRepeatedly(Throw(FuseErrnoException(GetParam())));
int error = AccessFileReturnError(FILENAME, 0);

View File

@ -9,7 +9,7 @@ class FuseAccessFilenameTest: public FuseAccessTest {
TEST_F(FuseAccessFilenameTest, AccessFile) {
ReturnIsFileOnLstat("/myfile");
EXPECT_CALL(fsimpl, access(StrEq("/myfile"), _))
EXPECT_CALL(*fsimpl, access(StrEq("/myfile"), _))
.Times(1).WillOnce(Return());
AccessFile("/myfile", 0);
@ -18,7 +18,7 @@ TEST_F(FuseAccessFilenameTest, AccessFile) {
TEST_F(FuseAccessFilenameTest, AccessFileNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsFileOnLstat("/mydir/myfile");
EXPECT_CALL(fsimpl, access(StrEq("/mydir/myfile"), _))
EXPECT_CALL(*fsimpl, access(StrEq("/mydir/myfile"), _))
.Times(1).WillOnce(Return());
AccessFile("/mydir/myfile", 0);
@ -28,7 +28,7 @@ TEST_F(FuseAccessFilenameTest, AccessFileNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnIsFileOnLstat("/mydir/mydir2/myfile");
EXPECT_CALL(fsimpl, access(StrEq("/mydir/mydir2/myfile"), _))
EXPECT_CALL(*fsimpl, access(StrEq("/mydir/mydir2/myfile"), _))
.Times(1).WillOnce(Return());
AccessFile("/mydir/mydir2/myfile", 0);

View File

@ -12,7 +12,7 @@ INSTANTIATE_TEST_CASE_P(FuseAccessModeTest, FuseAccessModeTest, Values(0, F_OK,
TEST_P(FuseAccessModeTest, AccessFile) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, access(StrEq(FILENAME), GetParam()))
EXPECT_CALL(*fsimpl, access(StrEq(FILENAME), GetParam()))
.Times(1).WillOnce(Return());
AccessFile(FILENAME, GetParam());

View File

@ -79,11 +79,11 @@ INSTANTIATE_TEST_CASE_P(FuseCloseTest, FuseCloseTest, Values(0, 1, 2, 100, 1024*
Barrier barrier;
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
EXPECT_CALL(*fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
{
//InSequence fileCloseSequence;
EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1);
EXPECT_CALL(fsimpl, closeFile(Eq(GetParam()))).Times(1).WillOnce(Invoke([&barrier] (int) {
EXPECT_CALL(*fsimpl, flush(Eq(GetParam()))).Times(1);
EXPECT_CALL(*fsimpl, closeFile(Eq(GetParam()))).Times(1).WillOnce(Invoke([&barrier] (int) {
// Release the waiting lock at the end of this test case, because the fuse release() came in now.
barrier.Release();
}));

View File

@ -17,7 +17,7 @@ INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenErrorTest, FuseCreateAndOpenErrorTest,
TEST_F(FuseCreateAndOpenErrorTest, ReturnNoError) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Return(1));
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Return(1));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(1);
@ -27,7 +27,7 @@ TEST_F(FuseCreateAndOpenErrorTest, ReturnNoError) {
TEST_P(FuseCreateAndOpenErrorTest, ReturnError) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = CreateAndOpenFileReturnError(FILENAME, O_RDONLY);
EXPECT_EQ(GetParam(), error);

View File

@ -34,9 +34,9 @@ INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenFileDescriptorTest, FuseCreateAndOpenFi
TEST_P(FuseCreateAndOpenFileDescriptorTest, TestReturnedFileDescriptor) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _))
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq(FILENAME), _, _, _))
.Times(1).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(fspp::num_bytes_t(1)));
EXPECT_CALL(*fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(fspp::num_bytes_t(1)));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstatWithSize(GetParam(), fspp::num_bytes_t(1));

View File

@ -10,7 +10,7 @@ public:
TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFile) {
ReturnDoesntExistOnLstat("/myfile");
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/myfile"), _, _, _))
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq("/myfile"), _, _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);
@ -21,7 +21,7 @@ TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFile) {
TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFileNested) {
ReturnIsDirOnLstat("/mydir");
ReturnDoesntExistOnLstat("/mydir/myfile");
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/mydir/myfile"), _, _, _))
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq("/mydir/myfile"), _, _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);
@ -33,7 +33,7 @@ TEST_F(FuseCreateAndOpenFilenameTest, CreateAndOpenFileNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnDoesntExistOnLstat("/mydir/mydir2/myfile");
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq("/mydir/mydir2/myfile"), _, _, _))
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq("/mydir/mydir2/myfile"), _, _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);

View File

@ -10,7 +10,7 @@ INSTANTIATE_TEST_CASE_P(FuseCreateAndOpenFlagsTest, FuseCreateAndOpenFlagsTest,
//TODO Disabled because it doesn't seem to work. Fuse doesn't seem to pass flags to create(). Why?
/*TEST_P(FuseCreateAndOpenFlagsTest, testFlags) {
ReturnDoesntExistOnLstat(FILENAME);
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(FILENAME), OpenFlagsEq(GetParam()), _, _))
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq(FILENAME), OpenFlagsEq(GetParam()), _, _))
.Times(1).WillOnce(Return(0));
//For the syscall to succeed, we also need to give an fstat implementation.
ReturnIsFileOnFstat(0);

View File

@ -15,7 +15,7 @@ INSTANTIATE_TEST_CASE_P(FuseFdatasyncErrorTest, FuseFdatasyncErrorTest, Values(E
TEST_P(FuseFdatasyncErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, fdatasync(0))
EXPECT_CALL(*fsimpl, fdatasync(0))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = FdatasyncFileReturnError(FILENAME);

View File

@ -17,7 +17,7 @@ INSTANTIATE_TEST_CASE_P(FuseFdatasyncFileDescriptorTest, FuseFdatasyncFileDescri
TEST_P(FuseFdatasyncFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, fdatasync(Eq(GetParam())))
EXPECT_CALL(*fsimpl, fdatasync(Eq(GetParam())))
.Times(1).WillOnce(Return());
FdatasyncFile(FILENAME);

View File

@ -19,8 +19,8 @@ INSTANTIATE_TEST_CASE_P(FuseFlushErrorTest, FuseFlushErrorTest, Values(EBADF, EI
TEST_P(FuseFlushErrorTest, ReturnErrorFromFlush) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
EXPECT_CALL(*fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
EXPECT_CALL(*fsimpl, flush(Eq(GetParam()))).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
auto fs = TestFS();
auto fd = OpenFile(fs.get(), FILENAME);

View File

@ -27,8 +27,8 @@ INSTANTIATE_TEST_CASE_P(FuseFlushFileDescriptorTest, FuseFlushFileDescriptorTest
TEST_P(FuseFlushFileDescriptorTest, FlushOnCloseFile) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, flush(Eq(GetParam()))).Times(1);
EXPECT_CALL(*fsimpl, openFile(StrEq(FILENAME), _)).WillOnce(Return(GetParam()));
EXPECT_CALL(*fsimpl, flush(Eq(GetParam()))).Times(1);
OpenAndCloseFile(FILENAME);
}

View File

@ -29,7 +29,7 @@ TEST_P(FuseFstatErrorTest, ReturnedErrorCodeIsCorrect) {
ReturnDoesntExistOnLstat(FILENAME);
OnCreateAndOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, fstat(Eq(0), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
EXPECT_CALL(*fsimpl, fstat(Eq(0), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
auto fs = TestFS();

View File

@ -28,7 +28,7 @@ TEST_P(FuseFstatParameterTest, FileDescriptorIsCorrect) {
ReturnDoesntExistOnLstat(FILENAME);
OnCreateAndOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, fstat(Eq(GetParam()), _)).Times(1).WillOnce(ReturnIsFileFstat);
EXPECT_CALL(*fsimpl, fstat(Eq(GetParam()), _)).Times(1).WillOnce(ReturnIsFileFstat);
CallFstat(FILENAME);
}

View File

@ -25,5 +25,5 @@ unique_ref<OpenFileHandle> FuseFstatTest::CreateFileAllowErrors(const TempTestFS
}
void FuseFstatTest::OnCreateAndOpenReturnFileDescriptor(const char *filename, int descriptor) {
EXPECT_CALL(fsimpl, createAndOpenFile(StrEq(filename), _, _, _)).Times(1).WillOnce(Return(descriptor));
EXPECT_CALL(*fsimpl, createAndOpenFile(StrEq(filename), _, _, _)).Times(1).WillOnce(Return(descriptor));
}

View File

@ -15,7 +15,7 @@ INSTANTIATE_TEST_CASE_P(FuseFsyncErrorTest, FuseFsyncErrorTest, Values(EBADF, EI
TEST_P(FuseFsyncErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, fsync(0))
EXPECT_CALL(*fsimpl, fsync(0))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = FsyncFileReturnError(FILENAME);

View File

@ -17,7 +17,7 @@ INSTANTIATE_TEST_CASE_P(FuseFsyncFileDescriptorTest, FuseFsyncFileDescriptorTest
TEST_P(FuseFsyncFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, fsync(Eq(GetParam())))
EXPECT_CALL(*fsimpl, fsync(Eq(GetParam())))
.Times(1).WillOnce(Return());
FsyncFile(FILENAME);

View File

@ -16,7 +16,7 @@ INSTANTIATE_TEST_CASE_P(FuseFTruncateErrorTest, FuseFTruncateErrorTest, Values(E
TEST_P(FuseFTruncateErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, ftruncate(0, _))
EXPECT_CALL(*fsimpl, ftruncate(0, _))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
//Needed to make ::ftruncate system call return successfully
ReturnIsFileOnFstat(0);

View File

@ -18,7 +18,7 @@ INSTANTIATE_TEST_CASE_P(FuseFTruncateFileDescriptorTest, FuseFTruncateFileDescri
TEST_P(FuseFTruncateFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, ftruncate(Eq(GetParam()), _))
EXPECT_CALL(*fsimpl, ftruncate(Eq(GetParam()), _))
.Times(1).WillOnce(Return());
//Needed to make ::ftruncate system call return successfully
ReturnIsFileOnFstat(GetParam());

View File

@ -18,7 +18,7 @@ INSTANTIATE_TEST_CASE_P(FuseFTruncateSizeTest, FuseFTruncateSizeTest, Values(
TEST_P(FuseFTruncateSizeTest, FTruncateFile) {
ReturnIsFileOnLstat(FILENAME);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, ftruncate(Eq(0), GetParam()))
EXPECT_CALL(*fsimpl, ftruncate(Eq(0), GetParam()))
.Times(1).WillOnce(Return());
//Needed to make ::ftruncate system call return successfully
ReturnIsFileOnFstat(0);

View File

@ -17,14 +17,14 @@ public:
INSTANTIATE_TEST_CASE_P(LstatErrorCodes, FuseLstatErrorTest, Values(EACCES, EBADF, EFAULT, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOTDIR, EOVERFLOW, EINVAL, ENOTDIR));
TEST_F(FuseLstatErrorTest, ReturnNoError) {
EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsFile);
EXPECT_CALL(*fsimpl, lstat(StrEq(FILENAME), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsFile);
errno = 0;
int error = LstatPathReturnError(FILENAME);
EXPECT_EQ(0, error);
}
TEST_P(FuseLstatErrorTest, ReturnError) {
EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).Times(AtLeast(1)).WillRepeatedly(Throw(FuseErrnoException(GetParam())));
EXPECT_CALL(*fsimpl, lstat(StrEq(FILENAME), _)).Times(AtLeast(1)).WillRepeatedly(Throw(FuseErrnoException(GetParam())));
int error = LstatPathReturnError(FILENAME);
EXPECT_EQ(GetParam(), error);
}

View File

@ -8,30 +8,30 @@ class FuseLstatPathParameterTest: public FuseLstatTest {
};
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectRoot) {
EXPECT_CALL(fsimpl, lstat(StrEq("/"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsDir);
EXPECT_CALL(*fsimpl, lstat(StrEq("/"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsDir);
LstatPath("/");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectSimpleFile) {
EXPECT_CALL(fsimpl, lstat(StrEq("/myfile"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsFile);
EXPECT_CALL(*fsimpl, lstat(StrEq("/myfile"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsFile);
LstatPath("/myfile");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectSimpleDir) {
EXPECT_CALL(fsimpl, lstat(StrEq("/mydir"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsDir);
EXPECT_CALL(*fsimpl, lstat(StrEq("/mydir"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsDir);
LstatPath("/mydir/");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectNestedFile) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
EXPECT_CALL(fsimpl, lstat(StrEq("/mydir/mydir2/myfile"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsFile);
EXPECT_CALL(*fsimpl, lstat(StrEq("/mydir/mydir2/myfile"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsFile);
LstatPath("/mydir/mydir2/myfile");
}
TEST_F(FuseLstatPathParameterTest, PathParameterIsCorrectNestedDir) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
EXPECT_CALL(fsimpl, lstat(StrEq("/mydir/mydir2/mydir3"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsDir);
EXPECT_CALL(*fsimpl, lstat(StrEq("/mydir/mydir2/mydir3"), _)).Times(AtLeast(1)).WillRepeatedly(ReturnIsDir);
LstatPath("/mydir/mydir2/mydir3/");
}

View File

@ -41,7 +41,7 @@ fspp::fuse::STAT FuseLstatTest::CallDirLstatWithImpl(function<void(fspp::fuse::S
}
fspp::fuse::STAT FuseLstatTest::CallLstatWithImpl(function<void(fspp::fuse::STAT*)> implementation) {
EXPECT_CALL(fsimpl, lstat(StrEq(FILENAME), _)).WillRepeatedly(Invoke([implementation](const char*, fspp::fuse::STAT *stat) {
EXPECT_CALL(*fsimpl, lstat(StrEq(FILENAME), _)).WillRepeatedly(Invoke([implementation](const char*, fspp::fuse::STAT *stat) {
implementation(stat);
}));

View File

@ -8,7 +8,7 @@ class FuseMkdirDirnameTest: public FuseMkdirTest {
TEST_F(FuseMkdirDirnameTest, Mkdir) {
ReturnDoesntExistOnLstat("/mydir");
EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir"), _, _, _))
EXPECT_CALL(*fsimpl, mkdir(StrEq("/mydir"), _, _, _))
// After mkdir was called, lstat should return that it is a dir.
// This is needed to make the ::mkdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
@ -19,7 +19,7 @@ TEST_F(FuseMkdirDirnameTest, Mkdir) {
TEST_F(FuseMkdirDirnameTest, MkdirNested) {
ReturnIsDirOnLstat("/mydir");
ReturnDoesntExistOnLstat("/mydir/mysubdir");
EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir/mysubdir"), _, _, _))
EXPECT_CALL(*fsimpl, mkdir(StrEq("/mydir/mysubdir"), _, _, _))
// After mkdir was called, lstat should return that it is a dir.
// This is needed to make the ::mkdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
@ -31,7 +31,7 @@ TEST_F(FuseMkdirDirnameTest, MkdirNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnDoesntExistOnLstat("/mydir/mydir2/mydir3");
EXPECT_CALL(fsimpl, mkdir(StrEq("/mydir/mydir2/mydir3"), _, _, _))
EXPECT_CALL(*fsimpl, mkdir(StrEq("/mydir/mydir2/mydir3"), _, _, _))
// After mkdir was called, lstat should return that it is a dir.
// This is needed to make the ::mkdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());

View File

@ -16,7 +16,7 @@ INSTANTIATE_TEST_CASE_P(FuseMkdirErrorTest, FuseMkdirErrorTest, Values(EACCES, E
TEST_F(FuseMkdirErrorTest, NoError) {
ReturnDoesntExistOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), _, _, _))
EXPECT_CALL(*fsimpl, mkdir(StrEq(DIRNAME), _, _, _))
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
int error = MkdirReturnError(DIRNAME, 0);
@ -25,7 +25,7 @@ TEST_F(FuseMkdirErrorTest, NoError) {
TEST_P(FuseMkdirErrorTest, ReturnedErrorIsCorrect) {
ReturnDoesntExistOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), _, _, _))
EXPECT_CALL(*fsimpl, mkdir(StrEq(DIRNAME), _, _, _))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = MkdirReturnError(DIRNAME, 0);

View File

@ -12,7 +12,7 @@ INSTANTIATE_TEST_CASE_P(FuseMkdirModeTest, FuseMkdirModeTest, Values(0, S_IRUSR,
TEST_P(FuseMkdirModeTest, Mkdir) {
ReturnDoesntExistOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, mkdir(StrEq(DIRNAME), GetParam(), _, _))
EXPECT_CALL(*fsimpl, mkdir(StrEq(DIRNAME), GetParam(), _, _))
.Times(1).WillOnce(FromNowOnReturnIsDirOnLstat());
Mkdir(DIRNAME, GetParam());

View File

@ -17,7 +17,7 @@ INSTANTIATE_TEST_CASE_P(FuseOpenErrorTest, FuseOpenErrorTest, Values(EACCES, EDQ
TEST_F(FuseOpenErrorTest, ReturnNoError) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Return(1));
EXPECT_CALL(*fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Return(1));
errno = 0;
int error = OpenFileReturnError(FILENAME, O_RDONLY);
EXPECT_EQ(0, error);
@ -25,7 +25,7 @@ TEST_F(FuseOpenErrorTest, ReturnNoError) {
TEST_P(FuseOpenErrorTest, ReturnError) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
EXPECT_CALL(*fsimpl, openFile(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = OpenFileReturnError(FILENAME, O_RDONLY);
EXPECT_EQ(GetParam(), error);
}

View File

@ -34,9 +34,9 @@ INSTANTIATE_TEST_CASE_P(FuseOpenFileDescriptorTest, FuseOpenFileDescriptorTest,
TEST_P(FuseOpenFileDescriptorTest, TestReturnedFileDescriptor) {
ReturnIsFileOnLstatWithSize(FILENAME, fspp::num_bytes_t(1));
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), _))
EXPECT_CALL(*fsimpl, openFile(StrEq(FILENAME), _))
.Times(1).WillOnce(Return(GetParam()));
EXPECT_CALL(fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(fspp::num_bytes_t(1)));
EXPECT_CALL(*fsimpl, read(GetParam(), _, _, _)).Times(1).WillOnce(Return(fspp::num_bytes_t(1)));
OpenAndReadFile(FILENAME);
}

View File

@ -10,7 +10,7 @@ public:
TEST_F(FuseOpenFilenameTest, OpenFile) {
ReturnIsFileOnLstat("/myfile");
EXPECT_CALL(fsimpl, openFile(StrEq("/myfile"), _))
EXPECT_CALL(*fsimpl, openFile(StrEq("/myfile"), _))
.Times(1).WillOnce(Return(0));
OpenFile("/myfile", O_RDONLY);
@ -19,7 +19,7 @@ TEST_F(FuseOpenFilenameTest, OpenFile) {
TEST_F(FuseOpenFilenameTest, OpenFileNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsFileOnLstat("/mydir/myfile");
EXPECT_CALL(fsimpl, openFile(StrEq("/mydir/myfile"), _))
EXPECT_CALL(*fsimpl, openFile(StrEq("/mydir/myfile"), _))
.Times(1).WillOnce(Return(0));
OpenFile("/mydir/myfile", O_RDONLY);
@ -29,7 +29,7 @@ TEST_F(FuseOpenFilenameTest, OpenFileNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnIsFileOnLstat("/mydir/mydir2/myfile");
EXPECT_CALL(fsimpl, openFile(StrEq("/mydir/mydir2/myfile"), _))
EXPECT_CALL(*fsimpl, openFile(StrEq("/mydir/mydir2/myfile"), _))
.Times(1).WillOnce(Return(0));
OpenFile("/mydir/mydir2/myfile", O_RDONLY);

View File

@ -11,7 +11,7 @@ INSTANTIATE_TEST_CASE_P(FuseOpenFlagsTest, FuseOpenFlagsTest, Values(O_RDWR, O_R
TEST_P(FuseOpenFlagsTest, testFlags) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, openFile(StrEq(FILENAME), OpenFlagsEq(GetParam())))
EXPECT_CALL(*fsimpl, openFile(StrEq(FILENAME), OpenFlagsEq(GetParam())))
.Times(1).WillOnce(Return(0));
OpenFile(FILENAME, GetParam());

View File

@ -27,7 +27,7 @@ INSTANTIATE_TEST_CASE_P(FuseReadErrorTest, FuseReadErrorTest, Values(EAGAIN, EBA
TEST_P(FuseReadErrorTest, ReturnErrorOnFirstReadCall) {
EXPECT_CALL(fsimpl, read(0, _, _, _))
EXPECT_CALL(*fsimpl, read(0, _, _, _))
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
char *buf = new char[READCOUNT.value()];
@ -41,14 +41,14 @@ TEST_P(FuseReadErrorTest, ReturnErrorOnSecondReadCall) {
// We store the number of bytes the first call could successfully read and check later that our
// read syscall returns exactly this number of bytes
fspp::num_bytes_t successfullyReadBytes = fspp::num_bytes_t(-1);
EXPECT_CALL(fsimpl, read(0, _, _, Eq(fspp::num_bytes_t(0))))
EXPECT_CALL(*fsimpl, read(0, _, _, Eq(fspp::num_bytes_t(0))))
.Times(1)
.WillOnce(Invoke([&successfullyReadBytes](int, void*, fspp::num_bytes_t count, fspp::num_bytes_t) {
// Store the number of successfully read bytes
successfullyReadBytes = count;
return count;
}));
EXPECT_CALL(fsimpl, read(0, _, _, Ne(fspp::num_bytes_t(0))))
EXPECT_CALL(*fsimpl, read(0, _, _, Ne(fspp::num_bytes_t(0))))
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
char *buf = new char[READCOUNT.value()];

View File

@ -17,7 +17,7 @@ INSTANTIATE_TEST_CASE_P(FuseReadFileDescriptorTest, FuseReadFileDescriptorTest,
TEST_P(FuseReadFileDescriptorTest, FileDescriptorIsCorrect) {
ReturnIsFileOnLstatWithSize(FILENAME, fspp::num_bytes_t(1));
OnOpenReturnFileDescriptor(FILENAME, GetParam());
EXPECT_CALL(fsimpl, read(Eq(GetParam()), _, _, _))
EXPECT_CALL(*fsimpl, read(Eq(GetParam()), _, _, _))
.Times(1).WillOnce(ReturnSuccessfulRead);
char buf[1];

View File

@ -16,7 +16,7 @@ public:
void SetUp() override {
ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE);
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, read(0, _, _, _)).WillRepeatedly(ReturnSuccessfulReadRegardingSize(FILESIZE));
EXPECT_CALL(*fsimpl, read(0, _, _, _)).WillRepeatedly(ReturnSuccessfulReadRegardingSize(FILESIZE));
}
};

View File

@ -52,7 +52,7 @@ public:
testFile = std::make_unique<InMemoryFile>(DataFixture::generate(testData.fileSize().value()));
ReturnIsFileOnLstatWithSize(FILENAME, testData.fileSize());
OnOpenReturnFileDescriptor(FILENAME, 0);
EXPECT_CALL(fsimpl, read(0, _, _, _))
EXPECT_CALL(*fsimpl, read(0, _, _, _))
.WillRepeatedly(ReadFromFile);
}

View File

@ -9,7 +9,7 @@ public:
};
TEST_F(FuseReadDirDirnameTest, ReadRootDir) {
EXPECT_CALL(fsimpl, readDir(StrEq("/")))
EXPECT_CALL(*fsimpl, readDir(StrEq("/")))
.Times(1).WillOnce(ReturnDirEntries({}));
ReadDir("/");
@ -17,7 +17,7 @@ TEST_F(FuseReadDirDirnameTest, ReadRootDir) {
TEST_F(FuseReadDirDirnameTest, ReadDir) {
ReturnIsDirOnLstat("/mydir");
EXPECT_CALL(fsimpl, readDir(StrEq("/mydir")))
EXPECT_CALL(*fsimpl, readDir(StrEq("/mydir")))
.Times(1).WillOnce(ReturnDirEntries({}));
ReadDir("/mydir");
@ -26,7 +26,7 @@ TEST_F(FuseReadDirDirnameTest, ReadDir) {
TEST_F(FuseReadDirDirnameTest, ReadDirNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
EXPECT_CALL(fsimpl, readDir(StrEq("/mydir/mydir2")))
EXPECT_CALL(*fsimpl, readDir(StrEq("/mydir/mydir2")))
.Times(1).WillOnce(ReturnDirEntries({}));
ReadDir("/mydir/mydir2");
@ -36,7 +36,7 @@ TEST_F(FuseReadDirDirnameTest, ReadDirNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnIsDirOnLstat("/mydir/mydir2/mydir3");
EXPECT_CALL(fsimpl, readDir(StrEq("/mydir/mydir2/mydir3")))
EXPECT_CALL(*fsimpl, readDir(StrEq("/mydir/mydir2/mydir3")))
.Times(1).WillOnce(ReturnDirEntries({}));
ReadDir("/mydir/mydir2/mydir3");

View File

@ -19,7 +19,7 @@ INSTANTIATE_TEST_CASE_P(FuseReadDirErrorTest, FuseReadDirErrorTest, Values(EACCE
TEST_F(FuseReadDirErrorTest, NoError) {
ReturnIsDirOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, readDir(StrEq(DIRNAME)))
EXPECT_CALL(*fsimpl, readDir(StrEq(DIRNAME)))
.Times(1).WillOnce(ReturnDirEntries({}));
int error = ReadDirReturnError(DIRNAME);
@ -28,7 +28,7 @@ TEST_F(FuseReadDirErrorTest, NoError) {
TEST_P(FuseReadDirErrorTest, ReturnedErrorCodeIsCorrect) {
ReturnIsDirOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, readDir(StrEq(DIRNAME)))
EXPECT_CALL(*fsimpl, readDir(StrEq(DIRNAME)))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = ReadDirReturnError(DIRNAME);

View File

@ -26,7 +26,7 @@ class FuseReadDirReturnTest: public FuseReadDirTest, public WithParamInterface<v
public:
void testDirEntriesAreCorrect(const vector<string> &direntries) {
ReturnIsDirOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, readDir(StrEq(DIRNAME)))
EXPECT_CALL(*fsimpl, readDir(StrEq(DIRNAME)))
.Times(1).WillOnce(ReturnDirEntries(direntries));
auto returned_dir_entries = ReadDir(DIRNAME);

View File

@ -15,7 +15,7 @@ INSTANTIATE_TEST_CASE_P(FuseRenameErrorTest, FuseRenameErrorTest, Values(EACCES,
TEST_P(FuseRenameErrorTest, ReturnedErrorIsCorrect) {
ReturnIsFileOnLstat(FILENAME1);
ReturnDoesntExistOnLstat(FILENAME2);
EXPECT_CALL(fsimpl, rename(StrEq(FILENAME1), StrEq(FILENAME2)))
EXPECT_CALL(*fsimpl, rename(StrEq(FILENAME1), StrEq(FILENAME2)))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = RenameReturnError(FILENAME1, FILENAME2);

View File

@ -9,7 +9,7 @@ class FuseRenameFilenameTest: public FuseRenameTest {
TEST_F(FuseRenameFilenameTest, RenameFileRootToRoot) {
ReturnIsFileOnLstat("/myfile");
ReturnDoesntExistOnLstat("/myrenamedfile");
EXPECT_CALL(fsimpl, rename(StrEq("/myfile"), StrEq("/myrenamedfile")))
EXPECT_CALL(*fsimpl, rename(StrEq("/myfile"), StrEq("/myrenamedfile")))
.Times(1).WillOnce(Return());
Rename("/myfile", "/myrenamedfile");
@ -19,7 +19,7 @@ TEST_F(FuseRenameFilenameTest, RenameFileRootToNested) {
ReturnIsFileOnLstat("/myfile");
ReturnIsDirOnLstat("/mydir");
ReturnDoesntExistOnLstat("/mydir/myrenamedfile");
EXPECT_CALL(fsimpl, rename(StrEq("/myfile"), StrEq("/mydir/myrenamedfile")))
EXPECT_CALL(*fsimpl, rename(StrEq("/myfile"), StrEq("/mydir/myrenamedfile")))
.Times(1).WillOnce(Return());
Rename("/myfile", "/mydir/myrenamedfile");
@ -29,7 +29,7 @@ TEST_F(FuseRenameFilenameTest, RenameFileNestedToRoot) {
ReturnDoesntExistOnLstat("/myrenamedfile");
ReturnIsDirOnLstat("/mydir");
ReturnIsFileOnLstat("/mydir/myfile");
EXPECT_CALL(fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/myrenamedfile")))
EXPECT_CALL(*fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/myrenamedfile")))
.Times(1).WillOnce(Return());
Rename("/mydir/myfile", "/myrenamedfile");
@ -39,7 +39,7 @@ TEST_F(FuseRenameFilenameTest, RenameFileNestedToNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsFileOnLstat("/mydir/myfile");
ReturnDoesntExistOnLstat("/mydir/myrenamedfile");
EXPECT_CALL(fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/mydir/myrenamedfile")))
EXPECT_CALL(*fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/mydir/myrenamedfile")))
.Times(1).WillOnce(Return());
Rename("/mydir/myfile", "/mydir/myrenamedfile");
@ -50,7 +50,7 @@ TEST_F(FuseRenameFilenameTest, RenameFileNestedToNested2) {
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnIsFileOnLstat("/mydir/mydir2/myfile");
ReturnDoesntExistOnLstat("/mydir/mydir2/myrenamedfile");
EXPECT_CALL(fsimpl, rename(StrEq("/mydir/mydir2/myfile"), StrEq("/mydir/mydir2/myrenamedfile")))
EXPECT_CALL(*fsimpl, rename(StrEq("/mydir/mydir2/myfile"), StrEq("/mydir/mydir2/myrenamedfile")))
.Times(1).WillOnce(Return());
Rename("/mydir/mydir2/myfile", "/mydir/mydir2/myrenamedfile");
@ -61,7 +61,7 @@ TEST_F(FuseRenameFilenameTest, RenameFileNestedToNested_DifferentFolder) {
ReturnIsDirOnLstat("/mydir2");
ReturnIsFileOnLstat("/mydir/myfile");
ReturnDoesntExistOnLstat("/mydir2/myrenamedfile");
EXPECT_CALL(fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/mydir2/myrenamedfile")))
EXPECT_CALL(*fsimpl, rename(StrEq("/mydir/myfile"), StrEq("/mydir2/myrenamedfile")))
.Times(1).WillOnce(Return());
Rename("/mydir/myfile", "/mydir2/myrenamedfile");
@ -70,7 +70,7 @@ TEST_F(FuseRenameFilenameTest, RenameFileNestedToNested_DifferentFolder) {
TEST_F(FuseRenameFilenameTest, RenameDirRootToRoot) {
ReturnIsDirOnLstat("/mydir");
ReturnDoesntExistOnLstat("/myrenameddir");
EXPECT_CALL(fsimpl, rename(StrEq("/mydir"), StrEq("/myrenameddir")))
EXPECT_CALL(*fsimpl, rename(StrEq("/mydir"), StrEq("/myrenameddir")))
.Times(1).WillOnce(Return());
Rename("/mydir", "/myrenameddir");
@ -80,7 +80,7 @@ TEST_F(FuseRenameFilenameTest, RenameDirRootToNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/myrootdir");
ReturnDoesntExistOnLstat("/myrootdir/myrenameddir");
EXPECT_CALL(fsimpl, rename(StrEq("/mydir"), StrEq("/myrootdir/myrenameddir")))
EXPECT_CALL(*fsimpl, rename(StrEq("/mydir"), StrEq("/myrootdir/myrenameddir")))
.Times(1).WillOnce(Return());
Rename("/mydir", "/myrootdir/myrenameddir");
@ -90,7 +90,7 @@ TEST_F(FuseRenameFilenameTest, RenameDirNestedToRoot) {
ReturnDoesntExistOnLstat("/myrenameddir");
ReturnIsDirOnLstat("/myrootdir");
ReturnIsDirOnLstat("/myrootdir/mydir");
EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrenameddir")))
EXPECT_CALL(*fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrenameddir")))
.Times(1).WillOnce(Return());
Rename("/myrootdir/mydir", "/myrenameddir");
@ -100,7 +100,7 @@ TEST_F(FuseRenameFilenameTest, RenameDirNestedToNested) {
ReturnIsDirOnLstat("/myrootdir");
ReturnIsDirOnLstat("/myrootdir/mydir");
ReturnDoesntExistOnLstat("/myrootdir/myrenameddir");
EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrootdir/myrenameddir")))
EXPECT_CALL(*fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrootdir/myrenameddir")))
.Times(1).WillOnce(Return());
Rename("/myrootdir/mydir", "/myrootdir/myrenameddir");
@ -111,7 +111,7 @@ TEST_F(FuseRenameFilenameTest, RenameDirNestedToNested2) {
ReturnIsDirOnLstat("/myrootdir/myrootdir2");
ReturnIsDirOnLstat("/myrootdir/myrootdir2/mydir");
ReturnDoesntExistOnLstat("/myrootdir/myrootdir2/myrenameddir");
EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/myrootdir2/mydir"), StrEq("/myrootdir/myrootdir2/myrenameddir")))
EXPECT_CALL(*fsimpl, rename(StrEq("/myrootdir/myrootdir2/mydir"), StrEq("/myrootdir/myrootdir2/myrenameddir")))
.Times(1).WillOnce(Return());
Rename("/myrootdir/myrootdir2/mydir", "/myrootdir/myrootdir2/myrenameddir");
@ -122,7 +122,7 @@ TEST_F(FuseRenameFilenameTest, RenameDirNestedToNested_DifferentFolder) {
ReturnIsDirOnLstat("/myrootdir2");
ReturnIsDirOnLstat("/myrootdir/mydir");
ReturnDoesntExistOnLstat("/myrootdir2/myrenameddir");
EXPECT_CALL(fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrootdir2/myrenameddir")))
EXPECT_CALL(*fsimpl, rename(StrEq("/myrootdir/mydir"), StrEq("/myrootdir2/myrenameddir")))
.Times(1).WillOnce(Return());
Rename("/myrootdir/mydir", "/myrootdir2/myrenameddir");

View File

@ -7,7 +7,7 @@ class FuseRmdirDirnameTest: public FuseRmdirTest {
TEST_F(FuseRmdirDirnameTest, Rmdir) {
ReturnIsDirOnLstat("/mydir");
EXPECT_CALL(fsimpl, rmdir(StrEq("/mydir")))
EXPECT_CALL(*fsimpl, rmdir(StrEq("/mydir")))
// After rmdir was called, lstat should return that it doesn't exist anymore
// This is needed to make the ::rmdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat());
@ -18,7 +18,7 @@ TEST_F(FuseRmdirDirnameTest, Rmdir) {
TEST_F(FuseRmdirDirnameTest, RmdirNested) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mysubdir");
EXPECT_CALL(fsimpl, rmdir(StrEq("/mydir/mysubdir")))
EXPECT_CALL(*fsimpl, rmdir(StrEq("/mydir/mysubdir")))
// After rmdir was called, lstat should return that it doesn't exist anymore
// This is needed to make the ::rmdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat());
@ -30,7 +30,7 @@ TEST_F(FuseRmdirDirnameTest, RmdirNested2) {
ReturnIsDirOnLstat("/mydir");
ReturnIsDirOnLstat("/mydir/mydir2");
ReturnIsDirOnLstat("/mydir/mydir2/mydir3");
EXPECT_CALL(fsimpl, rmdir(StrEq("/mydir/mydir2/mydir3")))
EXPECT_CALL(*fsimpl, rmdir(StrEq("/mydir/mydir2/mydir3")))
// After rmdir was called, lstat should return that it doesn't exist anymore
// This is needed to make the ::rmdir() syscall pass.
.Times(1).WillOnce(FromNowOnReturnDoesntExistOnLstat());

View File

@ -14,7 +14,7 @@ INSTANTIATE_TEST_CASE_P(FuseRmdirErrorTest, FuseRmdirErrorTest, Values(EACCES, E
TEST_P(FuseRmdirErrorTest, ReturnedErrorIsCorrect) {
ReturnIsDirOnLstat(DIRNAME);
EXPECT_CALL(fsimpl, rmdir(StrEq(DIRNAME)))
EXPECT_CALL(*fsimpl, rmdir(StrEq(DIRNAME)))
.Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = RmdirReturnError(DIRNAME);

View File

@ -17,14 +17,14 @@ INSTANTIATE_TEST_CASE_P(FuseStatfsErrorTest, FuseStatfsErrorTest, Values(EACCES,
TEST_F(FuseStatfsErrorTest, ReturnNoError) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, statfs(_)).Times(1).WillOnce(Return());
EXPECT_CALL(*fsimpl, statfs(_)).Times(1).WillOnce(Return());
int error = StatfsReturnError(FILENAME);
EXPECT_EQ(0, error);
}
TEST_P(FuseStatfsErrorTest, ReturnError) {
ReturnIsFileOnLstat(FILENAME);
EXPECT_CALL(fsimpl, statfs( _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
EXPECT_CALL(*fsimpl, statfs( _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam())));
int error = StatfsReturnError(FILENAME);
EXPECT_EQ(GetParam(), error);
}

Some files were not shown because too many files have changed in this diff Show More