diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -108,7 +108,6 @@ const std::vector &extra_args = {}); ~BasicTestingSetup(); -private: const fs::path m_path_root; }; diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1366,6 +1366,7 @@ BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) { // Test writing setting. TestArgsManager args1; + args1.ForceSetArg("-datadir", m_path_root.string()); args1.LockSettings([&](util::Settings &settings) { settings.rw_settings["name"] = "value"; }); @@ -1373,6 +1374,7 @@ // Test reading setting. TestArgsManager args2; + args2.ForceSetArg("-datadir", m_path_root.string()); args2.ReadSettingsFile(); args2.LockSettings([&](util::Settings &settings) { BOOST_CHECK_EQUAL(settings.rw_settings["name"].get_str(), "value"); @@ -1381,10 +1383,10 @@ // Test error logging, and remove previously written setting. { ASSERT_DEBUG_LOG("Failed renaming settings file"); - fs::remove(GetDataDir() / "settings.json"); - fs::create_directory(GetDataDir() / "settings.json"); + fs::remove(args1.GetDataDirPath() / "settings.json"); + fs::create_directory(args1.GetDataDirPath() / "settings.json"); args2.WriteSettingsFile(); - fs::remove(GetDataDir() / "settings.json"); + fs::remove(args1.GetDataDirPath() / "settings.json"); } } diff --git a/src/util/system.h b/src/util/system.h --- a/src/util/system.h +++ b/src/util/system.h @@ -182,6 +182,8 @@ std::map> m_available_args GUARDED_BY(cs_args); std::list m_config_sections GUARDED_BY(cs_args); + mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args); + mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args); NODISCARD bool ReadConfigStream(std::istream &stream, const std::string &filepath, @@ -237,6 +239,21 @@ */ const std::list GetUnrecognizedSections() const; + /** + * Get data directory path + * + * @param net_specific Append network identifier to the returned path + * @return Absolute path on success, otherwise an empty path when a + * non-directory path would be returned + * @post Returned directory path is created unless it is empty + */ + const fs::path &GetDataDirPath(bool net_specific = true) const; + + /** + * For testing + */ + void ClearDatadirPathCache(); + /** * Return a vector of strings of the given argument * diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -401,6 +401,46 @@ return std::nullopt; } +const fs::path &ArgsManager::GetDataDirPath(bool net_specific) const { + LOCK(cs_args); + fs::path &path = + net_specific ? m_cached_network_datadir_path : m_cached_datadir_path; + + // Cache the path to avoid calling fs::create_directories on every call of + // this function + if (!path.empty()) { + return path; + } + std::string datadir = GetArg("-datadir", ""); + if (!datadir.empty()) { + path = fs::system_complete(datadir); + if (!fs::is_directory(path)) { + path = ""; + return path; + } + } else { + path = GetDefaultDataDir(); + } + if (net_specific) { + path /= BaseParams().DataDir(); + } + + if (fs::create_directories(path)) { + // This is the first run, create wallets subdirectory too + fs::create_directories(path / "wallets"); + } + + path = StripRedundantLastElementsOfPath(path); + return path; +} + +void ArgsManager::ClearDatadirPathCache() { + LOCK(cs_args); + + m_cached_datadir_path = fs::path(); + m_cached_network_datadir_path = fs::path(); +} + std::vector ArgsManager::GetArgs(const std::string &strArg) const { std::vector result; for (const util::SettingsValue &value : GetSettingsList(strArg)) { @@ -440,7 +480,7 @@ if (filepath) { std::string settings = GetArg("-settings", BITCOIN_SETTINGS_FILENAME); *filepath = fs::absolute(temp ? settings + ".tmp" : settings, - GetDataDir(/* net_specific= */ true)); + GetDataDirPath(/* net_specific= */ true)); } return true; } @@ -758,8 +798,6 @@ } static fs::path g_blocks_path_cache_net_specific; -static fs::path pathCached; -static fs::path pathCachedNetSpecific; static RecursiveMutex csPathCached; const fs::path &GetBlocksDir() { @@ -790,43 +828,7 @@ } const fs::path &GetDataDir(bool fNetSpecific) { - LOCK(csPathCached); - fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; - - // Cache the path to avoid calling fs::create_directories on every call of - // this function - if (!path.empty()) { - return path; - } - - std::string datadir = gArgs.GetArg("-datadir", ""); - if (!datadir.empty()) { - path = fs::system_complete(datadir); - if (!fs::is_directory(path)) { - path = ""; - return path; - } - } else { - path = GetDefaultDataDir(); - } - - if (fNetSpecific) { - path /= BaseParams().DataDir(); - } - - if (fs::create_directories(path)) { - // This is the first run, create wallets subdirectory too - // - // TODO: this is an ugly way to create the wallets/ directory and - // really shouldn't be done here. Once this is fixed, please - // also remove the corresponding line in bitcoind.cpp AppInit. - // See more info at: - // https://reviews.bitcoinabc.org/D3312 - fs::create_directories(path / "wallets"); - } - - path = StripRedundantLastElementsOfPath(path); - return path; + return gArgs.GetDataDirPath(fNetSpecific); } bool CheckDataDirOption() { @@ -835,10 +837,7 @@ } void ClearDatadirCache() { - LOCK(csPathCached); - - pathCached = fs::path(); - pathCachedNetSpecific = fs::path(); + gArgs.ClearDatadirPathCache(); g_blocks_path_cache_net_specific = fs::path(); }