Merge bitcoin/bitcoin#27073: Convert ArgsManager::GetDataDir to a read-only function

64c105442c util: make GetDataDir read-only & create datadir.. (willcl-ark)
56e370fbb9 util: add ArgsManager datadir helper functions (willcl-ark)

Pull request description:

  Fixes #20070

  Currently `ArgsManager::GetDataDir()` ensures it will always return a datadir by creating one if necessary. The function is shared between `bitcoind` `bitcoin-qt` and `bitcoin-cli` which results in the undesirable behaviour described in #20070.

  This PR splits out the part of the function which creates directories and adds it as a standalone function, only called as part of `bitcoind` and `bitcoin-qt` init, but not `bitcoin-cli`.

  `ReadConfigFiles`' behavior is changed to use the absolute path of the config file in error and warning messages instead of a relative path.

  This was inadvertantly the form being tested [here](73966f75f6/test/functional/feature_config_args.py (L287)), whilst we were _not_ testing that a relative path was returned by the message even though we passed a relative path in as argument.

ACKs for top commit:
  achow101:
    ACK 64c105442c
  hebasto:
    re-ACK 64c105442c, only comments have been adjusted as requsted since my previous [review](https://github.com/bitcoin/bitcoin/pull/27073#pullrequestreview-1307435890).
  TheCharlatan:
    Re-ACK 64c105442c
  ryanofsky:
    Code review ACK 64c105442c. Only comment changes since last review

Tree-SHA512: b129501346071ad62551c9714492b21536d0558a94117d97218e255ef4e948d00df899a4bc2788faea27d3b1f20fc6136ef9d03e6a08498d926d9ad8688d6c96
This commit is contained in:
Andrew Chow 2023-02-23 16:29:49 -05:00
commit 1258af40c0
No known key found for this signature in database
GPG key ID: 17565732E08E5E41
3 changed files with 42 additions and 12 deletions

View file

@ -167,6 +167,7 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans
static bool InitSettings() static bool InitSettings()
{ {
gArgs.EnsureDataDir();
if (!gArgs.GetSettingsPath()) { if (!gArgs.GetSettingsPath()) {
return true; // Do nothing if settings file disabled. return true; // Do nothing if settings file disabled.
} }

View file

@ -417,8 +417,7 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
LOCK(cs_args); LOCK(cs_args);
fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path; 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 // Used cached path if available
// this function
if (!path.empty()) return path; if (!path.empty()) return path;
const fs::path datadir{GetPathArg("-datadir")}; const fs::path datadir{GetPathArg("-datadir")};
@ -432,20 +431,34 @@ const fs::path& ArgsManager::GetDataDir(bool net_specific) const
path = GetDefaultDataDir(); path = GetDefaultDataDir();
} }
if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}
if (net_specific && !BaseParams().DataDir().empty()) { if (net_specific && !BaseParams().DataDir().empty()) {
path /= fs::PathFromString(BaseParams().DataDir()); path /= fs::PathFromString(BaseParams().DataDir());
if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}
} }
return path; return path;
} }
void ArgsManager::EnsureDataDir() const
{
/**
* "/wallets" subdirectories are created in all **new**
* datadirs, because wallet code will create new wallets in the "wallets"
* subdirectory only if exists already, otherwise it will create them in
* the top-level datadir where they could interfere with other files.
* Wallet init code currently avoids creating "wallets" directories itself
* for backwards compatibility, but this be changed in the future and
* wallet code here could go away (#16220).
*/
auto path{GetDataDir(false)};
if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}
path = GetDataDir(true);
if (!fs::exists(path)) {
fs::create_directories(path / "wallets");
}
}
void ArgsManager::ClearPathCache() void ArgsManager::ClearPathCache()
{ {
LOCK(cs_args); LOCK(cs_args);
@ -491,6 +504,7 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const
bool ArgsManager::InitSettings(std::string& error) bool ArgsManager::InitSettings(std::string& error)
{ {
EnsureDataDir();
if (!GetSettingsPath()) { if (!GetSettingsPath()) {
return true; // Do nothing if settings file disabled. return true; // Do nothing if settings file disabled.
} }
@ -965,6 +979,11 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return true; return true;
} }
fs::path ArgsManager::GetConfigFilePath() const
{
return GetConfigFile(GetPathArg("-conf", BITCOIN_CONF_FILENAME));
}
bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{ {
{ {
@ -973,8 +992,8 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
m_config_sections.clear(); m_config_sections.clear();
} }
const fs::path conf_path = GetPathArg("-conf", BITCOIN_CONF_FILENAME); const auto conf_path{GetConfigFilePath()};
std::ifstream stream{GetConfigFile(conf_path)}; std::ifstream stream{conf_path};
// not ok to have a config file specified that cannot be opened // not ok to have a config file specified that cannot be opened
if (IsArgSet("-conf") && !stream.good()) { if (IsArgSet("-conf") && !stream.good()) {

View file

@ -242,6 +242,11 @@ protected:
void SelectConfigNetwork(const std::string& network); void SelectConfigNetwork(const std::string& network);
[[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error); [[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error);
/**
* Return config file path (read-only)
*/
fs::path GetConfigFilePath() const;
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
/** /**
@ -475,13 +480,18 @@ protected:
*/ */
void LogArgs() const; void LogArgs() const;
/**
* If datadir does not exist, create it along with wallets/
* subdirectory(s).
*/
void EnsureDataDir() const;
private: private:
/** /**
* Get data directory path * Get data directory path
* *
* @param net_specific Append network identifier to the returned 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 * @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& GetDataDir(bool net_specific) const; const fs::path& GetDataDir(bool net_specific) const;