2015-10-29 10:47:14 +01:00
|
|
|
#include "Cli.h"
|
|
|
|
|
2017-07-18 23:49:51 +02:00
|
|
|
#include <blockstore/implementations/ondisk/OnDiskBlockStore2.h>
|
2015-10-29 10:47:14 +01:00
|
|
|
#include <cmath>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
2016-02-11 16:39:42 +01:00
|
|
|
#include <cpp-utils/assert/backtrace.h>
|
2015-10-29 10:47:14 +01:00
|
|
|
|
2016-02-11 16:39:42 +01:00
|
|
|
#include <fspp/impl/FilesystemImpl.h>
|
|
|
|
#include <cpp-utils/process/subprocess.h>
|
2016-02-12 23:18:13 +01:00
|
|
|
#include <cpp-utils/io/DontEchoStdinToStdoutRAII.h>
|
2019-01-26 08:38:34 +01:00
|
|
|
#include <cryfs/impl/filesystem/CryDevice.h>
|
|
|
|
#include <cryfs/impl/config/CryConfigLoader.h>
|
2022-06-18 21:11:24 +02:00
|
|
|
#include <cryfs/impl/config/CryPresetPasswordBasedKeyProvider.h>
|
2015-11-17 10:49:35 +01:00
|
|
|
#include <boost/filesystem.hpp>
|
2015-10-29 10:47:14 +01:00
|
|
|
|
2019-01-26 08:38:34 +01:00
|
|
|
#include <cryfs/impl/filesystem/CryDir.h>
|
2016-03-02 13:53:37 +01:00
|
|
|
#include <gitversion/gitversion.h>
|
2015-10-29 10:47:14 +01:00
|
|
|
|
2015-11-23 17:43:21 +01:00
|
|
|
#include "VersionChecker.h"
|
2016-03-26 17:09:07 +01:00
|
|
|
#include <gitversion/VersionCompare.h>
|
2016-09-24 20:28:56 +02:00
|
|
|
#include <cpp-utils/io/NoninteractiveConsole.h>
|
2019-01-26 08:38:34 +01:00
|
|
|
#include <cryfs/impl/localstate/LocalStateDir.h>
|
|
|
|
#include <cryfs/impl/localstate/BasedirMetadata.h>
|
2016-02-21 22:15:27 +01:00
|
|
|
#include "Environment.h"
|
2019-01-26 08:38:34 +01:00
|
|
|
#include <cryfs/impl/CryfsException.h>
|
2019-01-20 12:25:21 +01:00
|
|
|
#include <cpp-utils/thread/debugging.h>
|
2015-11-23 17:43:21 +01:00
|
|
|
|
2015-10-30 17:23:08 +01:00
|
|
|
//TODO Many functions accessing the ProgramOptions object. Factor out into class that stores it as a member.
|
2015-11-19 10:08:09 +01:00
|
|
|
//TODO Factor out class handling askPassword
|
2015-10-30 17:23:08 +01:00
|
|
|
|
2019-01-19 22:02:41 +01:00
|
|
|
using namespace cryfs_cli;
|
2015-10-29 10:47:14 +01:00
|
|
|
using namespace cryfs;
|
|
|
|
namespace bf = boost::filesystem;
|
2015-10-30 20:32:25 +01:00
|
|
|
using namespace cpputils::logging;
|
2015-10-29 10:47:14 +01:00
|
|
|
|
2017-07-18 23:49:51 +02:00
|
|
|
using blockstore::ondisk::OnDiskBlockStore2;
|
2015-10-29 10:47:14 +01:00
|
|
|
using program_options::ProgramOptions;
|
|
|
|
|
|
|
|
using cpputils::make_unique_ref;
|
2016-09-24 20:28:56 +02:00
|
|
|
using cpputils::NoninteractiveConsole;
|
2015-10-30 18:28:33 +01:00
|
|
|
using cpputils::TempFile;
|
2015-11-04 05:27:00 +01:00
|
|
|
using cpputils::RandomGenerator;
|
|
|
|
using cpputils::unique_ref;
|
2018-10-22 04:31:08 +02:00
|
|
|
using cpputils::SCrypt;
|
2020-07-19 22:26:13 +02:00
|
|
|
using cpputils::either;
|
2015-11-04 05:27:00 +01:00
|
|
|
using cpputils::SCryptSettings;
|
2016-01-25 15:01:34 +01:00
|
|
|
using cpputils::Console;
|
2016-01-28 18:55:26 +01:00
|
|
|
using cpputils::HttpClient;
|
2015-10-29 10:47:14 +01:00
|
|
|
using std::string;
|
|
|
|
using std::endl;
|
2015-10-30 18:28:33 +01:00
|
|
|
using std::shared_ptr;
|
|
|
|
using std::make_shared;
|
2018-12-11 06:20:18 +01:00
|
|
|
using std::unique_ptr;
|
2018-12-09 18:27:53 +01:00
|
|
|
using std::make_unique;
|
2015-11-13 00:06:53 +01:00
|
|
|
using std::function;
|
|
|
|
using boost::optional;
|
2015-10-29 10:47:14 +01:00
|
|
|
using boost::none;
|
2015-11-13 00:06:53 +01:00
|
|
|
using boost::chrono::minutes;
|
|
|
|
using boost::chrono::milliseconds;
|
2016-02-19 02:10:10 +01:00
|
|
|
using cpputils::dynamic_pointer_move;
|
2016-03-26 17:09:07 +01:00
|
|
|
using gitversion::VersionCompare;
|
2015-10-29 10:47:14 +01:00
|
|
|
|
2015-12-11 00:27:26 +01:00
|
|
|
//TODO Delete a large file in parallel possible? Takes a long time right now...
|
2015-10-29 10:47:14 +01:00
|
|
|
//TODO Improve parallelity.
|
|
|
|
//TODO Replace ASSERTs with other error handling when it is not a programming error but an environment influence (e.g. a block is missing)
|
2016-03-02 01:36:04 +01:00
|
|
|
//TODO Can we improve performance by setting compiler parameter -maes for scrypt?
|
2015-10-29 10:47:14 +01:00
|
|
|
|
2019-01-19 22:02:41 +01:00
|
|
|
namespace cryfs_cli {
|
2015-10-29 10:47:14 +01:00
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings): _keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _idleUnmounter(none), _device(none) {}
|
2016-02-12 23:18:13 +01:00
|
|
|
|
2015-10-29 10:47:14 +01:00
|
|
|
bf::path Cli::_determineConfigFile(const ProgramOptions &options) {
|
|
|
|
auto configFile = options.configFile();
|
|
|
|
if (configFile == none) {
|
|
|
|
return bf::path(options.baseDir()) / "cryfs.config";
|
|
|
|
}
|
|
|
|
return *configFile;
|
|
|
|
}
|
|
|
|
|
2018-04-22 07:04:21 +02:00
|
|
|
void Cli::_checkConfigIntegrity(const bf::path& basedir, const LocalStateDir& localStateDir, const CryConfigFile& config, bool allowReplacedFilesystem) {
|
|
|
|
auto basedirMetadata = BasedirMetadata::load(localStateDir);
|
2018-02-03 17:33:59 +01:00
|
|
|
if (!allowReplacedFilesystem && !basedirMetadata.filesystemIdForBasedirIsCorrect(basedir, config.config()->FilesystemId())) {
|
2022-06-18 21:11:24 +02:00
|
|
|
throw CryfsException(
|
|
|
|
"The filesystem id in the config file is different to the last time we loaded a filesystem from this basedir.", ErrorCode::FilesystemIdChanged);
|
2017-09-28 08:41:08 +02:00
|
|
|
}
|
|
|
|
// Update local state (or create it if it didn't exist yet)
|
2017-09-28 09:19:30 +02:00
|
|
|
basedirMetadata.updateFilesystemIdForBasedir(basedir, config.config()->FilesystemId());
|
|
|
|
basedirMetadata.save();
|
2017-09-28 08:41:08 +02:00
|
|
|
}
|
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
CryConfigLoader::ConfigLoadResult Cli::_loadOrCreateConfig(const ProgramOptions &options, const LocalStateDir& localStateDir, unique_ptr<string> password) {
|
2018-02-02 01:08:01 +01:00
|
|
|
auto configFile = _determineConfigFile(options);
|
2022-06-18 21:11:24 +02:00
|
|
|
auto config = _loadOrCreateConfigFile(std::move(configFile), localStateDir, std::move(password), options.cipher(), options.blocksizeBytes(), options.allowFilesystemUpgrade(), options.missingBlockIsIntegrityViolation(), options.allowReplacedFilesystem());
|
2020-07-19 22:26:13 +02:00
|
|
|
if (config.is_left()) {
|
|
|
|
switch(config.left()) {
|
|
|
|
case CryConfigFile::LoadError::DecryptionFailed:
|
|
|
|
throw CryfsException("Failed to decrypt the config file. Did you enter the correct password?", ErrorCode::WrongPassword);
|
|
|
|
case CryConfigFile::LoadError::ConfigFileNotFound:
|
|
|
|
throw CryfsException("Could not find the cryfs.config file. Are you sure this is a valid CryFS file system?", ErrorCode::InvalidFilesystem);
|
|
|
|
}
|
2015-10-29 10:47:14 +01:00
|
|
|
}
|
2020-07-19 22:26:13 +02:00
|
|
|
_checkConfigIntegrity(options.baseDir(), localStateDir, *config.right().configFile, options.allowReplacedFilesystem());
|
|
|
|
return std::move(config.right());
|
2015-10-29 10:47:14 +01:00
|
|
|
}
|
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
either<CryConfigFile::LoadError, CryConfigLoader::ConfigLoadResult> Cli::_loadOrCreateConfigFile(bf::path configFilePath, LocalStateDir localStateDir, unique_ptr<string> password, const optional<string> &cipher, const optional<uint32_t> &blocksizeBytes, bool allowFilesystemUpgrade, const optional<bool> &missingBlockIsIntegrityViolation, bool allowReplacedFilesystem) {
|
2018-10-22 04:31:08 +02:00
|
|
|
// TODO Instead of passing in _askPasswordXXX functions to KeyProvider, only pass in console and move logic to the key provider,
|
|
|
|
// for example by having a separate CryPasswordBasedKeyProvider / CryNoninteractivePasswordBasedKeyProvider.
|
2022-06-18 21:11:24 +02:00
|
|
|
auto keyProvider = make_unique_ref<CryPresetPasswordBasedKeyProvider>(
|
|
|
|
*password.get(),
|
2018-10-22 04:31:08 +02:00
|
|
|
make_unique_ref<SCrypt>(_scryptSettings)
|
|
|
|
);
|
2022-06-18 21:11:24 +02:00
|
|
|
return CryConfigLoader(_keyGenerator, std::move(keyProvider), std::move(localStateDir), cipher, blocksizeBytes, missingBlockIsIntegrityViolation).loadOrCreate(std::move(configFilePath), allowFilesystemUpgrade, allowReplacedFilesystem);
|
2020-07-20 01:57:37 +02:00
|
|
|
}
|
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
fspp::fuse::Fuse* Cli::initFilesystem(const ProgramOptions &options, unique_ptr<string> password) {
|
2022-06-19 17:54:18 +02:00
|
|
|
cpputils::showBacktraceOnCrash();
|
|
|
|
cpputils::set_thread_name("cryfs");
|
2015-10-30 20:32:25 +01:00
|
|
|
try {
|
2022-06-19 17:54:18 +02:00
|
|
|
_sanityChecks(options);
|
2022-06-18 21:11:24 +02:00
|
|
|
LocalStateDir localStateDir(options.localStateDir());
|
2018-12-03 07:57:21 +01:00
|
|
|
auto blockStore = make_unique_ref<OnDiskBlockStore2>(options.baseDir());
|
2022-06-18 21:11:24 +02:00
|
|
|
auto config = _loadOrCreateConfig(options, localStateDir, std::move(password));
|
|
|
|
fspp::fuse::Fuse* fuse = nullptr;
|
2018-12-19 06:40:03 +01:00
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
auto onIntegrityViolation = [&fuse] () {
|
|
|
|
if (fuse != nullptr) {
|
2018-12-11 06:20:18 +01:00
|
|
|
LOG(ERR, "Integrity violation detected. Unmounting.");
|
2022-06-18 21:11:24 +02:00
|
|
|
fuse->destroy();
|
2018-12-11 06:20:18 +01:00
|
|
|
} else {
|
2018-12-19 06:40:03 +01:00
|
|
|
// 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
|
2018-12-11 06:20:18 +01:00
|
|
|
// 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.
|
2018-12-19 06:40:03 +01:00
|
|
|
throw CryfsException("Integrity violation detected. Unmounting.", ErrorCode::IntegrityViolation);
|
2018-12-11 06:20:18 +01:00
|
|
|
}
|
|
|
|
};
|
2018-12-22 00:58:30 +01:00
|
|
|
const bool missingBlockIsIntegrityViolation = config.configFile->config()->missingBlockIsIntegrityViolation();
|
2019-10-13 13:29:59 +02:00
|
|
|
_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)));
|
2018-12-03 07:57:21 +01:00
|
|
|
_sanityCheckFilesystem(_device->get());
|
2015-10-30 20:32:25 +01:00
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
auto initFilesystem = [&] (){
|
2018-12-03 07:57:21 +01:00
|
|
|
ASSERT(_device != none, "File system not ready to be initialized. Was it already initialized before?");
|
|
|
|
return make_shared<fspp::FilesystemImpl>(std::move(*_device));
|
|
|
|
};
|
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
fuse = new fspp::fuse::Fuse(initFilesystem, "cryfs", "cryfs@" + options.baseDir().string());
|
2015-11-13 00:06:53 +01:00
|
|
|
|
2022-06-18 21:11:24 +02:00
|
|
|
fuse->init();
|
|
|
|
return fuse;
|
2018-02-02 03:09:28 +01:00
|
|
|
} catch (const CryfsException &e) {
|
2022-06-18 21:11:24 +02:00
|
|
|
if (e.what() != string()) {
|
|
|
|
LOG(ERR, "Error {}: {}", static_cast<int>(e.errorCode()), e.what());
|
|
|
|
}
|
2015-10-30 20:32:25 +01:00
|
|
|
} catch (const std::exception &e) {
|
2018-05-17 08:03:03 +02:00
|
|
|
LOG(ERR, "Crashed: {}", e.what());
|
2015-10-30 20:32:25 +01:00
|
|
|
} catch (...) {
|
2018-05-17 08:03:03 +02:00
|
|
|
LOG(ERR, "Crashed");
|
2015-10-30 20:32:25 +01:00
|
|
|
}
|
2022-06-18 21:11:24 +02:00
|
|
|
|
|
|
|
return nullptr;
|
2015-10-29 10:47:14 +01:00
|
|
|
}
|
|
|
|
|
2016-02-19 02:10:10 +01:00
|
|
|
void Cli::_sanityCheckFilesystem(CryDevice *device) {
|
|
|
|
//Try to list contents of base directory
|
|
|
|
auto _rootDir = device->Load("/"); // this might throw an exception if the root blob doesn't exist
|
|
|
|
if (_rootDir == none) {
|
2018-02-02 01:08:01 +01:00
|
|
|
throw CryfsException("Couldn't find root blob", ErrorCode::InvalidFilesystem);
|
2016-02-19 02:10:10 +01:00
|
|
|
}
|
|
|
|
auto rootDir = dynamic_pointer_move<CryDir>(*_rootDir);
|
|
|
|
if (rootDir == none) {
|
2018-02-02 01:08:01 +01:00
|
|
|
throw CryfsException("Base directory blob doesn't contain a directory", ErrorCode::InvalidFilesystem);
|
2016-02-19 02:10:10 +01:00
|
|
|
}
|
|
|
|
(*rootDir)->children(); // Load children
|
|
|
|
}
|
|
|
|
|
2022-06-19 17:54:18 +02:00
|
|
|
void Cli::_sanityChecks(const ProgramOptions &options) {
|
|
|
|
_checkDirAccessible(bf::absolute(options.baseDir()), "base directory", options.createMissingBasedir(), ErrorCode::InaccessibleBaseDir);
|
|
|
|
}
|
2015-10-29 15:52:49 +01:00
|
|
|
|
2022-06-19 17:54:18 +02:00
|
|
|
void Cli::_checkDirAccessible(const bf::path &dir, const std::string &name, bool createMissingDir, ErrorCode errorCode) {
|
|
|
|
if (!bf::exists(dir)) {
|
|
|
|
if (createMissingDir) {
|
|
|
|
LOG(INFO, "Automatically creating {}", name);
|
|
|
|
if (!bf::create_directory(dir)) {
|
|
|
|
throw CryfsException("Error creating "+name, errorCode);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//std::cerr << "Exit code: " << exitCode(errorCode) << std::endl;
|
|
|
|
throw CryfsException(name + " not found.", errorCode);
|
2018-02-02 01:08:01 +01:00
|
|
|
}
|
2015-10-29 10:47:14 +01:00
|
|
|
}
|
2022-06-19 17:54:18 +02:00
|
|
|
if (!bf::is_directory(dir)) {
|
|
|
|
throw CryfsException(name+" is not a directory.", errorCode);
|
|
|
|
}
|
|
|
|
}
|
2015-10-30 18:28:33 +01:00
|
|
|
}
|