Merge branch 'develop' into feature/either
This commit is contained in:
commit
0444b809a2
@ -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
16
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
## Expected Behavior
|
||||
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
|
||||
## Steps to Reproduce the Problem
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Specifications
|
||||
|
||||
- CryFS Version:
|
||||
- Operating System (incl. Version):
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
|
@ -18,60 +18,55 @@ 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")
|
||||
# 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")
|
||||
# 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/*")
|
||||
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()
|
||||
# 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/*")
|
||||
|
||||
set(CPACK_PACKAGE_NAME "cryfs")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Encrypt your files and store them in the cloud.")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "CryFS encrypts your files, so you can safely store them anywhere. It works well together with cloud services like Dropbox, iCloud, OneDrive and others.")
|
||||
set(CPACK_PACKAGE_CONTACT "Sebastian Messmer <messmer@cryfs.org>")
|
||||
set(CPACK_PACKAGE_VENDOR "Sebastian Messmer")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE.txt")
|
||||
set(CPACK_PACKAGE_NAME "cryfs")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Encrypt your files and store them in the cloud.")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "CryFS encrypts your files, so you can safely store them anywhere. It works well together with cloud services like Dropbox, iCloud, OneDrive and others.")
|
||||
set(CPACK_PACKAGE_CONTACT "Sebastian Messmer <messmer@cryfs.org>")
|
||||
set(CPACK_PACKAGE_VENDOR "Sebastian Messmer")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE.txt")
|
||||
|
||||
get_git_version(GITVERSION_VERSION_STRING)
|
||||
get_git_version(GITVERSION_VERSION_STRING)
|
||||
|
||||
if(WIN32 AND NOT UNIX)
|
||||
set(CPACK_GENERATOR WIX)
|
||||
if(WIN32 AND NOT UNIX)
|
||||
set(CPACK_GENERATOR WIX)
|
||||
|
||||
string(REGEX REPLACE "^([0-9\\.]+)([-+][0-9\\.a-zA-Z+-]+)?$" "\\1" STRIPPED_VERSION_NUMBER "${GITVERSION_VERSION_STRING}")
|
||||
append_build_number(${STRIPPED_VERSION_NUMBER} WIX_VERSION_NUMBER)
|
||||
message(STATUS "WIX package version is ${WIX_VERSION_NUMBER}")
|
||||
set(CPACK_PACKAGE_VERSION "${WIX_VERSION_NUMBER}")
|
||||
string(REGEX REPLACE "^([0-9\\.]+)([-+][0-9\\.a-zA-Z+-]+)?$" "\\1" STRIPPED_VERSION_NUMBER "${GITVERSION_VERSION_STRING}")
|
||||
append_build_number(${STRIPPED_VERSION_NUMBER} WIX_VERSION_NUMBER)
|
||||
message(STATUS "WIX package version is ${WIX_VERSION_NUMBER}")
|
||||
set(CPACK_PACKAGE_VERSION "${WIX_VERSION_NUMBER}")
|
||||
|
||||
set(CPACK_WIX_UPGRADE_GUID "8b872ce1-557d-48e6-ac57-9f5e574feabf")
|
||||
set(CPACK_WIX_PRODUCT_GUID "26116061-4f99-4c44-a178-2153fa396308")
|
||||
#set(CPACK_WIX_PRODUCT_ICON "...")
|
||||
set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "https://www.cryfs.org")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "CryFS/${GITVERSION_VERSION_STRING}")
|
||||
set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/wix/change_path_env.xml")
|
||||
else()
|
||||
set(CPACK_GENERATOR TGZ DEB RPM)
|
||||
set(CPACK_PACKAGE_VERSION "${GITVERSION_VERSION_STRING}")
|
||||
set(CPACK_STRIP_FILES OFF)
|
||||
set(CPACK_SOURCE_STRIP_FILES OFF)
|
||||
endif()
|
||||
set(CPACK_PACKAGE_EXECUTABLES "cryfs" "CryFS")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
# Needs gnupg2, lsb-release for postinst script
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "fuse, gnupg2, lsb-release")
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.cryfs.org")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "LGPLv3")
|
||||
set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION})
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/bin;/usr/share/man;/usr/share/man/man1")
|
||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postinst;${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postrm")
|
||||
|
||||
include(CPack)
|
||||
set(CPACK_WIX_UPGRADE_GUID "8b872ce1-557d-48e6-ac57-9f5e574feabf")
|
||||
set(CPACK_WIX_PRODUCT_GUID "26116061-4f99-4c44-a178-2153fa396308")
|
||||
#set(CPACK_WIX_PRODUCT_ICON "...")
|
||||
set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "https://www.cryfs.org")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "CryFS/${GITVERSION_VERSION_STRING}")
|
||||
set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/wix/change_path_env.xml")
|
||||
else()
|
||||
set(CPACK_GENERATOR TGZ DEB RPM)
|
||||
set(CPACK_PACKAGE_VERSION "${GITVERSION_VERSION_STRING}")
|
||||
set(CPACK_STRIP_FILES OFF)
|
||||
set(CPACK_SOURCE_STRIP_FILES OFF)
|
||||
endif()
|
||||
set(CPACK_PACKAGE_EXECUTABLES "cryfs" "CryFS")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
# Needs gnupg2, lsb-release for postinst script
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "fuse, gnupg2, lsb-release")
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://www.cryfs.org")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "LGPLv3")
|
||||
set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION})
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/bin;/usr/share/man;/usr/share/man/man1")
|
||||
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postinst;${CMAKE_CURRENT_SOURCE_DIR}/debfiles/postrm")
|
||||
|
||||
include(CPack)
|
||||
endif()
|
||||
|
@ -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?");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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) {
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -1 +0,0 @@
|
||||
#include "IntegrityViolationError.h"
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>();
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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,25 +220,47 @@ 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());
|
||||
LocalStateDir localStateDir(Environment::localStateDir());
|
||||
auto blockStore = make_unique_ref<OnDiskBlockStore2>(options.baseDir());
|
||||
auto config = _loadOrCreateConfig(options, localStateDir);
|
||||
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());
|
||||
|
||||
//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()));
|
||||
}
|
||||
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
|
||||
_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;
|
||||
@ -245,7 +268,11 @@ namespace cryfs {
|
||||
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) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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,17 +19,24 @@ 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)
|
||||
auto httpClient = make_unique_ref<cpputils::WinHttpClient>();
|
||||
auto httpClient = make_unique_ref<cpputils::WinHttpClient>();
|
||||
#else
|
||||
auto httpClient = make_unique_ref<cpputils::CurlHttpClient>();
|
||||
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());
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
23
src/cryfs/config/CryPresetPasswordBasedKeyProvider.cpp
Normal file
23
src/cryfs/config/CryPresetPasswordBasedKeyProvider.cpp
Normal 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)};
|
||||
}
|
||||
|
||||
}
|
27
src/cryfs/config/CryPresetPasswordBasedKeyProvider.h
Normal file
27
src/cryfs/config/CryPresetPasswordBasedKeyProvider.h
Normal 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
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
110
src/fspp/fuse/InvalidFilesystem.h
Normal file
110
src/fspp/fuse/InvalidFilesystem.h
Normal 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
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
auto innerNode = dynamic_pointer_move<DataInnerNode>(*node);
|
||||
if (innerNode != none) {
|
||||
for (uint32_t childIndex = 0; childIndex < (*innerNode)->numChildren(); ++childIndex) {
|
||||
auto child = (*innerNode)->readChild(childIndex).blockId();
|
||||
unaccountedBlocks.erase(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
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 != boost::none) {
|
||||
for (uint32_t childIndex = 0; childIndex < (*innerNode)->numChildren(); ++childIndex) {
|
||||
auto childId = (*innerNode)->readChild(childIndex).blockId();
|
||||
_forEachBlockInBlob(nodeStore, childId, callback);
|
||||
}
|
||||
}
|
||||
return blocksReferencedByDirEntries;
|
||||
}
|
||||
|
||||
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 << "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);
|
||||
});
|
||||
}
|
||||
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) {
|
||||
|
@ -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, [] {});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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()));
|
||||
|
@ -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;
|
||||
|
@ -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,22 +16,56 @@ 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);
|
||||
}
|
||||
|
||||
KeyResult requestKeyForNewFilesystem(size_t keySize) override {
|
||||
auto derived = SCrypt(SCrypt::TestSettings).deriveNewKey(keySize, "pass");
|
||||
return {
|
||||
std::move(derived.key),
|
||||
std::move(derived.kdfParameters)
|
||||
std::move(derived.key),
|
||||
std::move(derived.kdfParameters)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
@ -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,39 +42,45 @@ public:
|
||||
return std::move(httpClient);
|
||||
}
|
||||
|
||||
int run(const std::vector<std::string>& args) {
|
||||
std::vector<const char*> _args;
|
||||
_args.reserve(args.size() + 1);
|
||||
_args.emplace_back("cryfs");
|
||||
for (const std::string& arg : args) {
|
||||
_args.emplace_back(arg.c_str());
|
||||
}
|
||||
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");
|
||||
for (const std::string& arg : args) {
|
||||
_args.emplace_back(arg.c_str());
|
||||
}
|
||||
auto &keyGenerator = cpputils::Random::PseudoRandom();
|
||||
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")));
|
||||
EXPECT_EQ(0, filesystem_output.exit_code);
|
||||
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,60 +89,80 @@ 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);
|
||||
static void _unmount(const boost::filesystem::path &mountDir) {
|
||||
int returncode = -1;
|
||||
#if defined(__APPLE__)
|
||||
returncode = cpputils::Subprocess::call(std::string("umount ") + mountDir.string().c_str() + " 2>/dev/null").exitcode;
|
||||
#elif defined(_MSC_VER)
|
||||
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;
|
||||
#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;
|
||||
});
|
||||
|
||||
if (mountDirForUnmounting.is_initialized()) {
|
||||
boost::filesystem::path mountDir = *mountDirForUnmounting;
|
||||
std::future<bool> unmount_success = std::async(std::launch::async, [&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;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if(std::future_status::ready != unmount_success.wait_for(std::chrono::seconds(10))) {
|
||||
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
||||
testing::internal::GetCapturedStderr(); // stop capturing stderr
|
||||
|
||||
std::cerr << "Unmount thread didn't finish";
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
EXPECT_TRUE(unmount_success.get()); // this also re-throws any potential exceptions
|
||||
// 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 != on_mounted_success.wait_for(std::chrono::seconds(1000))) {
|
||||
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
||||
testing::internal::GetCapturedStderr(); // stop capturing stderr
|
||||
|
||||
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(on_mounted_success.get()); // this also re-throws any potential exceptions
|
||||
|
||||
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::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);
|
||||
}
|
||||
|
||||
if(std::future_status::ready != exit_code.wait_for(std::chrono::seconds(10))) {
|
||||
testing::internal::GetCapturedStdout(); // stop capturing stdout
|
||||
testing::internal::GetCapturedStderr(); // stop capturing stderr
|
||||
|
||||
std::cerr << "Filesystem thread didn't finish";
|
||||
// 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(),
|
||||
testing::internal::GetCapturedStdout(),
|
||||
testing::internal::GetCapturedStderr()
|
||||
};
|
||||
return {
|
||||
exit_code.get(), // this also re-throws any potential exceptions
|
||||
testing::internal::GetCapturedStdout(),
|
||||
testing::internal::GetCapturedStderr()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
|
56
test/cryfs/config/CryPresetPasswordBasedKeyProviderTest.cpp
Normal file
56
test/cryfs/config/CryPresetPasswordBasedKeyProviderTest.cpp
Normal 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());
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}));
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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/");
|
||||
}
|
||||
|
@ -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);
|
||||
}));
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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()];
|
||||
|
@ -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];
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user