Implement cryfs-unmount for unmounting filesystems

This commit is contained in:
Sebastian Messmer 2019-01-19 13:02:41 -08:00
parent 7adbe4e3ca
commit 58cb91102d
39 changed files with 497 additions and 77 deletions

View File

@ -479,7 +479,7 @@ jobs:
OMP_NUM_THREADS: "1"
CXXFLAGS: "-O2 -fsanitize=thread -fno-omit-frame-pointer"
BUILD_TYPE: "Debug"
GTEST_ARGS: "--gtest_filter=-LoggingTest.LoggingAlsoWorksAfterFork:AssertTest_DebugBuild.*:CliTest_Setup.*:CliTest_IntegrityCheck.*:*/CliTest_WrongEnvironment.*"
GTEST_ARGS: "--gtest_filter=-LoggingTest.LoggingAlsoWorksAfterFork:AssertTest_DebugBuild.*:CliTest_Setup.*:CliTest_IntegrityCheck.*:*/CliTest_WrongEnvironment.*:CliTest_Unmount.*"
CMAKE_FLAGS: ""
RUN_TESTS: true
clang_tidy:

View File

@ -12,6 +12,7 @@ New Features & Improvements:
* New block size options: 4KB and 16KB
* New default block size: 16KB. This should decrease the size of the ciphertext directory for most users.
* Increased scrypt hardness to (N=1048576, r=4, p=8) to make it harder to crack the key while allowing cryfs to take advantage of multicore machines.
* cryfs-unmount tool to unmount filesystems
Fixed bugs:
* `du` shows correct file system size on Mac OS X.

View File

@ -8,4 +8,5 @@ add_subdirectory(blockstore)
add_subdirectory(blobstore)
add_subdirectory(cryfs)
add_subdirectory(cryfs-cli)
add_subdirectory(cryfs-unmount)
add_subdirectory(stats)

View File

@ -0,0 +1,26 @@
#pragma once
#ifndef MESSMER_CPPUTILS_SYSTEM_PATH_H
#define MESSMER_CPPUTILS_SYSTEM_PATH_H
#include <boost/filesystem/path.hpp>
#include <cpp-utils/macros.h>
namespace cpputils {
#if defined(_MSC_VER)
inline bool path_is_just_drive_letter(const boost::filesystem::path& path) {
return path.has_root_path() && !path.has_root_directory() && !path.has_parent_path();
}
#else
inline constexpr bool path_is_just_drive_letter(const boost::filesystem::path& /*path*/) {
return false;
}
#endif
}
#endif

View File

