diff --git a/ChangeLog.txt b/ChangeLog.txt index 732a0f5f..63023c14 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -2,6 +2,7 @@ Version 0.8.5 --------------- * Fix package manager warning when installing the .deb package * Offer a default configuration when creating new filesystems +* If the given base or mount directory doesn't exist, offer to create them Version 0.8.4 --------------- diff --git a/src/cli/Cli.cpp b/src/cli/Cli.cpp index 613a6596..16570619 100644 --- a/src/cli/Cli.cpp +++ b/src/cli/Cli.cpp @@ -47,6 +47,7 @@ using cpputils::TempFile; using cpputils::RandomGenerator; using cpputils::unique_ref; using cpputils::SCryptSettings; +using cpputils::Console; using std::cout; using std::string; using std::endl; @@ -71,8 +72,8 @@ using boost::chrono::milliseconds; namespace cryfs { - Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings): - _keyGenerator(keyGenerator), _scryptSettings(scryptSettings) {} + Cli::Cli(RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, shared_ptr console): + _keyGenerator(keyGenerator), _scryptSettings(scryptSettings), _console(console) {} void Cli::_showVersion() { cout << "CryFS Version " << version::VERSION_STRING << endl; @@ -174,8 +175,7 @@ namespace cryfs { CryConfigFile Cli::_loadOrCreateConfig(const ProgramOptions &options) { try { auto configFile = _determineConfigFile(options); - auto console = make_shared(); - auto config = CryConfigLoader(console, _keyGenerator, _scryptSettings, + auto config = CryConfigLoader(_console, _keyGenerator, _scryptSettings, std::bind(&Cli::_getPassword, this, std::cref(options), &Cli::_askPasswordForExistingFilesystem), std::bind(&Cli::_getPassword, this, std::cref(options), &Cli::_askPasswordForNewFilesystem), options.cipher()).loadOrCreate(configFile); @@ -247,7 +247,14 @@ namespace cryfs { void Cli::_checkDirAccessible(const bf::path &dir, const std::string &name) { if (!bf::exists(dir)) { - throw std::runtime_error(name+" not found."); + bool create = _console->askYesNo("Could not find " + name + ". Do you want to create it?"); + if (create) { + if (!bf::create_directory(dir)) { + throw std::runtime_error("Error creating "+name); + } + } else { + throw std::runtime_error(name + " not found."); + } } if (!bf::is_directory(dir)) { throw std::runtime_error(name+" is not a directory."); diff --git a/src/cli/Cli.h b/src/cli/Cli.h index 2f5d17b5..b0f2a693 100644 --- a/src/cli/Cli.h +++ b/src/cli/Cli.h @@ -6,13 +6,14 @@ #include "../config/CryConfigFile.h" #include #include +#include #include #include "CallAfterTimeout.h" namespace cryfs { class Cli final { public: - Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings); + Cli(cpputils::RandomGenerator &keyGenerator, const cpputils::SCryptSettings &scryptSettings, std::shared_ptr console); int main(int argc, char *argv[]); private: @@ -36,6 +37,7 @@ namespace cryfs { cpputils::RandomGenerator &_keyGenerator; cpputils::SCryptSettings _scryptSettings; + std::shared_ptr _console; DISALLOW_COPY_AND_ASSIGN(Cli); }; diff --git a/src/main.cpp b/src/main.cpp index cc05ba6d..14bc0562 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,8 +5,10 @@ using namespace cryfs; using cpputils::Random; using cpputils::SCrypt; +using std::make_shared; +using cpputils::IOStreamConsole; int main(int argc, char *argv[]) { auto &keyGenerator = Random::OSRandom(); - return Cli(keyGenerator, SCrypt::DefaultSettings).main(argc, argv); + return Cli(keyGenerator, SCrypt::DefaultSettings, make_shared()).main(argc, argv); } diff --git a/test/cli/CliTest_WrongEnvironment.cpp b/test/cli/CliTest_WrongEnvironment.cpp index f93aa62e..8a4b7526 100644 --- a/test/cli/CliTest_WrongEnvironment.cpp +++ b/test/cli/CliTest_WrongEnvironment.cpp @@ -3,6 +3,7 @@ namespace bf = boost::filesystem; using ::testing::Values; using ::testing::WithParamInterface; +using ::testing::Return; using std::vector; using cpputils::TempFile; @@ -117,9 +118,18 @@ TEST_P(CliTest_WrongEnvironment, MountDirIsBaseDir_BothRelative) { TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist) { _basedir.remove(); + ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?")).WillByDefault(Return(false)); // 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. Test_Run_Error("Error: base directory not found"); } +TEST_P(CliTest_WrongEnvironment, BaseDir_DoesntExist_Create) { + if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS) + _basedir.remove(); + ON_CALL(*console, askYesNo("Could not find base directory. Do you want to create it?")).WillByDefault(Return(true)); + Test_Run_Success(); + EXPECT_TRUE(bf::exists(_basedir.path()) && bf::is_directory(_basedir.path())); +} + TEST_P(CliTest_WrongEnvironment, BaseDir_IsNotDirectory) { TempFile basedirfile; basedir = basedirfile.path(); @@ -155,9 +165,18 @@ TEST_P(CliTest_WrongEnvironment, BaseDir_NoPermission) { TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist) { _mountdir.remove(); + ON_CALL(*console, askYesNo("Could not find mount directory. Do you want to create it?")).WillByDefault(Return(false)); // 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. Test_Run_Error("Error: mount directory not found"); } +TEST_P(CliTest_WrongEnvironment, MountDir_DoesntExist_Create) { + if (!GetParam().runningInForeground) {return;} // TODO Make this work also if run in background (see CliTest::EXPECT_RUN_SUCCESS) + _mountdir.remove(); + ON_CALL(*console, askYesNo("Could not find mount directory. Do you want to create it?")).WillByDefault(Return(true)); + Test_Run_Success(); + EXPECT_TRUE(bf::exists(_mountdir.path()) && bf::is_directory(_mountdir.path())); +} + TEST_P(CliTest_WrongEnvironment, MountDir_IsNotDirectory) { TempFile mountdirfile; mountdir = mountdirfile.path(); diff --git a/test/cli/testutils/CliTest.h b/test/cli/testutils/CliTest.h index 7826160c..69ac7ce2 100644 --- a/test/cli/testutils/CliTest.h +++ b/test/cli/testutils/CliTest.h @@ -9,10 +9,11 @@ #include "../../../src/cli/Cli.h" #include #include +#include "../../testutils/MockConsole.h" class CliTest : public ::testing::Test { public: - CliTest(): _basedir(), _mountdir(), basedir(_basedir.path()), mountdir(_mountdir.path()), logfile(), configfile(false) {} + CliTest(): _basedir(), _mountdir(), basedir(_basedir.path()), mountdir(_mountdir.path()), logfile(), configfile(false), console(std::make_shared()) {} cpputils::TempDir _basedir; cpputils::TempDir _mountdir; @@ -20,6 +21,7 @@ public: boost::filesystem::path mountdir; cpputils::TempFile logfile; cpputils::TempFile configfile; + std::shared_ptr console; void run(std::vector args) { std::vector _args; @@ -29,7 +31,7 @@ public: _args.push_back(const_cast(arg)); } auto &keyGenerator = cpputils::Random::PseudoRandom(); - cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings).main(_args.size(), _args.data()); + cryfs::Cli(keyGenerator, cpputils::SCrypt::TestSettings, console).main(_args.size(), _args.data()); } void EXPECT_EXIT_WITH_HELP_MESSAGE(std::vector args, const std::string &message = "") {