diff --git a/src/cryfs/config/CryConfigLoader.cpp b/src/cryfs/config/CryConfigLoader.cpp index 12a0c2fa..7439608d 100644 --- a/src/cryfs/config/CryConfigLoader.cpp +++ b/src/cryfs/config/CryConfigLoader.cpp @@ -28,7 +28,7 @@ using namespace cpputils::logging; namespace cryfs { CryConfigLoader::CryConfigLoader(shared_ptr console, RandomGenerator &keyGenerator, const SCryptSettings &scryptSettings, function askPasswordForExistingFilesystem, function askPasswordForNewFilesystem, const optional &cipherFromCommandLine, const boost::optional &blocksizeBytesFromCommandLine, bool noninteractive) - : _creator(std::move(console), keyGenerator, noninteractive), _scryptSettings(scryptSettings), + : _console(console), _creator(console, keyGenerator, noninteractive), _scryptSettings(scryptSettings), _askPasswordForExistingFilesystem(askPasswordForExistingFilesystem), _askPasswordForNewFilesystem(askPasswordForNewFilesystem), _cipherFromCommandLine(cipherFromCommandLine), _blocksizeBytesFromCommandLine(blocksizeBytesFromCommandLine) { } @@ -57,12 +57,13 @@ optional CryConfigLoader::_loadConfig(const bf::path &filename) { } void CryConfigLoader::_checkVersion(const CryConfig &config) { - const string allowedVersionPrefix = string() + gitversion::MajorVersion() + "." + gitversion::MinorVersion() + "."; - if (!boost::starts_with(config.Version(), allowedVersionPrefix)) { - throw std::runtime_error(string() + "This filesystem was created with CryFS " + config.Version() + " and is incompatible. Please create a new one with your version of CryFS and migrate your data."); - } if (gitversion::VersionCompare::isOlderThan(gitversion::VersionString(), config.Version())) { - throw std::runtime_error(string() + "This filesystem was used with CryFS " + config.Version() + " and should not be opened with older versions anymore. Please update your CryFS version."); + throw std::runtime_error(string() + "This filesystem is for CryFS " + config.Version() + " and should not be opened with older versions. Please update your CryFS version."); + } + if (gitversion::VersionCompare::isOlderThan(config.Version(), gitversion::VersionString())) { + if (!_console->askYesNo("This filesystem is for CryFS " + config.Version() + ". It can be migrated to CryFS " + gitversion::VersionString() + ", but afterwards couldn't be opened anymore with older versions. Do you want to migrate it?")) { + throw std::runtime_error(string() + "Not migrating file system."); + } } } diff --git a/src/cryfs/config/CryConfigLoader.h b/src/cryfs/config/CryConfigLoader.h index 4741d9e2..2f9e2b17 100644 --- a/src/cryfs/config/CryConfigLoader.h +++ b/src/cryfs/config/CryConfigLoader.h @@ -21,9 +21,10 @@ public: private: boost::optional _loadConfig(const boost::filesystem::path &filename); CryConfigFile _createConfig(const boost::filesystem::path &filename); - static void _checkVersion(const CryConfig &config); + void _checkVersion(const CryConfig &config); void _checkCipher(const CryConfig &config) const; + std::shared_ptr _console; CryConfigCreator _creator; cpputils::SCryptSettings _scryptSettings; std::function _askPasswordForExistingFilesystem; diff --git a/test/cryfs/config/CryConfigLoaderTest.cpp b/test/cryfs/config/CryConfigLoaderTest.cpp index 2ee0f7d8..6f934a66 100644 --- a/test/cryfs/config/CryConfigLoaderTest.cpp +++ b/test/cryfs/config/CryConfigLoaderTest.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using cpputils::unique_ref; using cpputils::make_unique_ref; @@ -16,6 +17,7 @@ using std::string; using std::ostream; using ::testing::Return; using ::testing::_; +using ::testing::HasSubstr; using namespace cryfs; @@ -29,11 +31,13 @@ namespace boost { class CryConfigLoaderTest: public ::testing::Test, public TestWithMockConsole { public: - CryConfigLoaderTest(): file(false) {} + CryConfigLoaderTest(): file(false) { + console = mockConsole(); + } CryConfigLoader loader(const string &password, bool noninteractive, const optional &cipher = none) { auto askPassword = [password] { return password;}; - return CryConfigLoader(mockConsole(), cpputils::Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, cipher, none, noninteractive); + return CryConfigLoader(console, cpputils::Random::PseudoRandom(), SCrypt::TestSettings, askPassword, askPassword, cipher, none, noninteractive); } CryConfigFile Create(const string &password = "mypassword", const optional &cipher = none, bool noninteractive = false) { @@ -71,6 +75,24 @@ public: cfg.save(); } + string olderVersion() { + string olderVersion; + if (std::stol(gitversion::MinorVersion()) > 0) { + olderVersion = gitversion::MajorVersion() + "." + std::to_string(std::stol(gitversion::MinorVersion()) - 1); + } else { + olderVersion = std::to_string(std::stol(gitversion::MajorVersion()) - 1) + "." + gitversion::MinorVersion(); + } + assert(gitversion::VersionCompare::isOlderThan(olderVersion, gitversion::VersionString())); + return olderVersion; + } + + string newerVersion() { + string newerVersion = gitversion::MajorVersion()+"."+std::to_string(std::stol(gitversion::MinorVersion())+1); + assert(gitversion::VersionCompare::isOlderThan(gitversion::VersionString(), newerVersion)); + return newerVersion; + } + + std::shared_ptr console; TempFile file; }; @@ -176,3 +198,42 @@ TEST_F(CryConfigLoaderTest, Version_Create) { EXPECT_EQ(gitversion::VersionString(), created.config()->Version()); EXPECT_EQ(gitversion::VersionString(), created.config()->CreatedWithVersion()); } + +TEST_F(CryConfigLoaderTest, RefusesToLoadNewerFilesystem) { + string version = newerVersion(); + CreateWithVersion(version); + try { + Load(); + EXPECT_TRUE(false); // expect throw + } catch (const std::runtime_error &e) { + EXPECT_THAT(e.what(), HasSubstr("Please update your CryFS version")); + } +} + +TEST_F(CryConfigLoaderTest, AsksWhenMigratingOlderFilesystem) { + EXPECT_CALL(*console, askYesNo(HasSubstr("Do you want to migrate it?"))).Times(1).WillOnce(Return(true)); + + string version = olderVersion(); + CreateWithVersion(version); + EXPECT_NE(boost::none, Load()); +} + +TEST_F(CryConfigLoaderTest, DoesNotAskForMigrationWhenCorrectVersion) { + EXPECT_CALL(*console, askYesNo(HasSubstr("Do you want to migrate it?"))).Times(0); + + CreateWithVersion(gitversion::VersionString()); + EXPECT_NE(boost::none, Load()); +} + +TEST_F(CryConfigLoaderTest, DontMigrateWhenAnsweredNo) { + EXPECT_CALL(*console, askYesNo(HasSubstr("Do you want to migrate it?"))).Times(1).WillOnce(Return(false)); + + string version = olderVersion(); + CreateWithVersion(version); + try { + Load(); + EXPECT_TRUE(false); // expect throw + } catch (const std::runtime_error &e) { + EXPECT_THAT(e.what(), HasSubstr("Not migrating file system")); + } +}