@ -1,12 +1,12 @@
#pragma once
#ifndef MESSMER_CRYFS_SRC_CLI_CALLAFTERTIMEOUT_H
#define MESSMER_CRYFS_SRC_CLI_CALLAFTERTIMEOUT_H
#ifndef MESSMER_CRYFSCLI_CALLAFTERTIMEOUT_H
#define MESSMER_CRYFSCLI_CALLAFTERTIMEOUT_H
#include <functional>
#include <mutex>
#include <cpp-utils/thread/LoopThread.h>
namespace cryfs {
namespace cryfs_cli {
class CallAfterTimeout final {
public:
CallAfterTimeout(boost::chrono::milliseconds timeout, std::function<void()> callback);

View File

@ -30,6 +30,7 @@
//TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
//TODO Factor out class handling askPassword
using namespace cryfs_cli;
using namespace cryfs;
namespace bf = boost::filesystem;
using namespace cpputils::logging;
@ -66,7 +67,7 @@ using gitversion::VersionCompare;
//TODO Replace ASSERTs with other error handling when it is not a programming error but an environment influence (e.g. a block is missing)
//TODO Can we improve performance by setting compiler parameter -maes for scrypt?
namespace cryfs {
namespace cryfs_cli {
Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, shared_ptr<Console> console):
_keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(), _noninteractive(false), _idleUnmounter(none), _device(none) {
@ -262,12 +263,8 @@ namespace cryfs {
_initLogfile(options);
#ifdef __APPLE__
std::cout << "\nMounting filesystem. To unmount, call:\n$ umount " << options.mountDir() << "\n" << std::endl;
#else
std::cout << "\nMounting filesystem. To unmount, call:\n$ fusermount -u " << options.mountDir() << "\n"
std::cout << "\nMounting filesystem. To unmount, call:\n$ cryfs-unmount " << options.mountDir() << "\n"
<< std::endl;
#endif
fuse->run(options.mountDir(), options.fuseOptions());
if (stoppedBecauseOfIntegrityViolation) {

View File

@ -1,6 +1,6 @@
#pragma once
#ifndef MESSMER_CRYFS_CLI_H
#define MESSMER_CRYFS_CLI_H
#ifndef MESSMER_CRYFSCLI_CLI_H
#define MESSMER_CRYFSCLI_CLI_H
#include "program_options/ProgramOptions.h"
#include <cryfs/config/CryConfigFile.h>
@ -14,7 +14,7 @@
#include <cryfs/config/CryConfigLoader.h>
#include <cryfs/ErrorCodes.h>
namespace cryfs {
namespace cryfs_cli {
class Cli final {
public:
Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings& scryptSettings, std::shared_ptr<cpputils::Console> console);
@ -23,9 +23,9 @@ namespace cryfs {
private:
void _checkForUpdates(cpputils::unique_ref<cpputils::HttpClient> httpClient);
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);
cryfs::CryConfigLoader::ConfigLoadResult _loadOrCreateConfig(const program_options::ProgramOptions &options, const cryfs::LocalStateDir& localStateDir);
void _checkConfigIntegrity(const boost::filesystem::path& basedir, const cryfs::LocalStateDir& localStateDir, const cryfs::CryConfigFile& config, bool allowReplacedFilesystem);
boost::optional<cryfs::CryConfigLoader::ConfigLoadResult> _loadOrCreateConfigFile(boost::filesystem::path configFilePath, cryfs::LocalStateDir localStateDir, const boost::optional<std::string> &cipher, const boost::optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const boost::optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem);
boost::filesystem::path _determineConfigFile(const program_options::ProgramOptions &options);
static std::function<std::string()> _askPasswordForExistingFilesystem(std::shared_ptr<cpputils::Console> console);
static std::function<std::string()> _askPasswordForNewFilesystem(std::shared_ptr<cpputils::Console> console);
@ -37,11 +37,11 @@ namespace cryfs {
void _sanityChecks(const program_options::ProgramOptions &options);
void _checkMountdirDoesntContainBasedir(const program_options::ProgramOptions &options);
bool _pathContains(const boost::filesystem::path &parent, const boost::filesystem::path &child);
void _checkDirAccessible(const boost::filesystem::path &dir, const std::string &name, ErrorCode errorCode);
std::shared_ptr<cpputils::TempFile> _checkDirWriteable(const boost::filesystem::path &dir, const std::string &name, ErrorCode errorCode);
void _checkDirReadable(const boost::filesystem::path &dir, std::shared_ptr<cpputils::TempFile> tempfile, const std::string &name, ErrorCode errorCode);
void _checkDirAccessible(const boost::filesystem::path &dir, const std::string &name, cryfs::ErrorCode errorCode);
std::shared_ptr<cpputils::TempFile> _checkDirWriteable(const boost::filesystem::path &dir, const std::string &name, cryfs::ErrorCode errorCode);
void _checkDirReadable(const boost::filesystem::path &dir, std::shared_ptr<cpputils::TempFile> tempfile, const std::string &name, cryfs::ErrorCode errorCode);
boost::optional<cpputils::unique_ref<CallAfterTimeout>> _createIdleCallback(boost::optional<double> minutes, std::function<void()> callback);
void _sanityCheckFilesystem(CryDevice *device);
void _sanityCheckFilesystem(cryfs::CryDevice *device);
cpputils::RandomGenerator &_keyGenerator;
@ -49,7 +49,7 @@ namespace cryfs {
std::shared_ptr<cpputils::Console> _console;
bool _noninteractive;
boost::optional<cpputils::unique_ref<CallAfterTimeout>> _idleUnmounter;
boost::optional<cpputils::unique_ref<CryDevice>> _device;
boost::optional<cpputils::unique_ref<cryfs::CryDevice>> _device;
DISALLOW_COPY_AND_ASSIGN(Cli);
};

View File

@ -6,7 +6,7 @@
using std::string;
namespace bf = boost::filesystem;
namespace cryfs {
namespace cryfs_cli {
const string Environment::FRONTEND_KEY = "CRYFS_FRONTEND";
const string Environment::FRONTEND_NONINTERACTIVE = "noninteractive";
const string Environment::NOUPDATECHECK_KEY = "CRYFS_NO_UPDATE_CHECK";

View File

@ -1,11 +1,11 @@
#pragma once
#ifndef MESSMER_CRYFS_CLI_ENVIRONMENT_H
#define MESSMER_CRYFS_CLI_ENVIRONMENT_H
#ifndef MESSMER_CRYFSCLI_ENVIRONMENT_H
#define MESSMER_CRYFSCLI_ENVIRONMENT_H
#include <string>
#include <boost/filesystem/path.hpp>
namespace cryfs {
namespace cryfs_cli {
class Environment {
public:

View File

@ -13,7 +13,7 @@ using boost::property_tree::ptree;
using boost::property_tree::json_parser_error;
using namespace cpputils::logging;
namespace cryfs {
namespace cryfs_cli {
VersionChecker::VersionChecker(HttpClient* httpClient)
: _versionInfo(_getVersionInfo(httpClient)) {}

View File

@ -1,5 +1,5 @@
#ifndef MESSMER_CRYFS_SRC_CLI_VERSIONCHECKER_H
#define MESSMER_CRYFS_SRC_CLI_VERSIONCHECKER_H
#ifndef MESSMER_CRYFSCLI_VERSIONCHECKER_H
#define MESSMER_CRYFSCLI_VERSIONCHECKER_H
#include <cpp-utils/macros.h>
#include <string>
@ -8,7 +8,7 @@
#include <cpp-utils/network/HttpClient.h>
#include <cpp-utils/pointer/unique_ref.h>
namespace cryfs {
namespace cryfs_cli {
class VersionChecker final {
public:
//TODO Write a cpputils::shared_ref and use it

View File

@ -10,7 +10,7 @@
#include <cpp-utils/network/CurlHttpClient.h>
#endif
using namespace cryfs;
using namespace cryfs_cli;
using cpputils::Random;
using cpputils::SCrypt;
using cpputils::IOStreamConsole;
@ -35,13 +35,13 @@ int main(int argc, const char *argv[]) {
#endif
return Cli(keyGenerator, SCrypt::DefaultSettings, make_shared<IOStreamConsole>())
.main(argc, argv, std::move(httpClient), []{});
} catch (const CryfsException &e) {
} catch (const cryfs::CryfsException &e) {
if (e.what() != string()) {
std::cerr << "Error: " << e.what() << std::endl;
}
return exitCode(e.errorCode());
} catch (const std::exception &e) {
cerr << "Error: " << e.what();
return exitCode(ErrorCode::UnspecifiedError);
return exitCode(cryfs::ErrorCode::UnspecifiedError);
}
}

View File

@ -8,7 +8,7 @@
namespace po = boost::program_options;
namespace bf = boost::filesystem;
using namespace cryfs::program_options;
using namespace cryfs_cli::program_options;
using cryfs::CryConfigConsole;
using cryfs::CryfsException;
using cryfs::ErrorCode;

View File

@ -1,12 +1,12 @@
#pragma once
#ifndef MESSMER_CRYFS_PROGRAMOPTIONS_PARSER_H
#define MESSMER_CRYFS_PROGRAMOPTIONS_PARSER_H
#ifndef MESSMER_CRYFSCLI_PROGRAMOPTIONS_PARSER_H
#define MESSMER_CRYFSCLI_PROGRAMOPTIONS_PARSER_H
#include "ProgramOptions.h"
#include <boost/program_options.hpp>
#include <cryfs/ErrorCodes.h>
namespace cryfs {
namespace cryfs_cli {
namespace program_options {
class Parser final {
public:
@ -21,7 +21,7 @@ namespace cryfs {
static void _addPositionalOptionForBaseDir(boost::program_options::options_description *desc,
boost::program_options::positional_options_description *positional);
static void _showHelp();
[[noreturn]] static void _showHelpAndExit(const std::string& message, ErrorCode errorCode);
[[noreturn]] static void _showHelpAndExit(const std::string& message, cryfs::ErrorCode errorCode);
[[noreturn]] static void _showCiphersAndExit(const std::vector<std::string> &supportedCiphers);
[[noreturn]] static void _showVersionAndExit();
static boost::program_options::variables_map _parseOptionsOrShowHelp(const std::vector<std::string> &options, const std::vector<std::string> &supportedCiphers);

View File

@ -1,8 +1,9 @@
#include "ProgramOptions.h"
#include <cstring>
#include <cpp-utils/assert/assert.h>
#include <cpp-utils/system/path.h>
using namespace cryfs::program_options;
using namespace cryfs_cli::program_options;
using std::string;
using std::vector;
using boost::optional;
@ -16,11 +17,7 @@ ProgramOptions::ProgramOptions(bf::path baseDir, bf::path mountDir, optional<bf:
boost::optional<bool> missingBlockIsIntegrityViolation,
vector<string> fuseOptions)
: _configFile(std::move(configFile)), _baseDir(bf::absolute(std::move(baseDir))), _mountDir(std::move(mountDir)),
#if defined(_MSC_VER)
_mountDirIsDriveLetter(_mountDir.has_root_path() && !_mountDir.has_root_directory() && !_mountDir.has_parent_path()),
#else
_mountDirIsDriveLetter(false),
#endif
_mountDirIsDriveLetter(cpputils::path_is_just_drive_letter(_mountDir)),
_foreground(foreground),
_allowFilesystemUpgrade(allowFilesystemUpgrade), _allowReplacedFilesystem(allowReplacedFilesystem), _allowIntegrityViolations(allowIntegrityViolations),
_cipher(std::move(cipher)), _blocksizeBytes(std::move(blocksizeBytes)), _unmountAfterIdleMinutes(std::move(unmountAfterIdleMinutes)),

View File

@ -1,6 +1,6 @@
#pragma once
#ifndef MESSMER_CRYFS_PROGRAMOPTIONS_PROGRAMOPTIONS_H
#define MESSMER_CRYFS_PROGRAMOPTIONS_PROGRAMOPTIONS_H
#ifndef MESSMER_CRYFSCLI_PROGRAMOPTIONS_PROGRAMOPTIONS_H
#define MESSMER_CRYFSCLI_PROGRAMOPTIONS_PROGRAMOPTIONS_H
#include <vector>
#include <string>
@ -8,7 +8,7 @@
#include <cpp-utils/macros.h>
#include <boost/filesystem.hpp>
namespace cryfs {
namespace cryfs_cli {
namespace program_options {
class ProgramOptions final {
public:

View File

@ -7,7 +7,7 @@ using std::make_pair;
using std::vector;
using std::string;
namespace cryfs {
namespace cryfs_cli {
namespace program_options {
pair<vector<string>, vector<string>> splitAtDoubleDash(const vector<string> &options) {
auto doubleDashIterator = std::find(options.begin(), options.end(), string("--"));

View File

@ -1,12 +1,12 @@
#pragma once
#ifndef MESSMER_CRYFS_PROGRAMOPTIONS_UTILS_H
#define MESSMER_CRYFS_PROGRAMOPTIONS_UTILS_H
#ifndef MESSMER_CRYFSCLI_PROGRAMOPTIONS_UTILS_H
#define MESSMER_CRYFSCLI_PROGRAMOPTIONS_UTILS_H
#include <utility>
#include <vector>
#include <string>
namespace cryfs {
namespace cryfs_cli {
namespace program_options {
/**
* Splits an array of program options into two arrays of program options, split at a double dash '--' option.

View File

@ -0,0 +1,24 @@
project (cryfs-unmount)
INCLUDE(GNUInstallDirs)
set(SOURCES
program_options/ProgramOptions.cpp
program_options/Parser.cpp
Cli.cpp
)
add_library(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils cryfs fspp-fuse)
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})
add_executable(${PROJECT_NAME}_bin main_unmount.cpp)
set_target_properties(${PROJECT_NAME}_bin PROPERTIES OUTPUT_NAME cryfs-unmount)
target_link_libraries(${PROJECT_NAME}_bin PUBLIC ${PROJECT_NAME})
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}
)

36
src/cryfs-unmount/Cli.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "Cli.h"
#include <fspp/fuse/Fuse.h>
#include <cryfs-unmount/program_options/Parser.h>
#include <gitversion/gitversion.h>
#include <cryfs/CryfsException.h>
#include <iostream>
using fspp::fuse::Fuse;
using cryfs_unmount::program_options::Parser;
using cryfs_unmount::program_options::ProgramOptions;
namespace cryfs_unmount {
namespace {
void _showVersion() {
std::cout << "CryFS Version " << gitversion::VersionString() << std::endl;
}
}
void Cli::main(int argc, const char* argv[]) {
_showVersion();
ProgramOptions options = Parser(argc, argv).parse();
if (!boost::filesystem::exists(options.mountDir())) {
throw cryfs::CryfsException("Given mountdir doesn't exist", cryfs::ErrorCode::InaccessibleMountDir);
}
// TODO This doesn't seem to work with relative paths
std::cout << "Unmounting CryFS filesystem at " << options.mountDir() << "." << std::endl;
Fuse::unmount(options.mountDir());
// TODO Wait until it is actually unmounted and then show a better success message?
std::cout << "Filesystem is unmounting now." << std::endl;
}
}

14
src/cryfs-unmount/Cli.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#ifndef MESSMER_CRYFSUNMOUNT_CLI_H
#define MESSMER_CRYFSUNMOUNT_CLI_H
namespace cryfs_unmount {
class Cli final {
public:
void main(int argc, const char* argv[]);
};
}
#endif

View File

@ -0,0 +1,38 @@
#if defined(_MSC_VER)
#include <Windows.h>
#include <VersionHelpers.h>
#endif
#include <iostream>
#include <cryfs/CryfsException.h>
#include <cpp-utils/assert/backtrace.h>
#include "Cli.h"
using std::cerr;
using cryfs::ErrorCode;
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
cpputils::showBacktraceOnCrash();
try {
cryfs_unmount::Cli().main(argc, argv);
}
catch (const cryfs::CryfsException &e) {
if (e.what() != std::string()) {
std::cerr << "Error " << static_cast<int>(e.errorCode()) << ": " << e.what() << std::endl;
}
return exitCode(e.errorCode());
}
catch (const std::runtime_error &e) {
std::cerr << "Error: " << e.what() << std::endl;
return exitCode(ErrorCode::UnspecifiedError);
}
return exitCode(ErrorCode::Success);
}

View File

@ -0,0 +1,128 @@
#include "Parser.h"
#include <iostream>
#include <boost/optional.hpp>
#include <cryfs/config/CryConfigConsole.h>
#include <cryfs/CryfsException.h>
#include <cryfs-cli/Environment.h>
namespace po = boost::program_options;
namespace bf = boost::filesystem;
using namespace cryfs_unmount::program_options;
using cryfs::CryConfigConsole;
using cryfs::CryfsException;
using cryfs::ErrorCode;
using std::vector;
using std::cerr;
using std::endl;
using std::string;
using namespace cpputils::logging;
Parser::Parser(int argc, const char *argv[])
:_options(_argsToVector(argc, argv)) {
}
vector<string> Parser::_argsToVector(int argc, const char *argv[]) {
vector<string> result;
for (int i = 0; i < argc; ++i) {
result.push_back(argv[i]);
}
return result;
}
ProgramOptions Parser::parse() const {
po::variables_map vm = _parseOptionsOrShowHelp(_options);
if (!vm.count("mount-dir")) {
_showHelpAndExit("Please specify a mount directory.", ErrorCode::InvalidArguments);
}
bf::path mountDir = vm["mount-dir"].as<string>();
return ProgramOptions(std::move(mountDir));
}
po::variables_map Parser::_parseOptionsOrShowHelp(const vector<string> &options) {
try {
return _parseOptions(options);
}
catch (const CryfsException& e) {
// If CryfsException is thrown, we already know what's wrong.
// Show usage information and pass through the exception, don't catch it.
if (e.errorCode() != ErrorCode::Success) {
_showHelp();
}
throw;
}
catch (const std::exception &e) {
std::cerr << e.what() << std::endl;
_showHelpAndExit("Invalid arguments", ErrorCode::InvalidArguments);
}
}
po::variables_map Parser::_parseOptions(const vector<string> &options) {
po::options_description desc;
po::positional_options_description positional_desc;
_addAllowedOptions(&desc);
_addPositionalOptionForBaseDir(&desc, &positional_desc);
po::variables_map vm;
vector<const char*> _options = _to_const_char_vector(options);
po::store(po::command_line_parser(_options.size(), _options.data())
.options(desc).positional(positional_desc).run(), vm);
if (vm.count("help")) {
_showHelpAndExit("", ErrorCode::Success);
}
if (vm.count("version")) {
_showVersionAndExit();
}
po::notify(vm);
return vm;
}
vector<const char*> Parser::_to_const_char_vector(const vector<string> &options) {
vector<const char*> result;
result.reserve(options.size());
for (const string &option : options) {
result.push_back(option.c_str());
}
return result;
}
void Parser::_addAllowedOptions(po::options_description *desc) {
po::options_description options("Allowed options");
string cipher_description = "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers. Default: ";
cipher_description += CryConfigConsole::DEFAULT_CIPHER;
string blocksize_description = "The block size used when storing ciphertext blocks (in bytes). Default: ";
blocksize_description += std::to_string(CryConfigConsole::DEFAULT_BLOCKSIZE_BYTES);
options.add_options()
("help,h", "show help message")
("version", "Show CryFS version number")
;
desc->add(options);
}
void Parser::_addPositionalOptionForBaseDir(po::options_description *desc, po::positional_options_description *positional) {
positional->add("mount-dir", 1);
po::options_description hidden("Hidden options");
hidden.add_options()
("mount-dir", po::value<string>(), "Mount directory")
;
desc->add(hidden);
}
void Parser::_showHelp() {
cerr << "Usage: cryfs-unmount [mountPoint]\n";
po::options_description desc;
_addAllowedOptions(&desc);
cerr << desc << endl;
}
[[noreturn]] void Parser::_showHelpAndExit(const std::string& message, ErrorCode errorCode) {
_showHelp();
throw CryfsException(message, errorCode);
}
[[noreturn]] void Parser::_showVersionAndExit() {
// no need to show version because it was already shown in the CryFS header before parsing program options
throw CryfsException("", ErrorCode::Success);
}

View File

@ -0,0 +1,37 @@
#pragma once
#ifndef MESSMER_CRYFSUNMOUNT_PROGRAMOPTIONS_PARSER_H
#define MESSMER_CRYFSUNMOUNT_PROGRAMOPTIONS_PARSER_H
#include "ProgramOptions.h"
#include <boost/program_options.hpp>
#include <cryfs/ErrorCodes.h>
namespace cryfs_unmount {
namespace program_options {
class Parser final {
public:
Parser(int argc, const char *argv[]);
ProgramOptions parse() const;
private:
static std::vector<std::string> _argsToVector(int argc, const char *argv[]);
static std::vector<const char*> _to_const_char_vector(const std::vector<std::string> &options);
static void _addAllowedOptions(boost::program_options::options_description *desc);
static void _addPositionalOptionForBaseDir(boost::program_options::options_description *desc,
boost::program_options::positional_options_description *positional);
static void _showHelp();
[[noreturn]] static void _showHelpAndExit(const std::string& message, cryfs::ErrorCode errorCode);
[[noreturn]] static void _showCiphersAndExit(const std::vector<std::string> &supportedCiphers);
[[noreturn]] static void _showVersionAndExit();
static boost::program_options::variables_map _parseOptionsOrShowHelp(const std::vector<std::string> &options);
static boost::program_options::variables_map _parseOptions(const std::vector<std::string> &options);
std::vector<std::string> _options;
DISALLOW_COPY_AND_ASSIGN(Parser);
};
}
}
#endif

View File

@ -0,0 +1,25 @@
#include "ProgramOptions.h"
#include <cstring>
#include <cpp-utils/assert/assert.h>
#include <cpp-utils/system/path.h>
using namespace cryfs_unmount::program_options;
using std::string;
namespace bf = boost::filesystem;
ProgramOptions::ProgramOptions(bf::path mountDir)
:_mountDir(std::move(mountDir)),
_mountDirIsDriveLetter(cpputils::path_is_just_drive_letter(_mountDir))
{
if (!_mountDirIsDriveLetter) {
_mountDir = bf::absolute(std::move(_mountDir));
}
}
const bf::path &ProgramOptions::mountDir() const {
return _mountDir;
}
bool ProgramOptions::mountDirIsDriveLetter() const {
return _mountDirIsDriveLetter;
}

View File

@ -0,0 +1,30 @@
#pragma once
#ifndef MESSMER_CRYFSUNMOUNT_PROGRAMOPTIONS_PROGRAMOPTIONS_H
#define MESSMER_CRYFSUNMOUNT_PROGRAMOPTIONS_PROGRAMOPTIONS_H
#include <vector>
#include <string>
#include <boost/optional.hpp>
#include <cpp-utils/macros.h>
#include <boost/filesystem.hpp>
namespace cryfs_unmount {
namespace program_options {
class ProgramOptions final {
public:
ProgramOptions(boost::filesystem::path mountDir);
ProgramOptions(ProgramOptions &&rhs) = default;
const boost::filesystem::path &mountDir() const;
bool mountDirIsDriveLetter() const;
private:
boost::filesystem::path _mountDir;
bool _mountDirIsDriveLetter;
DISALLOW_COPY_AND_ASSIGN(ProgramOptions);
};
}
}
#endif

View File

@ -7,9 +7,15 @@
#include <iostream>
#include <cpp-utils/assert/assert.h>
#include <cpp-utils/logging/logging.h>
#include <cpp-utils/process/subprocess.h>
#include <csignal>
#include "InvalidFilesystem.h"
#if defined(_MSC_VER)
#include <codecvt>
#include <dokan/dokan.h>
#endif
using std::vector;
using std::string;
@ -307,14 +313,24 @@ bool Fuse::running() const {
}
void Fuse::stop() {
unmount(_mountdir, false);
}
void Fuse::unmount(const bf::path& mountdir, bool force) {
//TODO Find better way to unmount (i.e. don't use external fusermount). Unmounting by kill(getpid(), SIGINT) worked, but left the mount directory transport endpoint as not connected.
#ifdef __APPLE__
int ret = system(("umount " + _mountdir.string()).c_str());
#if defined(__APPLE__)
int returncode = cpputils::Subprocess::call(std::string("umount ") + mountdir.string()).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());
int returncode = success ? 0 : -1;
#else
int ret = system(("fusermount -z -u " + _mountdir.string()).c_str()); // "-z" takes care that if the filesystem can't be unmounted right now because something is opened, it will be unmounted as soon as it can be.
std::string command = force ? "fusermount -u" : "fusermount -z -u"; // "-z" takes care that if the filesystem can't be unmounted right now because something is opened, it will be unmounted as soon as it can be.
int returncode = cpputils::Subprocess::call(
command + " " + mountdir.string()).exitcode;
#endif
if (ret != 0) {
LOG(ERR, "Could not unmount filesystem");
if (returncode != 0) {
throw std::runtime_error("Could not unmount filesystem");
}
}

View File

@ -28,6 +28,8 @@ public:
bool running() const;
void stop();
static void unmount(const boost::filesystem::path &mountdir, bool force = false);
int getattr(const boost::filesystem::path &path, fspp::fuse::STAT *stbuf);
int fgetattr(const boost::filesystem::path &path, fspp::fuse::STAT *stbuf, fuse_file_info *fileinfo);
int readlink(const boost::filesystem::path &path, char *buf, size_t size);

View File

@ -49,6 +49,7 @@ set(SOURCES
assert/assert_debug_test.cpp
system/GetTotalMemoryTest.cpp
system/TimeTest.cpp
system/PathTest.cpp
system/FiletimeTest.cpp
system/MemoryTest.cpp
system/HomedirTest.cpp

View File

@ -0,0 +1,32 @@
#include <gtest/gtest.h>
#include <cpp-utils/system/path.h>
using cpputils::path_is_just_drive_letter;
#if defined(_MSC_VER)
TEST(PathTest, pathIsJustDriveLetter) {
EXPECT_FALSE(path_is_just_drive_letter("C"));
EXPECT_TRUE(path_is_just_drive_letter("C:"));
EXPECT_FALSE(path_is_just_drive_letter("C:\\"));
EXPECT_FALSE(path_is_just_drive_letter("C:/"));
EXPECT_FALSE(path_is_just_drive_letter("C:\\test"));
EXPECT_FALSE(path_is_just_drive_letter("C:\\test\\"));
EXPECT_FALSE(path_is_just_drive_letter("/"));
EXPECT_FALSE(path_is_just_drive_letter(""));
}
#else
TEST(PathTest, onNonWindowsWeDontHaveDriveLetterPaths) {
EXPECT_FALSE(path_is_just_drive_letter("C"));
EXPECT_FALSE(path_is_just_drive_letter("C:"));
EXPECT_FALSE(path_is_just_drive_letter("C:\\"));
EXPECT_FALSE(path_is_just_drive_letter("C:/"));
EXPECT_FALSE(path_is_just_drive_letter("C:\\test"));
EXPECT_FALSE(path_is_just_drive_letter("C:\\test\\"));
EXPECT_FALSE(path_is_just_drive_letter("/"));
EXPECT_FALSE(path_is_just_drive_letter(""));
}
#endif

View File

@ -12,12 +12,12 @@ set(SOURCES
EnvironmentTest.cpp
VersionCheckerTest.cpp
CliTest_IntegrityCheck.cpp
CryfsUnmountTest.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} googletest cryfs-cli)
target_link_libraries(${PROJECT_NAME} googletest cryfs-cli cryfs-unmount fspp-fuse)
add_test(${PROJECT_NAME} ${PROJECT_NAME})
target_enable_style_warnings(${PROJECT_NAME})
target_activate_cpp14(${PROJECT_NAME})

View File

@ -8,7 +8,7 @@ using cpputils::make_unique_ref;
using boost::chrono::milliseconds;
using boost::chrono::minutes;
using boost::this_thread::sleep_for;
using namespace cryfs;
using namespace cryfs_cli;
class CallAfterTimeoutTest : public ::testing::Test {
public:

View File

@ -0,0 +1,25 @@
#include "testutils/CliTest.h"
#include <cryfs-cli/Cli.h>
#include <cryfs-unmount/Cli.h>
using CliTest_Unmount = CliTest;
namespace bf = boost::filesystem;
namespace {
void unmount(const bf::path& mountdir) {
std::vector<const char*> _args = {"cryfs-unmount", mountdir.string().c_str()};
cryfs_unmount::Cli().main(2, _args.data());
}
TEST_F(CliTest_Unmount, givenMountedFilesystem_whenUnmounting_thenSucceeds) {
// we're passing in boost::none as mountdir so EXPECT_RUN_SUCCESS doesn't unmount itself.
// if the unmount we're calling here in the onMounted callback wouldn't work, EXPECT_RUN_SUCCESS
// would never return and this would be a deadlock.
EXPECT_RUN_SUCCESS({basedir.string().c_str(), mountdir.string().c_str(), "-f"}, boost::none, [this] () {
unmount(mountdir);
});
}
// TODO Test calling with invalid args, valid '--version' or '--help' args, with a non-mounted mountdir and a nonexisting mountdir.
}

View File

@ -4,7 +4,7 @@
#include <boost/filesystem.hpp>
#include <cpp-utils/system/env.h>
using namespace cryfs;
using namespace cryfs_cli;
using std::string;
using boost::optional;
using boost::none;

View File

@ -8,7 +8,7 @@ using cpputils::FakeHttpClient;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using boost::none;
using namespace cryfs;
using namespace cryfs_cli;
class VersionCheckerTest: public ::testing::Test {
public:

View File

@ -7,7 +7,7 @@
#include <cpp-utils/testutils/CaptureStderrRAII.h>
using namespace cryfs;
using namespace cryfs::program_options;
using namespace cryfs_cli::program_options;
using std::vector;
using std::string;
using boost::none;

View File

@ -2,7 +2,7 @@
#include <cryfs-cli/program_options/ProgramOptions.h>
#include <cpp-utils/pointer/unique_ref_boost_optional_gtest_workaround.h>
using namespace cryfs::program_options;
using namespace cryfs_cli::program_options;
using boost::none;
using boost::optional;
using std::ostream;

View File

@ -1,7 +1,7 @@
#include "testutils/ProgramOptionsTestBase.h"
#include <cryfs-cli/program_options/utils.h>
using namespace cryfs::program_options;
using namespace cryfs_cli::program_options;
using std::pair;
using std::vector;
using std::string;

View File

@ -19,6 +19,7 @@
#include <cpp-utils/lock/ConditionBarrier.h>
#include "../../cryfs/testutils/MockConsole.h"
#include "../../cryfs/testutils/TestWithFakeHomeDirectory.h"
#include <fspp/fuse/Fuse.h>
#include <cryfs/ErrorCodes.h>
#include <cpp-utils/testutils/CaptureStderrRAII.h>
#include <regex>
@ -45,7 +46,7 @@ public:
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");
_args.emplace_back("cryfs");
for (const std::string& arg : args) {
_args.emplace_back(arg.c_str());
}
@ -53,7 +54,7 @@ public:
ON_CALL(*console, askPassword(testing::StrEq("Password: "))).WillByDefault(testing::Return("pass"));
ON_CALL(*console, askPassword(testing::StrEq("Confirm Password: "))).WillByDefault(testing::Return("pass"));
// Run Cryfs
return cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data(), _httpClient(), std::move(onMounted));
return cryfs_cli::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) {
@ -90,18 +91,7 @@ public:
};
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);
fspp::fuse::Fuse::unmount(mountDir, true);
}
FilesystemOutput run_filesystem(const std::vector<std::string>& args, boost::optional<boost::filesystem::path> mountDirForUnmounting, std::function<void()> onMounted) {